package raft

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	raft_hashicorp "github.com/hashicorp/raft"
	"libvirt.org/go/libvirt"

	etcd_client "deevirt.fr/compute/pkg/etcd"
	//"deevirt.fr/compute/pkg/scheduler"
)

type RaftNode struct {
	Bootstrap bool
	Raft      *raft_hashicorp.Raft
	Store     *Store
	NodeID    string
	StateCh   chan raft_hashicorp.Observation
}

func (n *RaftNode) init() {
	println("bootstrap :")
	nodes := make(NodeStore)

	// Récupération des Noeuds ID
	etcd, _ := etcd_client.New(n.Store.conf.EtcdURI)
	defer etcd.Close()

	for key, value := range etcd_client.GetNodes(etcd, n.Store.conf.ClusterID) {
		nodes[key] = &NodeStoreInfo{
			IpManagement: value.IpManagement,
			Scoring:      0,
		}
		println(key)
		println(value.IpManagement)

		var libvirt_uri string

		if n.Store.conf.LibvirtTLS {
			libvirt_uri = fmt.Sprintf("qemu+tls://%s/system", value.IpManagement)
		} else {
			libvirt_uri = fmt.Sprintf("qemu+tcp://%s/system", value.IpManagement)
		}

		c, err := libvirt.NewConnect(libvirt_uri)
		if err != nil {
			log.Fatalf("Erreur %v", err)
		}
		defer c.Close()

		getDomains, _ := c.ListAllDomains(libvirt.CONNECT_LIST_DOMAINS_PERSISTENT)
		for _, domain := range getDomains {
			conf, _ := domain.GetXMLDesc(libvirt.DOMAIN_XML_INACTIVE)
			uuid, _ := domain.GetUUIDString()
			state, _, _ := domain.GetState()

			d, _ := json.Marshal(struct {
				Config  string `json:"config"`
				State   int    `json:"state"`
				Migrate bool   `json:"Migrate"`
			}{
				conf, int(state), false,
			})

			n.Store.Set(fmt.Sprintf("/etc/libvirt/qemu/%s/%s", key, uuid), d)
		}
	}

	jNodes, _ := json.Marshal(nodes)
	n.Store.Set("/etc/libvirt/cluster", jNodes)
}

// Fonction pour surveiller et afficher les changements d'état
func (n *RaftNode) WatchStateChanges() {
	worker, _ := NewWorker(n.Store)

	for obs := range n.StateCh {
		switch evt := obs.Data.(type) {
		case raft_hashicorp.RaftState:

			if evt == raft_hashicorp.Leader {
				log.Println("[ÉVÉNEMENT] Changement d'état Raft :", evt)
				if n.Bootstrap {
					n.init()
				}

				// On attend une seconde avant de démarrer le worker
				time.Sleep(1 * time.Second)

				worker.Start()
			} else {
				worker.Stop()
			}

			log.Println("[ÉVÉNEMENT] Changement d'état Raft :", evt)
		case raft_hashicorp.LeaderObservation:
			log.Println("[ÉVÉNEMENT] Le leader est", evt.LeaderID)
		case raft_hashicorp.PeerObservation:
			if n.Raft.State() == raft_hashicorp.Leader {
				peerID := evt.Peer.ID
				peerAddr := evt.Peer.Address

				log.Println("[NOUVEAU NŒUD] Détection de", peerID, "à", peerAddr)
				log.Println("[ACTION] Ajout automatique en tant que voter...")

				future := n.Raft.AddVoter(peerID, peerAddr, 0, 0)
				if err := future.Error(); err != nil {
					log.Println("[ERREUR] Impossible d'ajouter", peerID, ":", err)
				} else {
					log.Println("[SUCCÈS] Voter ajouté :", peerID)
				}
			}
		case raft_hashicorp.FailedHeartbeatObservation:
			log.Println("[ÉVÉNEMENT] Perte de connexion avec un nœud :", evt.PeerID)
		default:
			log.Println("[ÉVÉNEMENT] Autre événement :", evt)
		}
	}
}
