package service

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"time"

	"u-desk/internal/common"
)

// ==================== 类型定义 ====================

// UpdateService 更新服务
type UpdateService struct {
	checkURL string // 版本检查接口 URL
}

// RemoteVersionInfo 远程版本信息
type RemoteVersionInfo struct {
	Version     string `json:"version"`
	DownloadURL string `json:"download_url"`
	Changelog   string `json:"changelog"`
	ForceUpdate bool   `json:"force_update"`
	ReleaseDate string `json:"release_date"`
	FileSize    int64  `json:"file_size"`
}

// UpdateCheckResult 更新检查结果
type UpdateCheckResult struct {
	HasUpdate      bool   `json:"has_update"`
	CurrentVersion string `json:"current_version"`
	LatestVersion  string `json:"latest_version"`
	DownloadURL    string `json:"download_url"`
	Changelog      string `json:"changelog"`
	ForceUpdate    bool   `json:"force_update"`
	ReleaseDate    string `json:"release_date"`
	FileSize       int64  `json:"file_size"`
}

// InstallResult 安装结果
type InstallResult struct {
	Success bool   `json:"success"`
	Message string `json:"message"`
}

// ==================== 更新服务 ====================

// NewUpdateService 创建更新服务
func NewUpdateService(checkURL string) *UpdateService {
	return &UpdateService{
		checkURL: checkURL,
	}
}

// CheckUpdate 检查更新
func (s *UpdateService) CheckUpdate() (*UpdateCheckResult, error) {
	config, err := LoadUpdateConfig()
	if err != nil {
		return nil, fmt.Errorf("加载配置失败: %v", err)
	}

	// 获取当前版本（使用缓存）
	currentVersion, err := ParseVersion(GetCurrentVersion())
	if err != nil {
		return nil, fmt.Errorf("解析当前版本失败: %v", err)
	}

	// 请求远程版本信息
	remoteInfo, err := s.fetchRemoteVersionInfo()
	if err != nil {
		return nil, fmt.Errorf("获取远程版本信息失败: %v", err)
	}

	// 解析远程版本号
	remoteVersion, err := ParseVersion(remoteInfo.Version)
	if err != nil {
		return nil, fmt.Errorf("解析远程版本号失败: %v", err)
	}

	// 比较版本
	hasUpdate := remoteVersion.IsNewerThan(currentVersion)

	// 更新最后检查时间
	config.UpdateLastCheckTime()

	return &UpdateCheckResult{
		HasUpdate:      hasUpdate,
		CurrentVersion: GetCurrentVersion(),
		LatestVersion:  remoteInfo.Version,
		DownloadURL:    remoteInfo.DownloadURL,
		Changelog:      remoteInfo.Changelog,
		ForceUpdate:    remoteInfo.ForceUpdate,
		ReleaseDate:    remoteInfo.ReleaseDate,
		FileSize:       remoteInfo.FileSize,
	}, nil
}

// fetchRemoteVersionInfo 获取远程版本信息
func (s *UpdateService) fetchRemoteVersionInfo() (*RemoteVersionInfo, error) {
	if s.checkURL == "" {
		return nil, fmt.Errorf("版本检查 URL 未配置，请先设置检查地址")
	}

	// 添加时间戳参数防止缓存
	timestamp := time.Now().UnixMilli()
	var requestURL string
	if strings.Contains(s.checkURL, "?") {
		requestURL = fmt.Sprintf("%s&t=%d", s.checkURL, timestamp)
	} else {
		requestURL = fmt.Sprintf("%s?t=%d", s.checkURL, timestamp)
	}

	// 创建 HTTP 客户端，设置超时
	client := &http.Client{
		Timeout: 10 * time.Second,
	}

	// 发送请求
	resp, err := client.Get(requestURL)
	if err != nil {
		return nil, fmt.Errorf("网络请求失败: %v", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("服务器返回错误状态码: %d", resp.StatusCode)
	}

	// 读取响应
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("读取响应失败: %v", err)
	}

	// 解析 JSON
	var remoteInfo RemoteVersionInfo
	if err := json.Unmarshal(body, &remoteInfo); err != nil {
		return nil, fmt.Errorf("解析响应失败: %v", err)
	}

	if remoteInfo.Version == "" {
		return nil, fmt.Errorf("远程版本信息不完整")
	}

	return &remoteInfo, nil
}

// ==================== 安装更新 ====================

// InstallUpdate 安装更新包
func InstallUpdate(installerPath string, autoRestart bool) (*InstallResult, error) {
	return InstallUpdateWithHash(installerPath, autoRestart, "", "")
}

// InstallUpdateWithHash 安装更新包（带哈希验证）
func InstallUpdateWithHash(installerPath string, autoRestart bool, expectedHash string, hashType string) (*InstallResult, error) {
	if _, err := os.Stat(installerPath); os.IsNotExist(err) {
		return nil, fmt.Errorf("安装文件不存在: %s", installerPath)
	}

	// 哈希验证
	if expectedHash != "" && hashType != "" {
		valid, err := VerifyFileHash(installerPath, expectedHash, hashType)
		if err != nil {
			return &InstallResult{Success: false, Message: "文件验证失败: " + err.Error()}, nil
		}
		if !valid {
			return &InstallResult{Success: false, Message: "文件哈希值不匹配，文件可能已损坏或被篡改"}, nil
		}
	}

	// 备份
	backupPath, err := BackupApplication()
	if err != nil {
		return &InstallResult{Success: false, Message: fmt.Sprintf("备份失败: %v", err)}, nil
	}

	// 安装
	ext := filepath.Ext(installerPath)
	switch ext {
	case ".exe":
		if runtime.GOOS != "windows" {
			return &InstallResult{Success: false, Message: "当前系统不是 Windows，无法安装 .exe 文件"}, nil
		}
		err = installExe(installerPath)
	case ".zip":
		err = installZip(installerPath)
	default:
		return nil, fmt.Errorf("不支持的安装包格式: %s", ext)
	}

	// 处理安装结果
	if err != nil {
		// 安装失败，尝试回滚
		if backupPath != "" {
			_ = rollbackFromBackup(backupPath)
		}
		return &InstallResult{Success: false, Message: fmt.Sprintf("安装失败: %v", err)}, nil
	}

	// 自动重启
	if autoRestart {
		go func() {
			time.Sleep(2 * time.Second)
			restartApplication()
		}()
	}

	return &InstallResult{Success: true, Message: "安装成功"}, nil
}

// ==================== 安装相关辅助函数 ====================

// installExe 安装 exe 文件
func installExe(exePath string) error {
	execPath, err := os.Executable()
	if err != nil {
		return err
	}
	return replaceExecutableFile(exePath, execPath)
}

// installZip 安装 ZIP 压缩包
func installZip(zipPath string) error {
	// 这里需要导入 archive/zip 包
	return fmt.Errorf("ZIP 安装暂未实现")
}

// replaceExecutableFile 替换可执行文件（Windows 和 Unix 通用逻辑）
func replaceExecutableFile(newFilePath, execPath string) error {
	if runtime.GOOS == "windows" {
		return replaceExecutableFileWindows(newFilePath, execPath)
	}

	// Unix-like: 直接替换
	if err := copyFile(newFilePath, execPath); err != nil {
		return fmt.Errorf("复制文件失败: %v", err)
	}
	return os.Chmod(execPath, 0755)
}

// replaceExecutableFileWindows Windows 平台替换可执行文件
func replaceExecutableFileWindows(newFilePath, execPath string) error {
	oldExecPath := execPath + ".old"
	newExecPathTemp := execPath + ".new"

	// 清理旧文件
	os.Remove(oldExecPath)
	os.Remove(newExecPathTemp)

	// 复制新文件到临时位置
	if err := copyFile(newFilePath, newExecPathTemp); err != nil {
		return fmt.Errorf("复制新文件失败: %v", err)
	}

	// 尝试重命名当前文件（如果失败，说明文件正在使用）
	if err := os.Rename(execPath, oldExecPath); err != nil {
		return nil
	}

	// 替换文件
	if err := os.Rename(newExecPathTemp, execPath); err != nil {
		os.Rename(oldExecPath, execPath) // 恢复
		return fmt.Errorf("替换文件失败: %v", err)
	}

	// 延迟删除旧文件
	go func() {
		time.Sleep(10 * time.Second)
		os.Remove(oldExecPath)
	}()
	return nil
}

// restartApplication 重启应用
func restartApplication() {
	execPath, err := os.Executable()
	if err != nil {
		return
	}

	currentPID := os.Getpid()
	if runtime.GOOS != "windows" {
		return
	}

	replacePendingFile(execPath)

	// 创建并执行重启脚本
	tempDir := os.TempDir()
	batFile := filepath.Join(tempDir, fmt.Sprintf("restart_%d.bat", currentPID))
	execDir := filepath.Dir(execPath)

	batContent := fmt.Sprintf(`@echo off
cd /d "%s"
start "" "%s"
timeout /t 3 /nobreak >nul
taskkill /PID %d /F >nul 2>&1
del "%%~f0"
`, execDir, execPath, currentPID)

	if err := os.WriteFile(batFile, []byte(batContent), 0644); err != nil {
		fallbackRestart(execPath)
		return
	}

	cmd := exec.Command("cmd", "/C", batFile)
	cmd.Stdout = nil
	cmd.Stderr = nil
	if err := cmd.Start(); err != nil {
		fallbackRestart(execPath)
		return
	}

	os.Exit(0)
}

// fallbackRestart 降级重启方案
func fallbackRestart(execPath string) {
	exec.Command("cmd", "/C", "start", "", execPath).Start()
	time.Sleep(2 * time.Second)
	os.Exit(0)
}

// replacePendingFile 替换待替换的文件（.new -> 可执行文件）
func replacePendingFile(execPath string) error {
	newExecPathTemp := execPath + ".new"
	if _, err := os.Stat(newExecPathTemp); os.IsNotExist(err) {
		return nil // 没有待替换文件
	}

	oldExecPath := execPath + ".old"
	os.Remove(oldExecPath)
	if err := os.Rename(newExecPathTemp, execPath); err != nil {
		return fmt.Errorf("文件替换失败: %v", err)
	}
	return nil
}

// rollbackFromBackup 从备份恢复
func rollbackFromBackup(backupPath string) error {
	execPath, err := os.Executable()
	if err != nil {
		return err
	}
	return copyFile(backupPath, execPath)
}

// BackupApplication 备份当前应用
func BackupApplication() (string, error) {
	execPath, err := os.Executable()
	if err != nil {
		return "", err
	}

	backupDir := filepath.Join(common.GetUserDataDir(), "backups")
	if err := os.MkdirAll(backupDir, 0755); err != nil {
		return "", fmt.Errorf("创建备份目录失败: %v", err)
	}

	timestamp := time.Now().Format("20060102-150405")
	backupFileName := fmt.Sprintf("u-desk-backup-%s%s", timestamp, filepath.Ext(execPath))
	backupPath := filepath.Join(backupDir, backupFileName)

	if err := copyFile(execPath, backupPath); err != nil {
		return "", fmt.Errorf("复制文件失败: %v", err)
	}

	return backupPath, nil
}

// copyFile 复制文件
func copyFile(src, dst string) error {
	sourceFile, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sourceFile.Close()

	destFile, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer destFile.Close()

	_, err = destFile.ReadFrom(sourceFile)
	return err
}
