import { existsSync, readFileSync, writeFileSync } from 'fs';
import logger from '../utils/logger.js';
import { promises as fs } from 'fs';
import path from 'path';
import { exec } from 'child_process';
import { promisify } from 'util';
import { CONFIG } from '../core/config-manager.js';
import { parseProxyUrl } from '../utils/proxy-utils.js';

const execAsync = promisify(exec);

/**
 * 获取更新检查使用的代理配置
 * @returns {Object|null} 代理配置对象或 null
 */
function getUpdateProxyConfig() {
    if (!CONFIG || !CONFIG.PROXY_URL) {
        return null;
    }
    
    const proxyConfig = parseProxyUrl(CONFIG.PROXY_URL);
    if (proxyConfig) {
        logger.info(`[Update] Using ${proxyConfig.proxyType} proxy for update check: ${CONFIG.PROXY_URL}`);
    }
    return proxyConfig;
}

/**
 * 带代理支持的 fetch 封装
 * @param {string} url - 请求 URL
 * @param {Object} options - fetch 选项
 * @returns {Promise<Response>}
 */
async function fetchWithProxy(url, options = {}) {
    const proxyConfig = getUpdateProxyConfig();
    
    if (proxyConfig) {
        // 使用 undici 的 fetch 支持代理
        const fetchOptions = {
            ...options,
            dispatcher: undefined
        };
        
        // 根据 URL 协议选择合适的 agent
        const urlObj = new URL(url);
        if (urlObj.protocol === 'https:') {
            fetchOptions.agent = proxyConfig.httpsAgent;
        } else {
            fetchOptions.agent = proxyConfig.httpAgent;
        }
        
        // Node.js 原生 fetch 不直接支持 agent，需要使用 undici 或 node-fetch
        // 这里使用动态导入 undici 来支持代理
        try {
            const { fetch: undiciFetch, ProxyAgent } = await import('undici');
            const proxyAgent = new ProxyAgent(CONFIG.PROXY_URL);
            return await undiciFetch(url, {
                ...options,
                dispatcher: proxyAgent
            });
        } catch (importError) {
            // 如果 undici 不可用，回退到原生 fetch（不使用代理）
            logger.warn('[Update] undici not available, falling back to native fetch without proxy');
            return await fetch(url, options);
        }
    }
    
    return await fetch(url, options);
}

/**
 * 比较版本号
 * @param {string} v1 - 版本号1
 * @param {string} v2 - 版本号2
 * @returns {number} 1 if v1 > v2, -1 if v1 < v2, 0 if equal
 */
function compareVersions(v1, v2) {
    // 移除 'v' 前缀（如果有）
    const clean1 = v1.replace(/^v/, '');
    const clean2 = v2.replace(/^v/, '');
    
    const parts1 = clean1.split('.').map(Number);
    const parts2 = clean2.split('.').map(Number);
    
    const maxLen = Math.max(parts1.length, parts2.length);
    
    for (let i = 0; i < maxLen; i++) {
        const num1 = parts1[i] || 0;
        const num2 = parts2[i] || 0;
        
        if (num1 > num2) return 1;
        if (num1 < num2) return -1;
    }
    
    return 0;
}

/**
 * 通过 GitHub API 获取最新版本
 * @returns {Promise<string|null>} 最新版本号或 null
 */
async function getLatestVersionFromGitHub() {
    const GITHUB_REPO = 'justlovemaki/AIClient-2-API';
    const apiUrl = `https://gh-proxy.org/https://api.github.com/repos/${GITHUB_REPO}/tags`;
    
    try {
        logger.info('[Update] Fetching latest version from GitHub API...');
        const response = await fetchWithProxy(apiUrl, {
            headers: {
                'Accept': 'application/vnd.github.v3+json',
                'User-Agent': 'AIClient2API-UpdateChecker'
            },
            timeout: 10000
        });
        
        if (!response.ok) {
            throw new Error(`GitHub API returned ${response.status}: ${response.statusText}`);
        }
        
        const tags = await response.json();
        
        if (!Array.isArray(tags) || tags.length === 0) {
            return null;
        }
        
        // 提取版本号并排序
        const versions = tags
            .map(tag => tag.name)
            .filter(name => /^v?\d+\.\d+/.test(name)); // 只保留符合版本号格式的 tag
        
        if (versions.length === 0) {
            return null;
        }
        
        // 按版本号排序（降序）
        versions.sort((a, b) => compareVersions(b, a));
        
        return versions[0];
    } catch (error) {
        logger.warn('[Update] Failed to fetch from GitHub API:', error.message);
        return null;
    }
}

/**
 * 检查是否有新版本可用
 * 支持两种模式：
 * 1. Git 仓库模式：通过 git 命令获取最新 tag
 * 2. Docker/非 Git 模式：通过 GitHub API 获取最新版本
 * @returns {Promise<Object>} 更新信息
 */
export async function checkForUpdates() {
    const versionFilePath = path.join(process.cwd(), 'VERSION');
    
    // 读取本地版本
    let localVersion = 'unknown';
    try {
        if (existsSync(versionFilePath)) {
            localVersion = readFileSync(versionFilePath, 'utf-8').trim();
        }
    } catch (error) {
        logger.warn('[Update] Failed to read local VERSION file:', error.message);
    }
    
    // 检查是否在 git 仓库中
    let isGitRepo = false;
    try {
        await execAsync('git rev-parse --git-dir');
        isGitRepo = true;
    } catch (error) {
        isGitRepo = false;
        logger.info('[Update] Not in a Git repository, will use GitHub API to check for updates');
    }
    
    let latestTag = null;
    let updateMethod = 'unknown';
    
    if (isGitRepo) {
        // Git 仓库模式：使用 git 命令
        updateMethod = 'git';
        
        // 获取远程 tags
        try {
            logger.info('[Update] Fetching remote tags...');
            await execAsync('git fetch --tags');
        } catch (error) {
            logger.warn('[Update] Failed to fetch tags via git, falling back to GitHub API:', error.message);
            // 如果 git fetch 失败，回退到 GitHub API
            latestTag = await getLatestVersionFromGitHub();
            updateMethod = 'github_api';
        }
        
        // 如果 git fetch 成功，获取最新的 tag
        if (!latestTag && updateMethod === 'git') {
            const isWindows = process.platform === 'win32';
            
            try {
                if (isWindows) {
                    // Windows: 使用 git for-each-ref，这是跨平台兼容的方式
                    const { stdout } = await execAsync('git for-each-ref --sort=-v:refname --format="%(refname:short)" refs/tags --count=1');
                    latestTag = stdout.trim();
                } else {
                    // Linux/macOS: 使用 head 命令，更高效
                    const { stdout } = await execAsync('git tag --sort=-v:refname | head -n 1');
                    latestTag = stdout.trim();
                }
            } catch (error) {
                // 备用方案：获取所有 tags 并在 JavaScript 中排序
                try {
                    const { stdout } = await execAsync('git tag');
                    const tags = stdout.trim().split('\n').filter(t => t);
                    if (tags.length > 0) {
                        // 按版本号排序（降序）
                        tags.sort((a, b) => compareVersions(b, a));
                        latestTag = tags[0];
                    }
                } catch (e) {
                    logger.warn('[Update] Failed to get latest tag via git, falling back to GitHub API:', e.message);
                    latestTag = await getLatestVersionFromGitHub();
                    updateMethod = 'github_api';
                }
            }
        }
    } else {
        // 非 Git 仓库模式（如 Docker 容器）：使用 GitHub API
        updateMethod = 'github_api';
        latestTag = await getLatestVersionFromGitHub();
    }
    
    if (!latestTag) {
        return {
            hasUpdate: false,
            localVersion,
            latestVersion: null,
            updateMethod,
            error: 'Unable to get latest version information'
        };
    }
    
    // 比较版本
    const comparison = compareVersions(latestTag, localVersion);
    const hasUpdate = comparison > 0;
    
    logger.info(`[Update] Local version: ${localVersion}, Latest version: ${latestTag}, Has update: ${hasUpdate}, Method: ${updateMethod}`);
    
    return {
        hasUpdate,
        localVersion,
        latestVersion: latestTag,
        updateMethod,
        error: null
    };
}

/**
 * 执行更新操作
 * @returns {Promise<Object>} 更新结果
 */
export async function performUpdate() {
    // 首先检查是否有更新
    const updateInfo = await checkForUpdates();
    
    if (updateInfo.error) {
        throw new Error(updateInfo.error);
    }
    
    if (!updateInfo.hasUpdate) {
        return {
            success: true,
            message: 'Already at the latest version',
            localVersion: updateInfo.localVersion,
            latestVersion: updateInfo.latestVersion,
            updated: false
        };
    }
    
    const latestTag = updateInfo.latestVersion;
    
    // 检查更新方式 - 如果是通过 GitHub API 获取的版本信息，说明不在 Git 仓库中
    if (updateInfo.updateMethod === 'github_api') {
        // Docker/非 Git 环境，通过下载 tarball 更新
        logger.info('[Update] Running in Docker/non-Git environment, will download and extract tarball');
        return await performTarballUpdate(updateInfo.localVersion, latestTag);
    }
    
    logger.info(`[Update] Starting update to ${latestTag}...`);
    
    // 检查是否有未提交的更改
    try {
        const { stdout: statusOutput } = await execAsync('git status --porcelain');
        if (statusOutput.trim()) {
            // 有未提交的更改，先 stash
            logger.info('[Update] Stashing local changes...');
            await execAsync('git stash');
        }
    } catch (error) {
        logger.warn('[Update] Failed to check git status:', error.message);
    }
    
    // 执行 checkout 到最新 tag
    try {
        logger.info(`[Update] Checking out to ${latestTag}...`);
        await execAsync(`git checkout ${latestTag}`);
    } catch (error) {
        logger.error('[Update] Failed to checkout:', error.message);
        throw new Error('Failed to switch to new version: ' + error.message);
    }
    
    // 更新 VERSION 文件（如果 tag 和 VERSION 文件不同步）
    const versionFilePath = path.join(process.cwd(), 'VERSION');
    try {
        const newVersion = latestTag.replace(/^v/, '');
        writeFileSync(versionFilePath, newVersion, 'utf-8');
        logger.info(`[Update] VERSION file updated to ${newVersion}`);
    } catch (error) {
        logger.warn('[Update] Failed to update VERSION file:', error.message);
    }
    
    // 检查是否需要安装依赖
    let needsRestart = false;
    try {
        // 确保本地版本号有 v 前缀，以匹配 git tag 格式
        const localVersionTag = updateInfo.localVersion.startsWith('v') ? updateInfo.localVersion : `v${updateInfo.localVersion}`;
        const { stdout: diffOutput } = await execAsync(`git diff ${localVersionTag}..${latestTag} --name-only`);
        if (diffOutput.includes('package.json') || diffOutput.includes('package-lock.json')) {
            logger.info('[Update] package.json changed, running npm install...');
            await execAsync('npm install');
            needsRestart = true;
        }
    } catch (error) {
        logger.warn('[Update] Failed to check package changes:', error.message);
    }
    
    logger.info(`[Update] Update completed successfully to ${latestTag}`);
    
    return {
        success: true,
        message: `Successfully updated to version ${latestTag}`,
        localVersion: updateInfo.localVersion,
        latestVersion: latestTag,
        updated: true,
        updateMethod: 'git',
        needsRestart: needsRestart,
        restartMessage: needsRestart ? 'Dependencies updated, recommend restarting service to apply changes' : null
    };
}

/**
 * 通过下载 tarball 执行更新（用于 Docker/非 Git 环境）
 * @param {string} localVersion - 本地版本
 * @param {string} latestTag - 最新版本 tag
 * @returns {Promise<Object>} 更新结果
 */
async function performTarballUpdate(localVersion, latestTag) {
    const GITHUB_REPO = 'justlovemaki/AIClient-2-API';
    const tarballUrl = `https://gh-proxy.org/https://github.com/${GITHUB_REPO}/archive/refs/tags/${latestTag}.tar.gz`;
    const appDir = process.cwd();
    const tempDir = path.join(appDir, '.update_temp');
    const tarballPath = path.join(tempDir, 'update.tar.gz');
    
    logger.info(`[Update] Starting tarball update to ${latestTag}...`);
    logger.info(`[Update] Download URL: ${tarballUrl}`);
    
    try {
        // 1. 创建临时目录
        await fs.mkdir(tempDir, { recursive: true });
        logger.info('[Update] Created temp directory');
        
        // 2. 下载 tarball
        logger.info('[Update] Downloading tarball...');
        const response = await fetchWithProxy(tarballUrl, {
            headers: {
                'User-Agent': 'AIClient2API-Updater'
            },
            redirect: 'follow'
        });
        
        if (!response.ok) {
            throw new Error(`Failed to download tarball: ${response.status} ${response.statusText}`);
        }
        
        const arrayBuffer = await response.arrayBuffer();
        const buffer = Buffer.from(arrayBuffer);
        await fs.writeFile(tarballPath, buffer);
        logger.info(`[Update] Downloaded tarball (${buffer.length} bytes)`);
        
        // 3. 解压 tarball
        logger.info('[Update] Extracting tarball...');
        await execAsync(`tar -xzf "${tarballPath}" -C "${tempDir}"`);
        
        // 4. 找到解压后的目录（格式通常是 repo-name-tag）
        const extractedItems = await fs.readdir(tempDir);
        const extractedDir = extractedItems.find(item =>
            item.startsWith('AIClient-2-API-') || item.startsWith('AIClient2API-')
        );
        
        if (!extractedDir) {
            throw new Error('Could not find extracted directory');
        }
        
        const sourcePath = path.join(tempDir, extractedDir);
        logger.info(`[Update] Extracted to: ${sourcePath}`);
        
        // 5. 备份当前的 package.json 用于比较
        const oldPackageJson = existsSync(path.join(appDir, 'package.json'))
            ? readFileSync(path.join(appDir, 'package.json'), 'utf-8')
            : null;
        
        // 5.5 在解压前删除 src/ 和 static/ 目录，确保旧代码被完全清除
        const dirsToClean = ['src', 'static'];
        for (const dirName of dirsToClean) {
            const dirPath = path.join(appDir, dirName);
            if (existsSync(dirPath)) {
                logger.info(`[Update] Removing old ${dirName}/ directory before extraction...`);
                await fs.rm(dirPath, { recursive: true, force: true });
                logger.info(`[Update] Old ${dirName}/ directory removed`);
            }
        }
        
        // 6. 定义需要保留的目录和文件（不被覆盖）
        const preservePaths = [
            'configs',           // 用户配置目录
            'node_modules',      // 依赖目录
            '.update_temp',      // 临时更新目录
            'logs'               // 日志目录
        ];
        
        // 7. 复制新文件到应用目录
        logger.info('[Update] Copying new files...');
        const sourceItems = await fs.readdir(sourcePath);
        
        for (const item of sourceItems) {
            // 跳过需要保留的目录
            if (preservePaths.includes(item)) {
                logger.info(`[Update] Skipping preserved path: ${item}`);
                continue;
            }
            
            const srcItemPath = path.join(sourcePath, item);
            const destItemPath = path.join(appDir, item);
            
            // 删除旧文件/目录（如果存在）
            if (existsSync(destItemPath)) {
                const stat = await fs.stat(destItemPath);
                if (stat.isDirectory()) {
                    await fs.rm(destItemPath, { recursive: true, force: true });
                } else {
                    await fs.unlink(destItemPath);
                }
            }
            
            // 复制新文件/目录
            await copyRecursive(srcItemPath, destItemPath);
            logger.info(`[Update] Copied: ${item}`);
        }
        
        // 8. 检查是否需要更新依赖
        let needsRestart = true; // tarball 更新后总是建议重启
        let needsNpmInstall = false;
        
        if (oldPackageJson) {
            const newPackageJson = readFileSync(path.join(appDir, 'package.json'), 'utf-8');
            if (oldPackageJson !== newPackageJson) {
                logger.info('[Update] package.json changed, running npm install...');
                needsNpmInstall = true;
                try {
                    await execAsync('npm install', { cwd: appDir });
                    logger.info('[Update] npm install completed');
                } catch (npmError) {
                    logger.error('[Update] npm install failed:', npmError.message);
                    // 不抛出错误，继续更新流程
                }
            }
        }
        
        // 9. 清理临时目录
        logger.info('[Update] Cleaning up...');
        await fs.rm(tempDir, { recursive: true, force: true });
        
        logger.info(`[Update] Tarball update completed successfully to ${latestTag}`);
        
        return {
            success: true,
            message: `Successfully updated to version ${latestTag}`,
            localVersion: localVersion,
            latestVersion: latestTag,
            updated: true,
            updateMethod: 'tarball',
            needsRestart: needsRestart,
            needsNpmInstall: needsNpmInstall,
            restartMessage: 'Code updated, please restart the service to apply changes'
        };
        
    } catch (error) {
        // 清理临时目录
        try {
            if (existsSync(tempDir)) {
                await fs.rm(tempDir, { recursive: true, force: true });
            }
        } catch (cleanupError) {
            logger.warn('[Update] Failed to cleanup temp directory:', cleanupError.message);
        }
        
        logger.error('[Update] Tarball update failed:', error.message);
        throw new Error(`Tarball update failed: ${error.message}`);
    }
}

/**
 * 递归复制文件或目录
 * @param {string} src - 源路径
 * @param {string} dest - 目标路径
 */
async function copyRecursive(src, dest) {
    const stat = await fs.stat(src);
    
    if (stat.isDirectory()) {
        await fs.mkdir(dest, { recursive: true });
        const items = await fs.readdir(src);
        for (const item of items) {
            await copyRecursive(path.join(src, item), path.join(dest, item));
        }
    } else {
        await fs.copyFile(src, dest);
    }
}

/**
 * 检查更新
 */
export async function handleCheckUpdate(req, res) {
    try {
        const updateInfo = await checkForUpdates();
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(updateInfo));
        return true;
    } catch (error) {
        logger.error('[UI API] Failed to check for updates:', error);
        res.writeHead(500, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({
            error: {
                message: 'Failed to check for updates: ' + error.message
            }
        }));
        return true;
    }
}

/**
 * 执行更新
 */
export async function handlePerformUpdate(req, res) {
    try {
        const updateResult = await performUpdate();
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(updateResult));
        return true;
    } catch (error) {
        logger.error('[UI API] Failed to perform update:', error);
        res.writeHead(500, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({
            error: {
                message: 'Update failed: ' + error.message
            }
        }));
        return true;
    }
}