package esr

import (
	"esr/internal/config"
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"github.com/evanw/esbuild/pkg/api"
)

type Esr struct {
	Config  *config.Config
	Builder *Builder
	Server  *Server
	Runner  *Runner
}

func NewEsr(c *config.Config, entryPoint string) *Esr {
	builder := NewBuilder(c, entryPoint)
	server := NewDevServer(c, entryPoint, builder)

	return &Esr{
		Config:  c,
		Builder: builder,
		Server:  server,
		Runner:  NewRunner(c, builder, entryPoint),
	}
}

func ExecuteTask(esr *Esr, build, serve, run, watch bool) {
	entryPoint := esr.Builder.EntryPoint

	if build {
		opts := ModeOptions(esr.Config, ModeBuilder, entryPoint, "")
		if err := esr.Builder.Build(opts); err != nil {
			fmt.Println("Build error:", err)
			return
		}
	}

	if serve {
		opts := ModeOptions(esr.Config, ModeServer, entryPoint, "")
		if err := esr.Builder.Build(opts); err != nil {
			fmt.Println("Build before serve error:", err)
			return
		}
		startServerWithWatching(esr, opts)
	}

	if run {
		if watch {
			// Special case for --run --watch: we start watching and re-run on changes
			opts := ModeOptions(esr.Config, ModeRunner, entryPoint, esr.Runner.TempFilePath)
			if err := esr.Builder.Build(opts); err != nil {
				fmt.Println("Build error:", err)
				return
			}
			
			 // Enter alt screen for watch mode
			EnterAltScreen()
			ClearScreen()
			
			// Run once initially
			esr.Runner.Run(entryPoint)
			
			// Then set up watching
			err := esr.Builder.StartWatch(opts, func() {
				ClearScreen()
				esr.Runner.Run(entryPoint)
			})
			
			if err != nil {
				fmt.Println("Watch error:", err)
				ExitAltScreen()
				return
			}
			
			// Block main thread with a signal handler to keep the process alive
			sigCh := make(chan os.Signal, 1)
			signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
			<-sigCh
			
			// Ensure we exit the alternate buffer when shutting down
			ExitAltScreen()
		} else {
			// Normal run case (no watch) - don't use alt screen
			esr.Runner.Run(entryPoint)
		}
	} else if watch && !serve {
		// Watch-only mode (not combined with serve or run)
		opts := ModeOptions(esr.Config, ModeServer, entryPoint, "")
		err := esr.Builder.StartWatch(opts, esr.generalCallback(false, entryPoint))
		if err != nil {
			fmt.Println("Watch error:", err)
			return
		}
		
		// Block main thread with a signal handler to keep the process alive
		sigCh := make(chan os.Signal, 1)
		signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
		<-sigCh
	}
}

func startServerWithWatching(esr *Esr, opts *api.BuildOptions) {
	err := esr.Builder.StartWatch(opts, esr.serveCallback())
	if err != nil {
		fmt.Println("Server watch error:", err)
	}

	if err := esr.Server.Start(); err != nil {
		fmt.Println("Server error:", err)
	}
}

func (esr *Esr) generalCallback(run bool, entryPoint string) func() {
	return func() {
		if run {
			esr.Runner.Run(entryPoint)
		}
	}
}

func (esr *Esr) serveCallback() func() {
	return func() {
		esr.Server.LiveReload.Reload()
	}
}
