package main

/*

	Two-Nodes

	A simple demo for showcasing the go-DDDNS working in a two nodes environment

*/
import (
	"errors"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	godddns "github.com/tobychui/go-DDDNS/godddns"
)

var (
	//Flags
	//Current node
	thisNodeUUID = flag.String("nid", "node1", "The UUID of this node")

	//Remote nodes
	remoteNodeAUUID   = flag.String("rida", "node2", "The UUID of the remote node A")
	remoteNodeAIPAddr = flag.String("ripa", "127.0.0.1", "The remote node A current IP address")
	remoteNodeBUUID   = flag.String("ridb", "node3", "The UUID of the remote node B")
	remoteNodeBIPAddr = flag.String("ripb", "127.0.0.1", "The remote node B current IP address")

	//The following flags assume both remote node A and B are hosted on the same port and with same connection endpoint
	connPath           = flag.String("conn", "/conn", "The relative path for connection establishment")
	thisNodeRecvPort   = flag.Int("np", 8080, "This node receive port")
	remoteNodeRecvPort = flag.Int("rp", 8080, "Remote nodes (A and B) receive port")

	//Paramter, which we hardcoded here for now
	syncInterval  int64 = 10
	serviceRouter *godddns.ServiceRouter
)

//Dummy function which allow all login to pass through
func ValidateCred(username string, password string) bool {
	return true
}

func cleanup(configFilename string) {
	//Save the router record to json file
	js, err := serviceRouter.ExportRouterToJSON()
	if err != nil {
		panic(err)
	}
	ioutil.WriteFile(configFilename, []byte(js), 0755)

	//Close the service router
	serviceRouter.Close()
}

func main() {
	//Set-ups
	flag.Parse()
	configFilename := *thisNodeUUID + "_config.json"
	c := make(chan os.Signal)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		cleanup(configFilename)
		os.Exit(1)
	}()

	//Step 1: we want to create a new service router based on the given information
	if fileExists(configFilename) {
		//Load service router from file
		s, err := godddns.NewRouterFromJSONFile(configFilename)
		if err != nil {
			log.Println("Failed to load service router from file: ")
			panic(err)
		}

		s.InjectAuthFunction(ValidateCred)
		serviceRouter = s
		log.Println("Service Router Config Loaded")
	} else {
		//Create a new service router
		serviceRouter = godddns.NewServiceRouter(godddns.RouterOptions{
			DeviceUUID:   *thisNodeUUID,
			AuthFunction: ValidateCred,
			SyncInterval: syncInterval,
		})
		log.Println("Service Router Created")
	}

	//Optional: Enable verbal output on router
	serviceRouter.Options.Verbal = true

	//Step 2: Create HTTP listening endpoint for incoming connection requests and heartbeat
	go func() {
		http.HandleFunc("/conn", serviceRouter.HandleConnections)
		log.Println("HTTP Listening endpoint started on :" + strconv.Itoa(*thisNodeRecvPort))
		http.ListenAndServe(":"+strconv.Itoa(*thisNodeRecvPort), nil)
	}()

	if !fileExists(configFilename) {
		//Step 3: we want to add the remote nodes to the list
		//Node A
		nodeA := serviceRouter.NewNode(godddns.NodeOptions{
			NodeID:        *remoteNodeAUUID,
			Port:          *remoteNodeRecvPort,
			RESTInterface: *connPath,
			RequireHTTPS:  false,
		})
		serviceRouter.AddNode(nodeA)
		log.Println("Remote NodeA Added")

		//Node B
		nodeB := serviceRouter.NewNode(godddns.NodeOptions{
			NodeID:        *remoteNodeBUUID,
			Port:          *remoteNodeRecvPort,
			RESTInterface: *connPath,
			RequireHTTPS:  false,
		})
		serviceRouter.AddNode(nodeB)
		log.Println("Remote NodeB Added")

		//Step 4: Start the connection to Node 1 (Replace username and password with real value here)
		retryCount := 0
		retry := true
		for retry {
			if retryCount > 10 {
				log.Println("Connection to remote node " + nodeA.UUID + " failed. Skipping.")
				break
			}
			assignedSendingTOTP, err := nodeA.StartConnection(*remoteNodeAIPAddr, "username", "password")
			if err != nil {
				log.Println("Unable to establish connection to Node A. Retrying in 3 seconds", assignedSendingTOTP)
				retryCount++

				time.Sleep(3 * time.Second)
			} else {
				log.Println("Remote Node A TOTP exchange done:", assignedSendingTOTP)
				retry = false
			}

		}

		//Start connection to Node 2
		retryCount = 0
		retry = true
		for retry {
			if retryCount > 10 {
				log.Println("Connection to remote node " + nodeB.UUID + " failed. Skipping.")
				break
			}
			assignedSendingTOTP, err := nodeB.StartConnection(*remoteNodeBIPAddr, "username", "password")
			if err != nil {
				log.Println("Unable to establish connection to Node B. Retrying in 3 seconds", assignedSendingTOTP)
				retryCount++

				time.Sleep(3 * time.Second)
			} else {
				log.Println("Node B TOTP exchange done:", assignedSendingTOTP)
				retry = false
			}

		}
	}

	//Step 4: Start Heartbeat to the remote node(s)
	serviceRouter.StartHeartBeat()
	log.Println("Heartbeat schedule started")

	//Do a blocking loop
	select {}
}

func fileExists(filename string) bool {
	if _, err := os.Stat(filename); errors.Is(err, os.ErrNotExist) {
		return false
	}
	return true
}
