package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	lfcc "git.loafle.net/commons_go/config"
	"git.loafle.net/commons_go/logging"
	"git.loafle.net/overflow/overflow_probes/auth"
	"git.loafle.net/overflow/overflow_probes/commons"
	"git.loafle.net/overflow/overflow_probes/config"
	"git.loafle.net/overflow/overflow_probes/probe"
	"github.com/takama/daemon"
)

const (
	version = "1.0.0"
	website = "https://www.overflow.cloud"
	banner  = `

 ██████╗ ██╗   ██╗███████╗██████╗ ███████╗██╗      ██████╗ ██╗    ██╗
██╔═══██╗██║   ██║██╔════╝██╔══██╗██╔════╝██║     ██╔═══██╗██║    ██║
██║   ██║██║   ██║█████╗  ██████╔╝█████╗  ██║     ██║   ██║██║ █╗ ██║
██║   ██║╚██╗ ██╔╝██╔══╝  ██╔══██╗██╔══╝  ██║     ██║   ██║██║███╗██║
╚██████╔╝ ╚████╔╝ ███████╗██║  ██║██║     ███████╗╚██████╔╝╚███╔███╔╝
 ╚═════╝   ╚═══╝  ╚══════╝╚═╝  ╚═╝╚═╝     ╚══════╝ ╚═════╝  ╚══╝╚══╝ 
                                                                     
`
)

const (
	// name of the service
	serviceName        = "Probe"
	serviceDescription = "Probe Service of overFlow"
)

type daemonHandler struct {
	daemon.Daemon
}

// Manage by daemon commands or run the daemon
// cmd install -config=./path
// cmd install
// cmd remove
// cmd start
// cmd stop
// cmd status
// cmd -config=./path
// cmd
func (d *daemonHandler) Manage() (isRunning bool, status string, err error) {

	isRunning = true

	if nil != daemonCommand {
		switch *daemonCommand {
		case "install":
			var runArgs = []string{}
			runArgs = append(runArgs, fmt.Sprintf("-configDir=%s", *configDir))

			isRunning = false
			status, err = d.Install(runArgs...)
		case "remove":
			isRunning = false
			status, err = d.Remove()
		case "start":
			isRunning = false
			status, err = d.Start()
		case "stop":
			isRunning = false
			status, err = d.Stop()
		case "status":
			isRunning = false
			status, err = d.Status()
		}
	}

	return
}

var (
	daemonCommand *string
	configDir     *string
)

func init() {
	flag.Usage = func() {
		fmt.Printf("Usage of %s\n", os.Args[0])
		fmt.Printf("  [install | remove | start | stop | status]\n")
		flag.PrintDefaults()
	}

	if len(os.Args) > 1 {
		command := os.Args[1]
		switch command {
		case "install", "remove", "start", "stop", "status":
			*daemonCommand = command
		}
	}

	configDir = flag.String("config-dir", ".", "The directory of config")
	flag.Parse()

}

func main() {
	var err error
	var srv daemon.Daemon
	var status string
	var handler commons.Handler
	isRunning := true
	var confDir string

	defer logging.Logger.Sync()

	fmt.Println(banner)
	fmt.Printf("Version: %s\n", version)
	fmt.Printf("URL: %s\n", website)
	fmt.Println()

	if dir, err := lfcc.ABSPathify(*configDir); nil != err {
		logging.Logger.Panic(fmt.Sprintf("Probe: config path[%s] is not valid", *configDir))
	} else {
		logging.Logger.Debug(fmt.Sprintf("Probe: config path[%s]", dir))
		confDir = dir
	}

	conf := lfcc.New()
	conf.SetConfigPath(*configDir)
	if err := conf.Load(&config.Config, "config.json"); nil != err {
		logging.Logger.Panic(fmt.Sprintf("Probe: config is not valid error[%v]", err))
	}

	if srv, err = daemon.New(serviceName, serviceDescription); nil != err {
		logging.Logger.Panic(fmt.Sprintf("Probe: %v", err))
	}

	s := &daemonHandler{srv}
	if isRunning, status, err = s.Manage(); nil != err {
		logging.Logger.Error(fmt.Sprintf("Probe: status[%s] error: %v", status, err))
		os.Exit(1)
	}

	if !isRunning {
		logging.Logger.Info(fmt.Sprintf("Probe: status[%s]", status))
		os.Exit(0)
	}

	if handler, err = auth.New(confDir); nil != err {
		logging.Logger.Error(fmt.Sprintf("Auth: error: %v", err))
		os.Exit(1)
	}

	go func() {
		if err := handler.Serve(); err != nil {
			logging.Logger.Error(fmt.Sprintf("Probe: Authenticator error: %v", err))
			return
		}

		if handler, err = probe.New(confDir); nil != err {
			logging.Logger.Error(fmt.Sprintf("Probe: error: %v", err))
			return
		}
		if err := handler.Serve(); err != nil {
			logging.Logger.Error(fmt.Sprintf("Probe: error: %v", err))
			return
		}
	}()

	// // Set up channel on which to send signal notifications.
	// // We must use a buffered channel or risk missing the signal
	// // if we're not ready to receive when the signal is sent.
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt,
		syscall.SIGKILL,
		syscall.SIGSTOP,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)

	<-interrupt
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := handler.Shutdown(ctx); err != nil {
		logging.Logger.Error(fmt.Sprintf("Probe: status[%s] error: %v", status, err))
	}

	// 	// loop work cycle with accept connections or interrupt
	// 	// by system signal
	// ListenLoop:
	// 	for {
	// 		select {
	// 		case s := <-interrupt:
	// 			logging.Logger.Info(fmt.Sprintf("Probe: signal[%v]", s))
	// 			ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	// 			defer cancel()
	// 			if err := p.Shutdown(ctx); err != nil {
	// 				logging.Logger.Error(fmt.Sprintf("Probe: status[%s] error: %v", status, err))
	// 			}

	// 			if s == os.Interrupt {
	// 				logging.Logger.Info("Probe was interruped by system signal")
	// 			} else {
	// 				logging.Logger.Info("Probe was killed")
	// 			}
	// 			break ListenLoop
	// 		}
	// 	}

}
