package main

import (
	"bytes"
	"embed"
	"fmt"
	"log"
	"os"
	"os/exec"
	"runtime"
	"strings"
	"sync"
	"syscall"
	"text/template"
)

//go:embed bin/*
var embeddedFrpc embed.FS

//go:embed frpc.tmpl
var FrpcTemplateContent string

var (
	cachedFrpcPath string
	frpcPathMutex  sync.Mutex
)

// getFrpcPath 获取当前平台对应的 frpc 路径（优先使用缓存）
func getFrpcPath() (string, error) {
	frpcPathMutex.Lock()
	defer frpcPathMutex.Unlock()

	// 如果缓存有效，直接返回
	if cachedFrpcPath != "" {
		if _, err := os.Stat(cachedFrpcPath); err == nil {
			return cachedFrpcPath, nil
		}
		// 缓存失效，清空
		cachedFrpcPath = ""
	}

	// 确定文件名
	var fileName string
	switch {
	case runtime.GOOS == "windows" && runtime.GOARCH == "amd64":
		fileName = "frpc_windows_amd64.exe"
	case runtime.GOOS == "linux" && runtime.GOARCH == "amd64":
		fileName = "frpc_linux_amd64"
	case runtime.GOOS == "linux" && runtime.GOARCH == "arm64":
		fileName = "frpc_linux_arm64"
	default:
		// 降级：尝试从系统 PATH 查找 frpc
		path, err := exec.LookPath("frpc")
		if err == nil {
			cachedFrpcPath = path
			return path, nil
		}
		return "", fmt.Errorf("不支持的平台: %s/%s", runtime.GOOS, runtime.GOARCH)
	}

	// 从 embed 读取
	data, err := embeddedFrpc.ReadFile("bin/" + fileName)
	if err != nil {
		// 降级到 PATH
		path, lookErr := exec.LookPath("frpc")
		if lookErr == nil {
			cachedFrpcPath = path
			return path, nil
		}
		return "", fmt.Errorf("未找到 frpc 文件 (embed 和 PATH 均无): %w", err)
	}

	// 写入临时目录
	tmpPath := os.TempDir() + "/frpc"
	if runtime.GOOS == "windows" {
		tmpPath += ".exe"
	}

	// 尝试写入（如果文件已存在且正在使用，忽略错误，直接使用已有文件）
	if err := os.WriteFile(tmpPath, data, 0755); err != nil {
		// 如果写入失败，检查是否已有可用的 frpc
		if _, statErr := os.Stat(tmpPath); statErr == nil {
			cachedFrpcPath = tmpPath
			return tmpPath, nil
		}
		return "", fmt.Errorf("写入临时文件失败: %w", err)
	}

	cachedFrpcPath = tmpPath
	return tmpPath, nil
}

// GenerateFrpcConfig 生成 frpc.toml 配置文件
func GenerateFrpcConfig() error {
	cfg, err := GetGlobalConfig()
	if err != nil {
		return fmt.Errorf("读取全局配置失败: %w", err)
	}

	proxies, err := GetProxies()
	if err != nil {
		return fmt.Errorf("读取隧道列表失败: %w", err)
	}

	var activeProxies []Proxy
	for _, p := range proxies {
		if p.Enabled {
			activeProxies = append(activeProxies, p)
		}
	}

	data := struct {
		*GlobalConfig
		Proxies []Proxy
	}{
		GlobalConfig: cfg,
		Proxies:      activeProxies,
	}

	// ===== 核心改动 =====
	// 优先使用外部模板文件 frpc.tmpl，如果不存在则使用 embed 中的模板
	var tmplContent string
	if _, err := os.Stat("frpc.tmpl"); err == nil {
		// 外部文件存在，读取它
		content, readErr := os.ReadFile("frpc.tmpl")
		if readErr == nil {
			tmplContent = string(content)
		} else {
			// 读取失败时回退到 embed
			tmplContent = FrpcTemplateContent
		}
	} else {
		// 外部文件不存在，使用 embed 中的模板
		tmplContent = FrpcTemplateContent
	}
	// ===== 核心改动结束 =====

	tmpl, err := template.New("frpc").Parse(tmplContent)
	if err != nil {
		return fmt.Errorf("解析模板失败: %w", err)
	}

	var buf bytes.Buffer
	if err := tmpl.Execute(&buf, data); err != nil {
		return fmt.Errorf("渲染模板失败: %w", err)
	}

	if err := os.WriteFile("./frpc.toml", buf.Bytes(), 0644); err != nil {
		return fmt.Errorf("写入配置文件失败: %w", err)
	}

	return nil
}

// StartFrpc 启动 frpc（后台运行）
func StartFrpc() error {
	frpcPath, err := getFrpcPath()
	if err != nil {
		return fmt.Errorf("获取 frpc 路径失败: %w", err)
	}

	// 确保配置文件存在
	if _, err := os.Stat("./frpc.toml"); os.IsNotExist(err) {
		if err := GenerateFrpcConfig(); err != nil {
			return fmt.Errorf("生成配置文件失败: %w", err)
		}
	}

	// 检查是否已在运行
	running, _ := GetFrpcStatus()
	if running {
		return nil
	}

	cmd := exec.Command(frpcPath, "-c", "./frpc.toml")
	if runtime.GOOS == "windows" {
		cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
	}
	if err := cmd.Start(); err != nil {
		return fmt.Errorf("启动 frpc 失败: %w", err)
	}
	return nil
}

// ReloadFrpc 热加载 frpc 配置（仅发信号，不重复写入二进制）
// ReloadFrpc 热加载 frpc 配置，失败时自动降级为重启
func ReloadFrpc() error {
	running, err := GetFrpcStatus()
	if err != nil {
		return fmt.Errorf("检查 frpc 状态失败: %w", err)
	}
	if !running {
		return StartFrpc()
	}

	if cachedFrpcPath == "" {
		if _, err := getFrpcPath(); err != nil {
			return fmt.Errorf("获取 frpc 路径失败: %w", err)
		}
	}

	cmd := exec.Command(cachedFrpcPath, "reload", "-c", "./frpc.toml")
	_, err = cmd.CombinedOutput() // ← 这里改了
	if err != nil {
		log.Printf("⚠️  热加载失败 (%v)，自动降级为重启 frpc", err)
		if stopErr := StopFrpc(); stopErr != nil {
			return fmt.Errorf("停止 frpc 失败: %w", stopErr)
		}
		if startErr := StartFrpc(); startErr != nil {
			return fmt.Errorf("启动 frpc 失败: %w", startErr)
		}
		return nil
	}
	return nil
}

// StopFrpc 停止 frpc
func StopFrpc() error {
	if runtime.GOOS == "windows" {
		cmd := exec.Command("taskkill", "/F", "/IM", "frpc.exe")
		if err := cmd.Run(); err != nil && !strings.Contains(err.Error(), "not found") {
			return fmt.Errorf("停止 frpc 失败: %w", err)
		}
		return nil
	}
	cmd := exec.Command("pkill", "-f", "frpc")
	if err := cmd.Run(); err != nil && !strings.Contains(err.Error(), "no process") {
		return fmt.Errorf("停止 frpc 失败: %w", err)
	}
	return nil
}

// GetFrpcStatus 检查 frpc 是否在运行
func GetFrpcStatus() (bool, error) {
	if runtime.GOOS == "windows" {
		cmd := exec.Command("tasklist", "/FI", "IMAGENAME eq frpc.exe")
		output, err := cmd.CombinedOutput()
		if err != nil {
			return false, nil
		}
		return strings.Contains(string(output), "frpc.exe"), nil
	}
	cmd := exec.Command("pgrep", "-f", "frpc")
	output, err := cmd.Output()
	if err != nil {
		return false, nil
	}
	return len(output) > 0, nil
}
