package app

import (
	"fmt"
	"log"
	"os"
	"os/signal"
	"strconv"
	"syscall"
)

type (
	Doer interface {
		Start() error
		RegisterHandler(string, func() any)
		OnShutdown()
	}
	Application interface {
		Start(while chan struct{})
		RegisterPlugin(PluginFn) error
		Shutdown()
	}

	App struct {
		doer Doer
	}
)

func NewApp(d Doer) *App {
	return &App{
		doer: d,
	}
}

func (a *App) Start(while chan struct{}) error {
	go func() {
		sigint := make(chan os.Signal, 1)
		signal.Notify(sigint, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
		<-sigint
		fmt.Println("Received signal:", sigint)

		a.Shutdown()

		close(while)
	}()

	runFile := a.createRunFile("./app.run") // FIXME path...
	defer a.removeRunFile(runFile)

	err := a.doer.Start()
	if err != nil {
		log.Fatalf("Failed to start app. Reason: %v\n", err)
		close(while)
	}
	<-while

	return err
}

func (a *App) RegisterPlugin(p Plugin) error {
	a.doer.RegisterHandler(p.name, p.fn)

	return nil
}

func (a *App) Shutdown() {
	a.doer.OnShutdown()
}

func (a *App) createRunFile(path string) *os.File {
	run, err := os.Create(path)
	if err != nil {
		log.Fatalf("Failed to create run file. Reason: %v\n", err)
		os.Exit(1)
	}
	run.WriteString(strconv.Itoa(os.Getpid()))

	return run
}

func (a *App) removeRunFile(f *os.File) error {
	return f.Close()
}
