#!/usr/bin/env node

"use strict";

const os = require("os");
const path = require("path");
const fs = require("fs");
const https = require("https");
const http = require("http");
const { execSync } = require("child_process");
const PACKAGE = require("../package.json");

const VERSION = PACKAGE.version;
const BINARY_NAME = "gitlink-cli";
const RELEASE_BASE = "https://www.gitlink.org.cn";
const REPO_OWNER = "Gitlink";
const REPO_NAME = "gitlink-cli";

// 颜色输出
const colors = {
  reset: "\x1b[0m",
  green: "\x1b[32m",
  yellow: "\x1b[33m",
  red: "\x1b[31m",
  cyan: "\x1b[36m",
  blue: "\x1b[34m",
};

function info(msg) {
  console.log(`${colors.green}[INFO]${colors.reset}  ${msg}`);
}

function warn(msg) {
  console.log(`${colors.yellow}[WARN]${colors.reset}  ${msg}`);
}

function error(msg) {
  console.log(`${colors.red}[ERROR]${colors.reset} ${msg}`);
}

function step(msg) {
  console.log(`${colors.cyan}[STEP]${colors.reset} ${msg}`);
}

function debug(msg) {
  if (process.env.DEBUG === "true") {
    console.log(`${colors.blue}[DEBUG]${colors.reset} ${msg}`);
  }
}

function getPlatformInfo(platform = os.platform(), arch = os.arch()) {
  const platformMap = {
    darwin: "darwin",
    linux: "linux",
    win32: "windows",
  };

  const archMap = {
    x64: "amd64",
    arm64: "arm64",
  };

  const goPlatform = platformMap[platform];
  const goArch = archMap[arch];

  if (!goPlatform || !goArch) {
    throw new Error(
      `Unsupported platform: ${platform}-${arch}. ` +
        `Supported: darwin-x64, darwin-arm64, linux-x64, linux-arm64, win32-x64, win32-arm64`
    );
  }

  return { platform: goPlatform, arch: goArch, isWindows: platform === "win32" };
}

function getBinaryName(platform) {
  return platform === "windows" ? `${BINARY_NAME}.exe` : BINARY_NAME;
}

function getArchiveName(platform, arch) {
  const ext = platform === "windows" ? ".zip" : ".tar.gz";
  return `${BINARY_NAME}_${VERSION}_${platform}_${arch}${ext}`;
}

function fetch(url, options = {}) {
  return new Promise((resolve, reject) => {
    const maxRedirects = options.maxRedirects || 5;
    let redirectCount = 0;

    function doRequest(currentUrl) {
      const mod = currentUrl.startsWith("https") ? https : http;
      const req = mod.get(currentUrl, (res) => {
        // Follow redirects
        if (
          (res.statusCode === 301 ||
            res.statusCode === 302 ||
            res.statusCode === 307 ||
            res.statusCode === 308) &&
          res.headers.location
        ) {
          redirectCount++;
          if (redirectCount > maxRedirects) {
            reject(new Error(`Too many redirects (max ${maxRedirects})`));
            return;
          }
          let redirectUrl = res.headers.location;
          if (redirectUrl.startsWith("/")) {
            const parsed = new URL(currentUrl);
            redirectUrl = `${parsed.protocol}//${parsed.host}${redirectUrl}`;
          }
          doRequest(redirectUrl);
          return;
        }

        if (res.statusCode !== 200) {
          reject(new Error(`HTTP ${res.statusCode} when downloading ${currentUrl}`));
          return;
        }

        if (options.json) {
          let body = "";
          res.on("data", (chunk) => (body += chunk));
          res.on("end", () => {
            try {
              resolve(JSON.parse(body));
            } catch (e) {
              reject(e);
            }
          });
        } else {
          res.pipe(resolve);
        }
      });

      req.on("error", reject);
      req.setTimeout(options.timeout || 30000, () => {
        req.destroy();
        reject(new Error(`Request timeout: ${currentUrl}`));
      });
    }

    doRequest(url);
  });
}

// 下载文件（带重试）
async function downloadFile(url, outputPath, maxAttempts = 3) {
  let attempt = 1;

  while (attempt <= maxAttempts) {
    try {
      step(`下载 (尝试 ${attempt}/${maxAttempts}): ${path.basename(url)}`);
      debug(`URL: ${url}`);

      await new Promise((resolve, reject) => {
        const file = fs.createWriteStream(outputPath);
        const mod = url.startsWith("https") ? https : http;

        const req = mod.get(url, (res) => {
          if (res.statusCode !== 200) {
            reject(new Error(`HTTP ${res.statusCode}`));
            return;
          }

          const totalSize = parseInt(res.headers["content-length"], 10);
          let downloadedSize = 0;

          res.on("data", (chunk) => {
            downloadedSize += chunk.length;
            if (totalSize) {
              const progress = ((downloadedSize / totalSize) * 100).toFixed(1);
              process.stdout.write(`\r下载进度: ${progress}%`);
            }
          });

          res.pipe(file);

          file.on("finish", () => {
            file.close();
            process.stdout.write("\r");
            resolve();
          });

          file.on("error", (err) => {
            fs.unlink(outputPath, () => {});
            reject(err);
          });
        });

        req.on("error", (err) => {
          file.destroy();
          fs.unlink(outputPath, () => {});
          reject(err);
        });

        req.setTimeout(120000, () => {
          req.destroy();
          file.destroy();
          fs.unlink(outputPath, () => {});
          reject(new Error("下载超时"));
        });
      });

      if (fs.existsSync(outputPath) && fs.statSync(outputPath).size > 0) {
        const sizeMB = (fs.statSync(outputPath).size / (1024 * 1024)).toFixed(2);
        info(`下载成功: ${path.basename(outputPath)} (${sizeMB}MB)`);
        return true;
      } else {
        warn("下载文件为空");
      }
    } catch (err) {
      warn(`下载失败: ${err.message}`);
    }

    if (attempt < maxAttempts) {
      const waitTime = attempt * 2;
      warn(`等待 ${waitTime}s 后重试...`);
      await new Promise((resolve) => setTimeout(resolve, waitTime * 1000));
    }

    attempt++;
  }

  error("下载失败，已尝试 ${maxAttempts} 次");
  return false;
}

// 获取最新版本
async function getLatestVersion() {
  try {
    step("检查更新...");
    const releasesUrl = `${RELEASE_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases.json`;
    const releases = await fetch(releasesUrl, { json: true, timeout: 30000 });

    if (releases && releases.length > 0) {
      return releases[0].tag_name;
    }
  } catch (err) {
    debug(`获取版本失败: ${err.message}`);
  }

  return null;
}

// 检查是否有更新
async function checkForUpdate() {
  try {
    const latestVersion = await getLatestVersion();

    if (!latestVersion) {
      info("无法获取最新版本信息");
      return;
    }

    const currentVersion = VERSION.startsWith("v") ? VERSION : `v${VERSION}`;
    const latest = latestVersion.startsWith("v") ? latestVersion : `v${latestVersion}`;

    info(`当前版本: ${currentVersion}`);
    info(`最新版本: ${latest}`);

    if (currentVersion === latest) {
      info("已经是最新版本");
      return;
    }

    // 简单的版本比较
    if (latest > currentVersion) {
      warn(`发现新版本: ${latest}`);
      warn("运行 'npm update -g @gitlink-ai/cli' 更新");
    } else if (latest < currentVersion) {
      info("当前版本比最新发布版本更新（开发版本）");
    }
  } catch (err) {
    debug(`检查更新失败: ${err.message}`);
  }
}

// 安装二进制
async function installBinary() {
  const platform = getPlatformInfo();
  info(`平台: ${platform.platform}-${platform.arch}`);

  const binaryName = getBinaryName(platform.platform);
  const archiveName = getArchiveName(platform.platform, platform.arch);
  const npmBinDir = path.dirname(process.execPath);
  const installDir = path.join(npmBinDir, "..");

  step(`安装二进制到: ${installDir}`);

  const version = VERSION.startsWith("v") ? VERSION : `v${VERSION}`;
  const binaryUrl = `${RELEASE_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases/${version}/assets/${archiveName}`;

  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gitlink-cli-"));
  const archivePath = path.join(tmpDir, archiveName);

  try {
    // 下载
    const success = await downloadFile(binaryUrl, archivePath);
    if (!success) {
      throw new Error("下载失败");
    }

    // 解压
    step("解压...");
    if (platform.isWindows) {
      const AdmZip = require("adm-zip");
      const zip = new AdmZip(archivePath);
      zip.extractAllTo(installDir, true);
    } else {
      const tar = require("tar");
      await tar.x({
        file: archivePath,
        cwd: installDir,
        strip: 1,
      });
    }

    // 设置执行权限（Unix）
    if (!platform.isWindows) {
      const binaryPath = path.join(installDir, binaryName);
      if (fs.existsSync(binaryPath)) {
        fs.chmodSync(binaryPath, "755");
      }
    }

    info(`二进制安装成功: ${binaryName}`);
  } finally {
    // 清理临时文件
    fs.rmSync(tmpDir, { recursive: true, force: true });
  }
}

// 安装skills
async function installSkills() {
  const version = VERSION.startsWith("v") ? VERSION : `v${VERSION}`;
  const skillsArchive = `${BINARY_NAME}_${version}_skills.zip`;
  const skillsUrl = `${RELEASE_BASE}/api/${REPO_OWNER}/${REPO_NAME}/releases/${version}/assets/${skillsArchive}`;

  const skillsDir = path.join(os.homedir(), ".gitlink", "skills");

  if (!fs.existsSync(skillsDir)) {
    fs.mkdirSync(skillsDir, { recursive: true });
  }

  const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "gitlink-skills-"));
  const archivePath = path.join(tmpDir, skillsArchive);

  try {
    step("安装 skills...");

    const success = await downloadFile(skillsUrl, archivePath);
    if (!success) {
      warn("Skills 包下载失败（可稍后手动安装）");
      return;
    }

    // 解压
    const AdmZip = require("adm-zip");
    const zip = new AdmZip(archivePath);
    zip.extractAllTo(skillsDir, true);

    info("Skills 安装成功");
  } catch (err) {
    warn(`Skills 安装失败: ${err.message}`);
  } finally {
    fs.rmSync(tmpDir, { recursive: true, force: true });
  }
}

// 验证安装
function verifyInstallation() {
  step("验证安装...";

  try {
    const result = execSync("gitlink-cli version", { encoding: "utf8" });
    info(`安装成功! ${result.trim()}`);
    return true;
  } catch (err) {
    warn("验证命令失败（可能需要重启终端）");
    return false;
  }
}

// 主函数
async function main() {
  console.log("");
  console.log("  ╔══════════════════════════════════════╗");
  console.log("  ║     GitLink CLI npm 安装脚本        ║");
  console.log("  ╚══════════════════════════════════════╝");
  console.log("");

  try {
    await installBinary();
    await installSkills();
    verifyInstallation();

    console.log("");
    info("快速开始:");
    info("  gitlink-cli auth login   # 登录账号");
    info("  gitlink-cli --help        # 查看所有命令");
    info("  gitlink-cli version       # 查看版本信息");
    console.log("");
  } catch (err) {
    error(`安装失败: ${err.message}`);
    process.exit(1);
  }
}

// 如果直接运行此脚本
if (require.main === module || process.argv[1].endsWith("update.js")) {
  // 检查更新
  if (process.argv.includes("--check")) {
    (async () => {
      await checkForUpdate();
    })();
  } else {
    // 安装
    (async () => {
      await main();
    })();
  }
}

module.exports = {
  main,
  checkForUpdate,
  installBinary,
  installSkills,
};
