package main

import (
	"embed"
	"fmt"
	"ld/eventbus"
	"ld/interval"
	"ld/server"
	"ld/sqlite"
	"log"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
)

const (
	exitCodeErr       = 1
	exitCodeInterrupt = 2
)

var AppRoot = "./" // path for supporting files that sit in app root folder in production

//go:embed frontend/*
var frontend embed.FS

//go:embed embed
var embedded embed.FS

// main specific variables
var ExecutableName string

func main() {
	fmt.Println("Here")
	err := run()
	if err != nil {
		os.Exit(exitCodeErr)
	}
	ExecutableName, err = getExecutableName()
	if err != nil {
		os.Exit(exitCodeErr)
	}

	// readCrew()

	// readMessages()
	ebus := eventbus.NewEventBus()

	interval.SetInterval(func() {
		fmt.Println("------------------")
	}, 10)

	runEventbus(ebus)

	run()

}

func runEventbus(ebus *eventbus.EventBus) {

	// Create a new instance
	eventChannel := eventbus.NewEventChannel()

	// Subscribe to "foo:baz" - or use a wildcard like "foo:*"
	ebus.SubscribeChannel("foo:baz", eventChannel)
	ebus.SubscribeChannel("pups-klo", eventChannel)
	ebus.SubscribeChannel("ömme*", eventChannel)

	eventChannelTopic := ebus.Subscribe("ömmels")

	// Subscribe with existing channel use
	// eventbus.SubscribeChannel("foo:*", eventChannel)

	// Wait for the incoming event on the channel
	go func() {

		for evt := range eventChannel {
			fmt.Println("FIRST", evt.Topic, evt.Data)
			evt.Done()
		}

	}()
	go func() {

		for evt := range eventChannel {
			fmt.Println("SECOND", evt.Topic, evt.Data)
			evt.Done()
		}
	}()

	go func() {

		for evt := range eventChannelTopic {
			fmt.Println("ÖMMELS", evt.Topic, evt.Data)
			evt.Done()
		}
	}()

	for i := 0; i < 1000; i++ {

		ebus.Publish("foo:baz", eventbus.Rec{"value": i})
		ebus.Publish("pups-klo", eventbus.Rec{"value": i})
		ebus.Publish("pups-klo", eventbus.Rec{"value": i})
		ebus.Publish("ömmels", eventbus.Rec{"value": i})
		ebus.Publish("ömmels", eventbus.Rec{"value": i * 10})
		ebus.Publish("ömmels", eventbus.Rec{"value": i * 100})
	}

}

func run() error {

	// setting up logging to file
	logFileName := AppRoot + ExecutableName + ".log"
	logFile, err := os.OpenFile(logFileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.Printf("error opening file: %v", err)
		os.Exit(exitCodeErr)
	}
	defer logFile.Close()
	log.SetOutput(logFile)

	stateDB, err := createStateDB(true)
	if err != nil {
		log.Fatalf("Failed to create internal StateDB: %v", err)
	}

	// setting up the server
	server, err := server.New(
		logFileName,
		stateDB,
		embedded,
	)

	if err != nil {
		log.Fatalf("server-app not created - error: %v", err)
	}

	// tasks.SetupTasks(server)

	err = server.Start()
	if err != nil {
		fmt.Printf("server not started - error: %v \n", err)
		log.Fatalf("server not started - error: %v", err)
	}

	readCrew(server)
	readMessages(server)
	return nil

	// listen for os shutdown events, report them into log file and exit application
	chanOS := make(chan os.Signal, 2)
	signal.Notify(chanOS, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-chanOS
		log.Println("shutting down request signal received")
		server.Close()
		os.Exit(exitCodeInterrupt)
	}()

	// setting up the routes, hooking up API endpoints with backend functions
	// routes.SetupRoutes(server)

	return nil

}

// some internal functions

func getExecutableName() (string, error) {
	name, err := os.Executable()
	if err != nil {
		return "", err
	}
	name = filepath.Base(name)
	return strings.TrimSuffix(name, filepath.Ext(name)), nil
}

func createStateDB(StateDBDelete bool) (*sqlite.Database, error) {

	// fileName := fmt.Sprintf("state-%s.db", ulid.Make())

	fileName := "state.db"

	if StateDBDelete {
		_, err := os.Stat(fileName)
		if err == nil {
			err := os.Remove(fileName)
			if err != nil {
				log.Fatal("error deleting statedb-file:", err)
			}
		}
	}

	db, err := sqlite.New(fileName)
	if err != nil {
		return nil, err
	}

	err = db.Open()
	if err != nil {
		return nil, err
	}

	query, err := embedded.ReadFile("embed/create_state_db.sql")
	if err != nil {
		return nil, err
	}

	_, err = db.DB().Exec(string(query))
	if err != nil {
		return nil, err
	}

	return db, nil
}
