package daemon

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"index-go/config"
	"index-go/service"
)

const (
	pidFileName = "daemon.pid"
	logFileName = "daemon.log"
)

func pidFilePath() string {
	return filepath.Join("logs", pidFileName)
}

func logFilePath() string {
	return filepath.Join("logs", logFileName)
}

// Start 启动后台 Web 服务进程
func Start(cfg *config.Config) error {
	if running, _ := IsRunning(); running {
		return errors.New("后台服务已在运行，无需重复启动")
	}

	serverRunning, err := isServerRunning()
	if err != nil {
		return err
	}
	if serverRunning {
		return errors.New("检测到系统守护已运行，请先关闭系统守护模式")
	}

	if err := os.MkdirAll("logs", 0755); err != nil {
		return fmt.Errorf("创建日志目录失败: %w", err)
	}

	logFile, err := os.OpenFile(logFilePath(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		return fmt.Errorf("打开日志文件失败: %w", err)
	}
	defer logFile.Close()

	cmd := exec.Command(os.Args[0], "--daemon-run")
	cmd.Stdout = logFile
	cmd.Stderr = logFile
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

	if err := cmd.Start(); err != nil {
		return fmt.Errorf("启动后台进程失败: %w", err)
	}

	if err := os.WriteFile(pidFilePath(), []byte(strconv.Itoa(cmd.Process.Pid)), 0644); err != nil {
		_ = cmd.Process.Kill()
		return fmt.Errorf("写入 PID 文件失败: %w", err)
	}

	return nil
}

// Stop 停止后台进程
func Stop() error {
	pid, err := readPID()
	if err != nil {
		return err
	}

	proc, err := os.FindProcess(pid)
	if err != nil {
		cleanupPidFile()
		return fmt.Errorf("无法找到进程 %d: %w", pid, err)
	}

	_ = proc.Signal(syscall.SIGTERM)

	timeout := time.After(5 * time.Second)
	ticker := time.NewTicker(200 * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-timeout:
			_ = proc.Kill()
			cleanupPidFile()
			return errors.New("停止后台服务超时，已强制结束")
		case <-ticker.C:
			if !processExists(pid) {
				cleanupPidFile()
				return nil
			}
		}
	}
}

// Restart 先停止再启动
func Restart(cfg *config.Config) error {
	_ = Stop()
	return Start(cfg)
}

// Status 返回后台服务运行状态
func Status() string {
	if running, pid := IsRunning(); running {
		return fmt.Sprintf("🟢 运行中 (PID %d)", pid)
	}
	cleanupPidFile()
	return "🔴 未运行"
}

// EnsureStopped 若正在运行则停止
func EnsureStopped() error {
	if running, _ := IsRunning(); running {
		return Stop()
	}
	return nil
}

// IsRunning 判断后台进程是否存在
func IsRunning() (bool, int) {
	pid, err := readPID()
	if err != nil {
		return false, 0
	}
	if processExists(pid) {
		return true, pid
	}
	cleanupPidFile()
	return false, 0
}

func readPID() (int, error) {
	data, err := os.ReadFile(pidFilePath())
	if err != nil {
		if os.IsNotExist(err) {
			return 0, errors.New("后台服务未运行")
		}
		return 0, err
	}
	pid, err := strconv.Atoi(strings.TrimSpace(string(data)))
	if err != nil {
		return 0, fmt.Errorf("PID 文件损坏: %w", err)
	}
	return pid, nil
}

func cleanupPidFile() {
	_ = os.Remove(pidFilePath())
}

func processExists(pid int) bool {
	proc, err := os.FindProcess(pid)
	if err != nil {
		return false
	}
	err = proc.Signal(syscall.Signal(0))
	return err == nil
}

func isServerRunning() (bool, error) {
	mgr, err := service.CreateServiceManager()
	if err != nil {
		return false, nil
	}
	status := mgr.GetServiceStatus()
	return status.IsInstalled && status.IsRunning, nil
}
