package main

import (
	"context"
	"fmt"
	"log/slog"
	_ "net/http/pprof"
	"os"
	"runtime"
	"time"
	"vctp/db"
	"vctp/internal/tasks"
	utils "vctp/internal/utils"
	"vctp/log"
	"vctp/server"
	"vctp/server/router"

	"github.com/go-co-op/gocron/v2"
	"github.com/joho/godotenv"
)

var (
	bindDisableTls bool
	sha1ver        string // sha1 revision used to build the program
	buildTime      string // when the executable was built
	cronFrequency  time.Duration
)

func main() {
	// Load data from environment file
	envFilename := utils.GetFilePath(".env")
	err := godotenv.Load(envFilename)
	if err != nil {
		panic("Error loading .env file")
	}

	logger := log.New(
		log.GetLevel(),
		log.GetOutput(),
	)

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

	// Configure database
	database, err := db.New(logger, utils.GetFilePath("db.sqlite3"))
	if err != nil {
		logger.Error("Failed to create database", "error", err)
		os.Exit(1)
	}
	defer database.Close()
	//defer database.DB().Close()

	if err = db.Migrate(database); err != nil {
		logger.Error("failed to migrate database", "error", err)
		os.Exit(1)
	}

	// Prepare the task scheduler
	s, err := gocron.NewScheduler()
	if err != nil {
		logger.Error("failed to create scheduler", "error", err)
		os.Exit(1)
	}

	// Pass useful information to the cron jobs
	c := &tasks.CronTask{
		Logger:   logger,
		Database: database,
	}

	// Determine bind IP
	bindIP := os.Getenv("BIND_IP")
	if bindIP == "" {
		bindIP = utils.GetOutboundIP().String()
	}
	// Determine bind port
	bindPort := os.Getenv("BIND_PORT")
	if bindPort == "" {
		bindPort = "9443"
	}
	bindAddress := fmt.Sprint(bindIP, ":", bindPort)
	//logger.Info("Will listen on address", "ip", bindIP, "port", bindPort)

	// Determine bind disable TLS
	bindDisableTlsEnv := os.Getenv("BIND_DISABLE_TLS")
	if bindDisableTlsEnv == "true" {
		bindDisableTls = true
	}

	// Get file names for TLS cert/key
	tlsCertFilename := os.Getenv("TLS_CERT_FILE")
	if tlsCertFilename != "" {
		tlsCertFilename = utils.GetFilePath(tlsCertFilename)
	} else {
		tlsCertFilename = "./cert.pem"
	}

	tlsKeyFilename := os.Getenv("TLS_KEY_FILE")
	if tlsKeyFilename != "" {
		tlsKeyFilename = utils.GetFilePath(tlsKeyFilename)
	} else {
		tlsKeyFilename = "./privkey.pem"
	}

	// Generate certificate if required
	if !(utils.FileExists(tlsCertFilename) && utils.FileExists(tlsKeyFilename)) {
		logger.Warn("Specified TLS certificate or private key do not exist", "certificate", tlsCertFilename, "tls-key", tlsKeyFilename)
		utils.GenerateCerts(tlsCertFilename, tlsKeyFilename)
	}

	cronFrequencyString := os.Getenv("VCENTER_POLLING_SECONDS")
	if cronFrequencyString != "" {
		cronFrequency, err = time.ParseDuration(cronFrequencyString)
		if err != nil {
			slog.Error("Can't convert VCENTER_POLLING_SECONDS value to time duration. Defaulting to 60s", "value", cronFrequencyString, "error", err)
			cronFrequency = time.Second * 60
		}
	} else {
		cronFrequency = time.Second * 60
	}
	logger.Debug("Setting VM polling cronjob frequency to", "frequency", cronFrequency)

	// start background processing for events stored in events table
	startsAt := time.Now().Add(time.Second * 10)
	job, err := s.NewJob(
		gocron.DurationJob(cronFrequency),
		gocron.NewTask(func() {
			c.RunVmCheck(ctx, logger)
		}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
		gocron.WithStartAt(gocron.WithStartDateTime(startsAt)),
	)
	if err != nil {
		logger.Error("failed to start cron jobs", "error", err)
		os.Exit(1)
	}
	logger.Debug("Created event processing cron job", "job", job.ID())

	startsAt2 := time.Now().Add(time.Second * 10)
	job2, err := s.NewJob(
		gocron.DurationJob(cronFrequency),
		gocron.NewTask(func() {
			c.RunVcenterPoll(ctx, logger)
		}), gocron.WithSingletonMode(gocron.LimitModeReschedule),
		gocron.WithStartAt(gocron.WithStartDateTime(startsAt2)),
	)
	if err != nil {
		logger.Error("failed to start cron jobs", "error", err)
		os.Exit(1)
	}
	logger.Debug("Created vcenter polling cron job", "job", job2.ID())

	// start cron scheduler
	s.Start()

	// Start server
	svr := server.New(
		logger,
		s,
		cancel,
		bindAddress,
		server.WithRouter(router.New(logger, database, buildTime, sha1ver, runtime.Version())),
	)

	svr.DisableTls(bindDisableTls)
	svr.SetCertificate(tlsCertFilename)
	svr.SetPrivateKey(tlsKeyFilename)

	svr.StartAndWait()

	os.Exit(0)
}
