package windows

// Windows 版本的客户端实现
import (
	"client/internal/db"
	message "client/internal/msg"
	"client/internal/utils"
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"gopkg.in/ini.v1"
)

type WindowsClient struct {
	mu             sync.Mutex
	Auth           string
	MachineCode    string
	ID             string
	MiningConfig   message.MiningConfig
	db             *db.SQLiteServer
	Status         int             // 当前client状态，1正在工作，2空闲
	currentProcess *currentProcess // 当前挖矿进程及类型
}

type currentProcess struct {
	process *exec.Cmd
	miner   string
}

// CheckPermission 检查当前进程是否具有管理员权限（Windows）
// 通过执行 `net session` 命令来判断：该命令只有在管理员权限下才会成功。
func CheckPermission() error {
	cmd := exec.Command("net", "session")
	if err := cmd.Run(); err != nil {
		return fmt.Errorf("当前用户不是管理员，请以管理员身份运行本程序（右键以管理员运行）")
	}
	return nil
}

func NewWindowsClient(auth string) *WindowsClient {
	cfg, err := ini.Load("mining.windows.conf")
	if err != nil {
		log.Printf("获取挖矿配置失败: %v", err)
		log.Printf("客户端已退出，请重新检查配置文件(%s)是否存在", "mining.windows.conf")
		return nil
	}
	// 解析配置
	var miningConfig message.MiningConfig
	// 解析 [bzminer] 部分
	sectionBzMiner := cfg.Section("bzminer")
	miningConfig.BzMinerPath = sectionBzMiner.Key("path").String()

	// 解析 [lolminer] 部分
	sectionLolMiner := cfg.Section("lolminer")
	miningConfig.LolMinerPath = sectionLolMiner.Key("path").String()

	// 解析 [rigel] 部分
	sectionRigel := cfg.Section("rigel")
	miningConfig.RigelPath = sectionRigel.Key("path").String()

	// 解析 [proxy] 部分
	sectionProxy := cfg.Section("proxy")
	miningConfig.ProxyEnabled, _ = sectionProxy.Key("proxy").Bool()

	if miningConfig.LolMinerPath != "" {
		result := utils.CheckFileExists(miningConfig.LolMinerPath)
		if !result {
			log.Printf("未检测到lolminer挖矿软件的存在，请确认是否已经安装lolminer，并核对下列路径：%s", miningConfig.LolMinerPath)
			log.Fatalln("客户端已退出，确认路径后请重启客户端")
			return nil
		}
	}

	if miningConfig.RigelPath != "" {
		result := utils.CheckFileExists(miningConfig.RigelPath)
		if !result {
			log.Printf("未检测到rigel挖矿软件的存在，请确认是否已经安装rigel，并核对下列路径：%s", miningConfig.RigelPath)
			log.Fatalln("客户端已退出，确认路径后请重启客户端")
			return nil
		}
	}
	var client = &WindowsClient{}
	client.Auth = auth
	client.MiningConfig = miningConfig
	client.Status = 2

	// 初始化sqlite3数据库
	db := db.NewSQLiteServer()
	client.db = db

	mac, err := client.GetMACAddress()
	if err != nil {
		log.Println(err)
		log.Fatalln("获取当前主机信息失败，程序已退出，请检查网络后重新启动本客户端。")
	}

	client.MachineCode = mac
	client.ID = auth + "." + mac

	return client
}

func (w *WindowsClient) initHistoryTask() {
	exsits, task := w.db.LoadMiningTask()
	if exsits {
		currentTs := time.Now().Unix()
		if currentTs < int64(task.EndTimestamp) {
			w.mu.Lock()
			w.Status = 1
			w.mu.Unlock()
			err := w.Mining(task)
			if err != nil {
				log.Printf("重新开启挖矿任务失败，请手动检查：%v", err)
				return
			}
		} else {
			w.db.FinishMiningTask(task)
		}
	}
}

// 获取 NVIDIA 显卡信息
func getNvidiaGPUInfo() (map[int]message.GPU, error) {
	// 使用 nvidia-smi 列出所有 GPU
	cmd := exec.Command("nvidia-smi", "--query-gpu=name,memory.total", "--format=csv,noheader,nounits")
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("无法执行 nvidia-smi 命令: %v", err)
	}

	// 解析输出
	info := strings.TrimSpace(string(output))
	lines := strings.Split(info, "\n")

	gpus := make(map[int]message.GPU)

	for i, line := range lines {
		parts := strings.Split(strings.TrimSpace(line), ", ")
		if len(parts) < 2 {
			return nil, fmt.Errorf("nvidia-smi 输出格式错误")
		}

		// 解析显存容量
		mem, err := parseMemory(parts[1])
		if err != nil {
			return nil, fmt.Errorf("无法解析显存容量: %v", err)
		}

		// 将每个 GPU 的信息存储到 map 中
		gpus[i] = message.GPU{
			Brand: "NVIDIA",
			Model: parts[0],
			Mem:   mem,
		}
	}

	return gpus, nil
}

// 解析显存容量字符串并返回 float64 类型
func parseMemory(memStr string) (float64, error) {
	var mem float64
	_, err := fmt.Sscanf(memStr, "%f", &mem)
	if err != nil {
		return 0, fmt.Errorf("显存解析失败: %v", err)
	}
	return mem, nil
}

// 获取其他显卡信息（如 Intel 或 AMD）
func getOtherGPUInfo() (map[int]message.GPU, error) {
	// Windows 下使用 wmic 命令获取显卡信息
	cmd := exec.Command("wmic", "path", "win32_VideoController", "get", "name")
	output, err := cmd.Output()
	if err != nil {
		return nil, fmt.Errorf("无法执行 wmic 命令: %v", err)
	}

	// 解析输出
	info := strings.TrimSpace(string(output))
	lines := strings.Split(info, "\n")

	gpus := make(map[int]message.GPU)
	gpuIndex := 0

	for _, line := range lines {
		line = strings.TrimSpace(line)
		// 跳过标题行和空行
		if line == "" || line == "Name" {
			continue
		}
		// 判断显卡品牌
		brand := "Unknown"
		if strings.Contains(line, "NVIDIA") {
			brand = "NVIDIA"
		} else if strings.Contains(line, "AMD") || strings.Contains(line, "Radeon") {
			brand = "AMD"
		} else if strings.Contains(line, "Intel") {
			brand = "Intel"
		}

		// 将 GPU 信息存储到 map 中
		gpus[gpuIndex] = message.GPU{
			Brand: brand,
			Model: line,
			Mem:   0, // wmic 无法直接获取显存，需要其他方法
		}
		gpuIndex++
	}

	if len(gpus) == 0 {
		return nil, fmt.Errorf("未找到显卡信息")
	}

	return gpus, nil
}

// 获取所有 GPU 信息
func (w *WindowsClient) GetGPUInfo() (map[int]message.GPU, error) {
	// 尝试获取 NVIDIA GPU 信息
	gpus, err := getNvidiaGPUInfo()
	if err == nil {
		return gpus, nil
	}

	// 如果没有 NVIDIA 显卡，尝试获取其他类型显卡信息
	return getOtherGPUInfo()
}

// Windows 版本：使用主机 UUID 作为“机器码”返回给上层（仍然复用 GetMACAddress 接口名）
// 通过 PowerShell 命令：Get-WmiObject -Class Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID
func (w *WindowsClient) GetMACAddress() (string, error) {
	// 注意：在 Go 中调用 PowerShell
	cmd := exec.Command("powershell", "-Command", "Get-WmiObject -Class Win32_ComputerSystemProduct | Select-Object -ExpandProperty UUID")
	out, err := cmd.Output()
	if err != nil {
		return "", fmt.Errorf("获取主机 UUID 失败: %v", err)
	}

	uuid := strings.TrimSpace(string(out))
	if uuid == "" || strings.EqualFold(uuid, "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF") {
		return "", fmt.Errorf("获取到的主机 UUID 无效: %q", uuid)
	}

	// 将 UUID 中的 "." 替换成 "::"
	// uuid = strings.Replace(uuid, ".", "::", -1)

	// 统一转换为大写，去掉花括号
	uuid = strings.Trim(uuid, "{}")
	uuid = strings.ToUpper(uuid)

	return uuid, nil
}

/*
配置lolminer
Windows 下使用 lolMiner.exe
*/
func (w *WindowsClient) lolminer(cfg message.ConfigurationMiningMsg) {
	w.mu.Lock()
	if w.Status != 2 {
		log.Printf("当前还有挖矿任务正在进行中：币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
		return
	}
	w.Status = 1
	w.mu.Unlock()

	var address string
	if cfg.WalletMining {
		address = cfg.WalletAddress
	} else {
		address = cfg.PoolUser
	}

	dir := w.MiningConfig.LolMinerPath
	name := filepath.Join(dir, "lolMiner.exe")
	args := []string{"--algo", cfg.Coin, "--pool", cfg.PoolUrl, "--user", address + "." + cfg.WorkerID}
	cmd := exec.Command(name, args...)
	cmd.Dir = dir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	log.Printf("执行命令：%s %s", name, strings.Join(args, " "))
	// 启动进程
	err := cmd.Start()
	if err != nil {
		log.Printf("Error starting lolMiner: %v", err)
		return
	}
	// 添加执行记录
	go func() {
		err := w.db.InsertMiningTask(cfg)
		if err != nil {
			log.Printf("本次挖矿任务记录失败：%v", err)
		}
	}()
	// 获取 lolMiner 的进程 ID
	fmt.Printf("lolMiner started with PID: %d\n", cmd.Process.Pid)

	// 记录当前挖矿进程
	w.mu.Lock()
	w.currentProcess = &currentProcess{
		process: cmd,
		miner:   "lolminer",
	}
	w.mu.Unlock()
	// 获取当前时间戳（秒级）
	currentTimestamp := time.Now().Unix()
	endTimestamp := int64(cfg.EndTimestamp)
	// 计算目标时间戳和当前时间戳之间的差值
	if endTimestamp <= currentTimestamp {
		// 如果目标时间已经到达，立即结束挖矿进程
		fmt.Println("目标时间已经到达，立即结束 lolMiner 挖矿进程")
		w.StopMining()
		// 修改执行记录
		go func() {
			err := w.db.FinishMiningTask(cfg)
			if err != nil {
				log.Printf("修改执行记录失败：%v", err)
			}
		}()
	} else {
		// 计算需要等待的秒数
		waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
		fmt.Printf("当前时间戳：%d，目标时间戳：%d，剩余时间：%v\n", currentTimestamp, endTimestamp, waitDuration)
		// 使用 time.Sleep 等待直到目标时间戳
		time.Sleep(waitDuration)
		fmt.Println("目标时间到达，开始执行操作，结束 lolMiner 挖矿进程")
		// 通过 StopMining 统一停止挖矿进程
		w.StopMining()
		// 修改执行记录
		go func() {
			err := w.db.FinishMiningTask(cfg)
			if err != nil {
				log.Printf("修改执行记录失败：%v", err)
			}
		}()
	}
	log.Printf("当前挖矿任务已执行完毕：币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
	w.mu.Lock()
	w.Status = 2
	w.mu.Unlock()
}

/*
配置bzminer
Windows 下使用 bzminer.exe
*/
func (w *WindowsClient) bzminer(cfg message.ConfigurationMiningMsg) {
	w.mu.Lock()
	if w.Status != 2 {
		log.Printf("当前还有挖矿任务正在进行中：币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
		return
	}
	w.Status = 1
	w.mu.Unlock()
	var address string
	if cfg.WalletMining {
		address = cfg.WalletAddress
	} else {
		address = cfg.PoolUser
	}
	dir := w.MiningConfig.BzMinerPath
	name := filepath.Join(dir, "bzminer.exe")
	args := []string{"-a", cfg.Coin, "-w", address + "." + cfg.WorkerID, "-p", cfg.PoolUrl}
	cmd := exec.Command(name, args...)
	cmd.Dir = dir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	log.Printf("执行命令：%s %s", name, strings.Join(args, " "))
	// 启动进程
	err := cmd.Start()
	if err != nil {
		log.Printf("Error starting bzminer: %v", err)
		return
	}
	// 添加执行记录
	go func() {
		err := w.db.InsertMiningTask(cfg)
		if err != nil {
			log.Printf("本次挖矿任务记录失败：%v", err)
		}
	}()
	// 获取 bzminer 的进程 ID
	fmt.Printf("bzminer started with PID: %d\n", cmd.Process.Pid)

	// 记录当前挖矿进程
	w.mu.Lock()
	w.currentProcess = &currentProcess{
		process: cmd,
		miner:   "bzminer",
	}
	w.mu.Unlock()

	// 获取当前时间戳（秒级）
	currentTimestamp := time.Now().Unix()
	endTimestamp := int64(cfg.EndTimestamp)
	// 计算目标时间戳和当前时间戳之间的差值
	if endTimestamp <= currentTimestamp {
		// 如果目标时间已经到达，立即结束挖矿进程
		fmt.Println("目标时间已经到达，立即结束 bzminer 挖矿进程")
		w.StopMining()
		fmt.Println("bzminer process killed.")
	} else {
		// 计算需要等待的秒数
		waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
		fmt.Printf("当前时间戳：%d，目标时间戳：%d，剩余时间：%v\n", currentTimestamp, endTimestamp, waitDuration)
		// 使用 time.Sleep 等待直到目标时间戳
		time.Sleep(waitDuration)
		fmt.Println("目标时间到达，开始执行操作，结束 bzminer 挖矿进程")
		// 通过 StopMining 统一停止挖矿进程
		w.StopMining()
		// 修改执行记录
		go func() {
			err := w.db.FinishMiningTask(cfg)
			if err != nil {
				log.Printf("修改执行记录失败：%v", err)
			}
		}()
		// 输出进程被结束的信息
		fmt.Println("bzminer process killed.")
	}
	log.Printf("当前挖矿任务已执行完毕：币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
	w.mu.Lock()
	w.Status = 2
	w.mu.Unlock()
}

/*
配置rigel
Windows 下使用 rigel.exe
*/
func (w *WindowsClient) rigel(cfg message.ConfigurationMiningMsg) {
	w.mu.Lock()
	if w.Status != 2 {
		log.Printf("当前还有挖矿任务正在进行中：币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
		return
	}
	w.Status = 1
	w.mu.Unlock()
	var address string
	if cfg.WalletMining {
		address = cfg.WalletAddress
	} else {
		address = cfg.PoolUser
	}
	dir := w.MiningConfig.RigelPath
	name := filepath.Join(dir, "rigel.exe")
	// 禁用 rigel 内置 watchdog，避免自动拉起子进程
	args := []string{"--no-watchdog", "-a", strings.ToLower(cfg.Algo), "-o", cfg.PoolUrl, "-u", address, "-w", cfg.WorkerID, "--log-file", "logs/miner.log"}
	cmd := exec.Command(name, args...)
	cmd.Dir = dir
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	log.Printf("执行命令：%s %s", name, strings.Join(args, " "))
	// 启动进程
	err := cmd.Start()
	if err != nil {
		log.Printf("Error starting rigel: %v", err)
		return
	}
	// 添加执行记录
	go func() {
		err := w.db.InsertMiningTask(cfg)
		if err != nil {
			log.Printf("本次挖矿任务记录失败：%v", err)
		}
	}()
	// 获取 rigel 的进程 ID
	fmt.Printf("rigel started with PID: %d\n", cmd.Process.Pid)

	// 记录当前挖矿进程
	w.mu.Lock()
	w.currentProcess = &currentProcess{
		process: cmd,
		miner:   "rigel",
	}
	w.mu.Unlock()

	// 获取当前时间戳（秒级）
	currentTimestamp := time.Now().Unix()
	endTimestamp := int64(cfg.EndTimestamp)
	// 计算目标时间戳和当前时间戳之间的差值
	if endTimestamp <= currentTimestamp {
		// 如果目标时间已经到达，立即结束挖矿进程
		fmt.Println("目标时间已经到达，立即结束 rigel 挖矿进程")
		w.StopMining()
		fmt.Println("rigel process killed.")
	} else {
		// 计算需要等待的秒数
		waitDuration := time.Second * time.Duration(endTimestamp-currentTimestamp)
		fmt.Printf("当前时间戳：%d，目标时间戳：%d，剩余时间：%v\n", currentTimestamp, endTimestamp, waitDuration)
		// 使用 time.Sleep 等待直到目标时间戳
		time.Sleep(waitDuration)
		fmt.Println("目标时间到达，开始执行操作，结束 rigel 挖矿进程")
		// 通过 StopMining 统一停止挖矿进程
		w.StopMining()
		// 修改执行记录
		go func() {
			err := w.db.FinishMiningTask(cfg)
			if err != nil {
				log.Printf("修改执行记录失败：%v", err)
			}
		}()
		// 输出进程被结束的信息
		fmt.Println("rigel process killed.")
	}
	log.Printf("当前挖矿任务已执行完毕：币=%s, 算法=%s, 矿池=%s, 截止时间=%d", cfg.Coin, cfg.Algo, cfg.Pool, cfg.EndTimestamp)
	w.mu.Lock()
	w.Status = 2
	w.mu.Unlock()
}

// StopMining 主动终止当前挖矿进程
func (w *WindowsClient) StopMining() {
	w.mu.Lock()
	cp := w.currentProcess
	w.mu.Unlock()

	if cp == nil || cp.process == nil || cp.process.Process == nil {
		log.Println("当前没有正在进行的挖矿任务")
		return
	}

	log.Printf("准备停止当前挖矿任务，miner=%s, pid=%d", cp.miner, cp.process.Process.Pid)

	// Windows 下统一使用 Kill 结束进程
	if err := cp.process.Process.Kill(); err != nil {
		log.Printf("停止挖矿进程失败：%v", err)
	}

	// 清理状态
	w.mu.Lock()
	w.currentProcess = nil
	w.Status = 2
	w.mu.Unlock()

	log.Println("当前挖矿任务已被手动停止")
}

// XTM-rigel(1%), XNA-bzminer(1%), CLORE-bzminer(1%), CFX-rigel(2%), IRON-lolminer(1%), NEXA-lolminer(2%), KLS-lolminer(1%), RVN-bzminer(1%), ERG-bzminer(1%), XEL-rigel(2%)
func (w *WindowsClient) Mining(cfg message.ConfigurationMiningMsg) error {
	info := cfg.Coin + "-" + cfg.Algo
	switch info {
	case "SHA3X-SHA3X":
		go w.lolminer(cfg) // SHA3X
	case "XNA-KawPow":
		go w.bzminer(cfg) // xna
	case "CLORE-KawPow":
		go w.bzminer(cfg) // clore
	case "CFX-Octopus":
		go w.rigel(cfg) // octopus
	case "IRON-IronFish":
		go w.lolminer(cfg)
	case "NEXA-NexaPow":
		go w.lolminer(cfg)
	case "KLS-KarlsenHash":
		go w.lolminer(cfg)
	case "RVN-KawPow":
		go w.bzminer(cfg) // rvn
	case "ERG-Autolykos":
		go w.bzminer(cfg) // ergo
	case "XEL-Xelishashv2":
		go w.rigel(cfg)
	default:
		return fmt.Errorf("不支持%s算法", info)
	}
	return nil
}
