package main

import (
	"context"
	"encoding/json"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"keksvpn/pkg/ipfunc"
	"keksvpn/pkg/wgfunc"
	"net/http"
	"os"
	"os/signal"
	"time"
)

var wgServer wgfunc.WireGuard
var ipSubnet ipfunc.IPSubnet
var publickey string
var privatekey string
var mux *http.ServeMux
var serverip string

func main()  {
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	ctx, cancel := context.WithCancel(context.Background())

	go func() {
		oscall := <-c
		log.Printf("system call:%+v", oscall)
		cancel()
	}()


	setupWireguard()
	defer func() {
		down()
	}()
	setupHTTPServer()
	runHTTPServer(ctx)
}

type PeerRequest struct {
	PublicKey string `json:"publicKey"`
}

type PeerResponse struct {
	IP string `json:"ip"`
	PublicKey string `json:"publicKey"`
	AllowedIPs []string `json:"allowedIPs"`
}

func setupHTTPServer() {
	mux = http.NewServeMux()
	mux.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
		writer.Write([]byte("ok"))
	})
	
	mux.HandleFunc("/peer", func(writer http.ResponseWriter, request *http.Request) {
		var r = PeerRequest{}
		err := json.NewDecoder(request.Body).Decode(&r)
		if err != nil {
			http.Error(writer, err.Error(), http.StatusBadRequest)
			return
		}

		peerIP, err := ipSubnet.GetNextFreeIP()

		if err != nil {
			http.Error(writer, err.Error(), http.StatusBadRequest)
			return
		}

		peerConfig := wgfunc.PeerConfig{
			PublicKey:    r.PublicKey,
			PreSharedKey: "",
			AllowedIPs:   []string{peerIP + "/32"},
			Endpoint:     "",
		}

		wgServer.AddPeer(peerConfig)

		res := PeerResponse{
			IP: peerIP + "/32",
			PublicKey: publickey,
			AllowedIPs: []string{serverip+"/24"},
		}

		byteRes, err := json.Marshal(res)
		if err != nil {
			http.Error(writer, err.Error(), http.StatusBadRequest)
			return
		}

		writer.Write(byteRes)
	})
}

func runHTTPServer(ctx context.Context) {
	var err error
	log.Info().Msg("Start HTTP Server")
	srv := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}

	go func() {
		if err = srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatal().Err(err).Msg("Error running HTTP Server")
		}
	}()

	log.Debug().Msg("HTTP Server started")

	<-ctx.Done()

	log.Debug().Msg("HTTP Server stopping")

	ctxShutDown, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer func() {
		cancel()
	}()

	if err = srv.Shutdown(ctxShutDown); err != nil {
		log.Fatal().Err(err).Msg("Error stop HTTP Server")
	}
}

func setKeys()  {
	var err error
	publickey, privatekey, err = wgfunc.GenerateKeyPair()
	if err != nil {
		panic(err)
	}
}

func setupWireguard()  {
	var err error

	setKeys()

	ipSubnet, err = ipfunc.NewIPSubnet("10.0.4.0/24")
	if err != nil {
		panic(err)
	}

	serverip, err = ipSubnet.GetNextFreeIP()
	if err != nil {
		panic(err)
	}

	serverConfig := wgfunc.WireGuardConfig{
		Name:       "wgSampleServer",
		IPRanges:   []string{serverip + "/24"},
		Port:       51820,
		PrivateKey: privatekey,
	}



	wgServer, err = wgfunc.NewWireGuard(serverConfig)
	if err != nil {
		panic(err)
	}

	err = wgServer.Up()
	if err != nil {
		panic(err)
	}
}

func down() {
	log.Debug().Msg("Stop Wireguard Server")
	wgServer.Down()
}