//go:build !windows

// Package app 提供 Lolly 应用程序的生命周期管理和命令行入口。
//
// 包含应用生命周期管理和命令行入口相关的逻辑。
//
// 作者：xfy
package app

import (
	"fmt"
	"net"
	"os"
	"os/signal"
	"syscall"
	"time"

	"rua.plus/lolly/internal/config"
	"rua.plus/lolly/internal/logging"
	"rua.plus/lolly/internal/server"
)

// Run starts the application: loads config, creates servers, and handles signals.
func (a *App) Run() int {
	if err := a.loadAndValidateConfig(); err != nil {
		return 1
	}

	a.initVariables()

	// Inherit parent listeners when running as a graceful upgrade child.
	a.inheritListeners()

	a.logServerAddresses()
	a.initResolver()
	a.initServer()
	a.initStreamServers()
	a.initHTTP3()
	a.initHTTP2()

	a.upgradeMgr = server.NewUpgradeManager(a.srv)
	a.srv.SetUpgradeManager(a.upgradeMgr)
	if a.pidFile != "" {
		a.upgradeMgr.SetPidFile(a.pidFile)
		_ = a.upgradeMgr.WritePid()
	}

	a.signalReady()

	sigChan := make(chan os.Signal, 1)
	a.setupSignalHandlers(sigChan)

	errChan := make(chan error, 1)
	go func() {
		a.logger.LogStartup("Starting HTTP server", nil)
		if err := a.srv.Start(); err != nil {
			errChan <- err
		}
	}()

	sigintCount := 0

	for {
		select {
		case err := <-errChan:
			a.logger.Error().Err(err).Msg("Server failed to start")
			return 1
		case sig := <-sigChan:
			if sig == syscall.SIGINT {
				sigintCount++
				if sigintCount >= 3 {
					a.logger.LogShutdown("Received 3 SIGINT, forcing exit")
					return 1
				}
			}
			if !a.handleSignal(sig) {
				a.logger.LogShutdown("Server stopped")
				return 0
			}
		}
	}
}

// inheritListeners inherits parent listeners during graceful upgrade.
func (a *App) inheritListeners() {
	if os.Getenv("GRACEFUL_UPGRADE") == "1" {
		a.logger.LogStartup("Graceful upgrade mode detected, inheriting parent listeners", nil)
		a.upgradeMgr = server.NewUpgradeManager(nil)
		listeners, err := a.upgradeMgr.GetInheritedListeners()
		if err == nil && len(listeners) > 0 {
			a.listeners = listeners
		}
	}
}

func (a *App) setupSignalHandlers(sigChan chan<- os.Signal) {
	signal.Notify(sigChan,
		syscall.SIGTERM,
		syscall.SIGINT,
		syscall.SIGQUIT,
		syscall.SIGHUP,
		syscall.SIGUSR1,
		syscall.SIGUSR2,
	)
}

// handleSignal returns false to indicate the app should exit.
func (a *App) handleSignal(sig os.Signal) bool {
	if a.cfg == nil {
		a.logger.Error().Msg("Signal handling failed: config is nil, using default timeout")
		a.cfg = &config.Config{
			Shutdown: config.ShutdownConfig{
				GracefulTimeout: 30 * time.Second,
				FastTimeout:     5 * time.Second,
			},
		}
	}

	switch sig {
	case syscall.SIGQUIT:
		timeout := a.cfg.Shutdown.GracefulTimeout
		if timeout <= 0 {
			timeout = 30 * time.Second
		}
		a.logger.LogSignal("SIGQUIT", fmt.Sprintf("Graceful stop (waiting %v)", timeout))
		a.shutdownStream()
		a.shutdownHTTP2()
		a.shutdownHTTP3()
		_ = a.srv.GracefulStop(timeout)
		return false

	case syscall.SIGTERM, syscall.SIGINT:
		timeout := a.cfg.Shutdown.FastTimeout
		if timeout <= 0 {
			timeout = 5 * time.Second
		}
		sigTyped, ok := sig.(syscall.Signal)
		if !ok {
			a.logger.LogSignal("unknown", "Stopping server")
		} else {
			a.logger.LogSignal(sigName(sigTyped), "Stopping server")
		}
		a.shutdownStream()
		a.shutdownHTTP2()
		a.shutdownHTTP3()
		_ = a.srv.StopWithTimeout(timeout)
		return false

	case syscall.SIGHUP:
		a.logger.LogSignal("SIGHUP", "Reloading config")
		a.reloadConfig()
		return true

	case syscall.SIGUSR1:
		a.logger.LogSignal("SIGUSR1", "Reopening logs")
		a.reopenLogs()
		return true

	case syscall.SIGUSR2:
		a.logger.LogSignal("SIGUSR2", "Performing graceful upgrade")
		a.gracefulUpgrade()
		return true

	default:
		a.logger.Info().Str("signal", sig.String()).Msg("Received unknown signal")
		return true
	}
}

func (a *App) reloadConfig() {
	newCfg, err := config.Load(a.cfgPath)
	if err != nil {
		a.logger.Error().Err(err).Msg("Failed to reload config")
		return
	}

	if a.srv == nil {
		a.cfg = newCfg
		a.logger = logging.NewAppLogger(&newCfg.Logging)
		a.logger.LogStartup("Config reloaded (no running server)", nil)
		return
	}

	if a.requiresFullRestart(newCfg) {
		logging.Warn().Msg("Config requires full restart (listen address or mode changed). Use SIGUSR2 for graceful upgrade.")
		return
	}

	listeners := a.srv.GetListeners()
	if len(listeners) == 0 {
		a.logger.Error().Msg("Cannot reload: server has no saved listeners")
		return
	}

	duped := make([]net.Listener, len(listeners))
	for i, ln := range listeners {
		duped[i], err = server.DupListener(ln)
		if err != nil {
			for j := range i {
				_ = duped[j].Close()
			}
			a.logger.Error().Err(err).Msg("Failed to dup listener for reload")
			return
		}
	}

	newSrv := server.New(newCfg)
	if a.resv != nil {
		newSrv.SetResolver(a.resv)
	}
	newSrv.SetListeners(duped)

	startErr := make(chan error, 1)
	go func() {
		if err := newSrv.Start(); err != nil {
			startErr <- err
		}
	}()

	reloadTimeout := a.cfg.Shutdown.ReloadTimeout
	if reloadTimeout <= 0 {
		reloadTimeout = 5 * time.Second
	}

	select {
	case err := <-startErr:
		a.logger.Error().Err(err).Msg("Failed to start new server with reloaded config")
		for _, ln := range duped {
			_ = ln.Close()
		}
		return
	case <-time.After(reloadTimeout):
	}

	oldSrv := a.srv
	oldHTTP2 := a.http2Srv
	oldHTTP3 := a.http3Srv

	a.srv = newSrv
	a.cfg = newCfg
	a.logger = logging.NewAppLogger(&newCfg.Logging)
	a.http2Srv = nil
	a.http3Srv = nil

	a.initVariables()
	a.initHTTP2()
	a.initHTTP3()

	if a.upgradeMgr != nil {
		a.upgradeMgr.SetListeners(newSrv.GetListeners())
	}

	go func() {
		if oldHTTP2 != nil {
			_ = oldHTTP2.Stop()
		}
		if oldHTTP3 != nil {
			_ = oldHTTP3.Stop()
		}
		_ = oldSrv.GracefulStop(30 * time.Second)
	}()

	a.logger.LogStartup("Config reloaded successfully", nil)
}

func (a *App) requiresFullRestart(newCfg *config.Config) bool {
	if a.cfg.GetMode() != newCfg.GetMode() {
		return true
	}
	oldMode := a.cfg.GetMode()
	switch oldMode {
	case config.ServerModeSingle:
		if len(a.cfg.Servers) > 0 && len(newCfg.Servers) > 0 {
			if a.cfg.Servers[0].Listen != newCfg.Servers[0].Listen {
				return true
			}
		}
	case config.ServerModeVHost:
		if len(a.cfg.Servers) != len(newCfg.Servers) {
			return true
		}
		if len(a.cfg.Servers) > 0 && len(newCfg.Servers) > 0 {
			if a.cfg.Servers[0].Listen != newCfg.Servers[0].Listen {
				return true
			}
		}
	case config.ServerModeMultiServer:
		if len(a.cfg.Servers) != len(newCfg.Servers) {
			return true
		}
		for i := range a.cfg.Servers {
			if a.cfg.Servers[i].Listen != newCfg.Servers[i].Listen {
				return true
			}
		}
	case config.ServerModeAuto:
		return true
	}
	return false
}

func (a *App) gracefulUpgrade() {
	execPath, err := os.Executable()
	if err != nil {
		a.logger.Error().Err(err).Msg("Failed to get executable path")
		return
	}

	if a.srv == nil {
		a.logger.Error().Msg("Graceful upgrade failed: server instance is nil")
		return
	}

	listeners := a.srv.GetListeners()
	if len(listeners) == 0 {
		a.logger.Error().Msg("Graceful upgrade failed: server has no saved listeners (graceful upgrade not fully implemented)")
		a.logger.Info().Msg("Hint: graceful upgrade requires the server to use manual listener management mode")
		return
	}

	a.upgradeMgr.SetListeners(listeners)

	if err := a.upgradeMgr.GracefulUpgrade(execPath); err != nil {
		a.logger.Error().Err(err).Msg("Graceful upgrade failed")
		return
	}

	a.logger.LogStartup("Graceful upgrade started, new process is taking over", nil)

	timeout := a.cfg.Shutdown.GracefulTimeout
	if timeout <= 0 {
		timeout = 30 * time.Second
	}
	a.shutdownStream()
	a.shutdownHTTP2()
	a.shutdownHTTP3()
	_ = a.srv.GracefulStop(timeout)
}

func sigName(sig syscall.Signal) string {
	//nolint:exhaustive // Only handling app-relevant signals.
	switch sig {
	case syscall.SIGTERM:
		return "SIGTERM"
	case syscall.SIGINT:
		return "SIGINT"
	case syscall.SIGQUIT:
		return "SIGQUIT"
	case syscall.SIGHUP:
		return "SIGHUP"
	case syscall.SIGUSR1:
		return "SIGUSR1"
	case syscall.SIGUSR2:
		return "SIGUSR2"
	default:
		return fmt.Sprintf("Signal(%d)", sig)
	}
}
