package autostart

import (
	"context"
	"fmt"
	"os"
	"path/filepath"

	"frpc-tool/internal/util"
)

type launchdManager struct {
	env       util.Environment
	plistPath string
	label     string
}

func newLaunchdManager(env util.Environment) Manager {
	home, err := os.UserHomeDir()
	if err != nil {
		return nil
	}
	plistPath := filepath.Join(home, "Library", "LaunchAgents", "com.frpc.client.plist")
	return &launchdManager{
		env:       env,
		plistPath: plistPath,
		label:     "com.frpc.client",
	}
}

func (m *launchdManager) Kind() util.AutostartKind {
	return util.AutostartLaunchd
}

func (m *launchdManager) IsInstalled(ctx context.Context) (bool, error) {
	_, err := os.Stat(m.plistPath)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, err
	}
	return true, nil
}

func (m *launchdManager) Enable(ctx context.Context) error {
	if err := util.EnsureDir(filepath.Dir(m.plistPath), 0o755); err != nil {
		return err
	}
	if err := util.AtomicWriteFile(m.plistPath, []byte(m.renderPlist()), 0o644); err != nil {
		return err
	}
	_ = util.RunCommand(ctx, nil, nil, "launchctl", "unload", "-w", m.plistPath)
	if err := util.RunCommand(ctx, nil, nil, "launchctl", "load", "-w", m.plistPath); err != nil {
		return fmt.Errorf("加载 launchd 配置失败: %w", err)
	}
	return nil
}

func (m *launchdManager) Disable(ctx context.Context) error {
	_ = util.RunCommand(ctx, nil, nil, "launchctl", "unload", "-w", m.plistPath)
	if err := os.Remove(m.plistPath); err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}

func (m *launchdManager) Start(ctx context.Context) error {
	return util.RunCommand(ctx, nil, nil, "launchctl", "start", m.label)
}

func (m *launchdManager) Stop(ctx context.Context) error {
	return util.RunCommand(ctx, nil, nil, "launchctl", "stop", m.label)
}

func (m *launchdManager) Restart(ctx context.Context) error {
	if err := m.Stop(ctx); err != nil {
		return err
	}
	return m.Start(ctx)
}

func (m *launchdManager) Status(ctx context.Context) (string, error) {
	return util.RunCommandCombined(ctx, "launchctl", "list", m.label)
}

func (m *launchdManager) renderPlist() string {
	return fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>%s</string>
    <key>ProgramArguments</key>
    <array>
      <string>%s</string>
      <string>-c</string>
      <string>%s</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>%s</string>
    <key>StandardErrorPath</key>
    <string>%s</string>
  </dict>
</plist>
`, m.label, m.env.BinaryPath, m.env.ConfigPath, m.env.LogFilePath, m.env.LogFilePath)
}
