package cmd

import (
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"

	"github.com/spf13/cobra"
	"github.com/yeho/klp/internal/database"
	"github.com/yeho/klp/internal/service"
)

var (
	foregroundFlag bool
)

var serviceCmd = &cobra.Command{
	Use:   "service",
	Short: "Service management commands",
	Long:  `Commands for managing the clipboard monitoring service.`,
}

var serviceStartCmd = &cobra.Command{
	Use:   "start",
	Short: "Start the clipboard monitor",
	Long: `Start the clipboard monitoring service as a background daemon.
The service monitors clipboard changes and stores them in the database.

Use --foreground to run in foreground instead of daemonizing.
Use 'klp service stop' to stop the daemon.`,
	Run: func(cmd *cobra.Command, args []string) {
		if !foregroundFlag && os.Getenv("KLP_DAEMON") != "1" {
			// Daemonize: re-exec ourselves in background
			daemonize()
			return
		}

		// Running as daemon or in foreground mode
		runMonitor()
	},
}

var serviceStopCmd = &cobra.Command{
	Use:   "stop",
	Short: "Stop the clipboard monitor daemon",
	Run: func(cmd *cobra.Command, args []string) {
		pidFile := getPidFile()
		data, err := os.ReadFile(pidFile)
		if err != nil {
			fmt.Println("No running daemon found")
			return
		}

		pid, err := strconv.Atoi(string(data))
		if err != nil {
			fmt.Fprintf(os.Stderr, "Invalid PID file: %v\n", err)
			os.Exit(1)
		}

		process, err := os.FindProcess(pid)
		if err != nil {
			fmt.Println("No running daemon found")
			os.Remove(pidFile)
			return
		}

		if err := process.Signal(syscall.SIGTERM); err != nil {
			fmt.Printf("Daemon not running (PID %d)\n", pid)
			os.Remove(pidFile)
			return
		}

		fmt.Printf("Stopped daemon (PID %d)\n", pid)
		os.Remove(pidFile)
	},
}

var serviceStatusCmd = &cobra.Command{
	Use:   "status",
	Short: "Check if the clipboard monitor is running",
	Run: func(cmd *cobra.Command, args []string) {
		pidFile := getPidFile()
		data, err := os.ReadFile(pidFile)
		if err != nil {
			fmt.Println("Daemon is not running")
			return
		}

		pid, err := strconv.Atoi(string(data))
		if err != nil {
			fmt.Println("Daemon is not running (invalid PID file)")
			return
		}

		// Check if process exists
		process, err := os.FindProcess(pid)
		if err != nil {
			fmt.Println("Daemon is not running")
			os.Remove(pidFile)
			return
		}

		// On Unix, FindProcess always succeeds, so we need to send signal 0 to check
		if err := process.Signal(syscall.Signal(0)); err != nil {
			fmt.Println("Daemon is not running")
			os.Remove(pidFile)
			return
		}

		fmt.Printf("Daemon is running (PID %d)\n", pid)
	},
}

func init() {
	rootCmd.AddCommand(serviceCmd)
	serviceCmd.AddCommand(serviceStartCmd)
	serviceCmd.AddCommand(serviceStopCmd)
	serviceCmd.AddCommand(serviceStatusCmd)

	serviceStartCmd.Flags().BoolVarP(&foregroundFlag, "foreground", "f", false, "Run in foreground instead of daemonizing")
}

func getPidFile() string {
	homeDir, _ := os.UserHomeDir()
	return filepath.Join(homeDir, ".config", "klp", "klp.pid")
}

func getLogFile() string {
	homeDir, _ := os.UserHomeDir()
	return filepath.Join(homeDir, ".config", "klp", "klp.log")
}

func daemonize() {
	// Check if already running
	pidFile := getPidFile()
	if data, err := os.ReadFile(pidFile); err == nil {
		if pid, err := strconv.Atoi(string(data)); err == nil {
			if process, err := os.FindProcess(pid); err == nil {
				if err := process.Signal(syscall.Signal(0)); err == nil {
					fmt.Printf("Daemon already running (PID %d)\n", pid)
					return
				}
			}
		}
	}

	// Get executable path
	executable, err := os.Executable()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error getting executable path: %v\n", err)
		os.Exit(1)
	}

	// Ensure config directory exists
	os.MkdirAll(filepath.Dir(pidFile), 0755)

	// Open log file
	logFile, err := os.OpenFile(getLogFile(), os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error opening log file: %v\n", err)
		os.Exit(1)
	}

	// Start daemon process
	cmd := exec.Command(executable, "service", "start", "--foreground")
	cmd.Env = append(os.Environ(), "KLP_DAEMON=1")
	cmd.Stdout = logFile
	cmd.Stderr = logFile
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Setsid: true, // Create new session
	}

	if err := cmd.Start(); err != nil {
		fmt.Fprintf(os.Stderr, "Error starting daemon: %v\n", err)
		os.Exit(1)
	}

	// Write PID file
	if err := os.WriteFile(pidFile, []byte(strconv.Itoa(cmd.Process.Pid)), 0644); err != nil {
		fmt.Fprintf(os.Stderr, "Error writing PID file: %v\n", err)
	}

	fmt.Printf("Daemon started (PID %d)\n", cmd.Process.Pid)
	fmt.Printf("Log file: %s\n", getLogFile())
}

func runMonitor() {
	db, err := database.Load(cfg.DBPath())
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error loading database: %v\n", err)
		os.Exit(1)
	}

	monitor, err := service.NewMonitor(cfg, db)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error creating monitor: %v\n", err)
		os.Exit(1)
	}

	// Clean up PID file on exit
	pidFile := getPidFile()
	defer os.Remove(pidFile)

	if err := monitor.Run(); err != nil {
		fmt.Fprintf(os.Stderr, "Error running monitor: %v\n", err)
		os.Exit(1)
	}
}
