#!/usr/bin/env node
// 检测 netabrowser-cli chromium 是否就位；缺失则下载 + 解压到 packages/netabrowser-cli/chromium/win64/
// 覆盖变量：
//   NETA_CHROMIUM_PATH  已就位的 chrome.exe 绝对路径（优先级最高，存在即直接复用）
//   NETA_CHROMIUM_URL   自定义下载 zip 地址
'use strict';

const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const https = require('node:https');
const http = require('node:http');
const { URL } = require('node:url');
const { spawnSync } = require('node:child_process');

const DEFAULT_URL =
  'https://ai-flow.bj.bcebos.com/browers-cli/ungoogled-chromium_144.0.7559.132-1.1_windows_x64.zip?responseContentDisposition=attachment';

function log(msg) {
  process.stderr.write(`[ensure-chromium] ${msg}\n`);
}

function fail(msg, code = 1) {
  process.stderr.write(`[ensure-chromium] ERROR: ${msg}\n`);
  process.exit(code);
}

function resolveTargetDir() {
  if (process.env.NETA_CHROMIUM_PATH) {
    // 用户指定了 chrome.exe 路径，目标目录 = 其上级
    return path.dirname(process.env.NETA_CHROMIUM_PATH);
  }
  // dev 模式：scripts/ → skill dir → skills/ → backend/ → packages/ → packages/netabrowser-cli/chromium/win64
  return path.resolve(__dirname, '../../../../netabrowser-cli/chromium/win64');
}

function chromeExeIn(dir) {
  return path.join(dir, 'chrome.exe');
}

function alreadyInstalled(dir) {
  try {
    const stat = fs.statSync(chromeExeIn(dir));
    return stat.isFile() && stat.size > 1024 * 1024; // > 1MB 才算有效
  } catch {
    return false;
  }
}

function httpGet(urlStr, redirectsLeft = 5) {
  return new Promise((resolve, reject) => {
    const url = new URL(urlStr);
    const mod = url.protocol === 'http:' ? http : https;
    const req = mod.get(
      urlStr,
      {
        headers: {
          'User-Agent': 'netabrowser-cli/ensure-chromium',
          Accept: '*/*',
        },
      },
      (res) => {
        const status = res.statusCode || 0;
        if (status >= 300 && status < 400 && res.headers.location) {
          res.resume();
          if (redirectsLeft <= 0) return reject(new Error('redirect loop'));
          const next = new URL(res.headers.location, urlStr).toString();
          log(`redirect → ${next}`);
          resolve(httpGet(next, redirectsLeft - 1));
          return;
        }
        if (status < 200 || status >= 300) {
          res.resume();
          return reject(new Error(`HTTP ${status} from ${urlStr}`));
        }
        resolve(res);
      }
    );
    req.on('error', reject);
    req.setTimeout(60_000, () => {
      req.destroy(new Error('connect timeout'));
    });
  });
}

function humanBytes(n) {
  if (!Number.isFinite(n)) return '?';
  if (n > 1024 ** 3) return (n / 1024 ** 3).toFixed(2) + ' GiB';
  if (n > 1024 ** 2) return (n / 1024 ** 2).toFixed(2) + ' MiB';
  if (n > 1024) return (n / 1024).toFixed(2) + ' KiB';
  return n + ' B';
}

async function download(url, destFile) {
  log(`downloading ${url}`);
  log(`→ ${destFile}`);
  const res = await httpGet(url);
  const total = parseInt(String(res.headers['content-length'] || '0'), 10);
  const out = fs.createWriteStream(destFile);
  let received = 0;
  let lastReport = Date.now();
  await new Promise((resolve, reject) => {
    res.on('data', (chunk) => {
      received += chunk.length;
      const now = Date.now();
      if (now - lastReport > 2000) {
        lastReport = now;
        const pct = total ? ((received / total) * 100).toFixed(1) + '%' : '';
        log(`  ${humanBytes(received)}${total ? ' / ' + humanBytes(total) : ''} ${pct}`);
      }
    });
    res.on('error', reject);
    out.on('error', reject);
    out.on('finish', resolve);
    res.pipe(out);
  });
  log(`downloaded ${humanBytes(received)}`);
}

function extractZip(zipFile, destDir) {
  fs.mkdirSync(destDir, { recursive: true });

  if (process.platform === 'win32') {
    // Windows：直接用 PowerShell Expand-Archive，避开 Git Bash 里 tar 把 C: 当主机的老问题
    log(`extracting (PowerShell Expand-Archive) → ${destDir}`);
    const ps = [
      '-NoProfile',
      '-ExecutionPolicy',
      'Bypass',
      '-Command',
      `Expand-Archive -Path '${zipFile.replace(/'/g, "''")}' -DestinationPath '${destDir.replace(
        /'/g,
        "''"
      )}' -Force`,
    ];
    const r = spawnSync('powershell.exe', ps, {
      stdio: ['ignore', 'inherit', 'inherit'],
    });
    if (r.status === 0) return;
    fail(`extract failed: powershell exit=${r.status}`);
    return;
  }

  // 非 Windows：优先 unzip，缺失再回落 tar（libarchive 支持 zip）
  log(`extracting (unzip) → ${destDir}`);
  const r1 = spawnSync('unzip', ['-q', '-o', zipFile, '-d', destDir], {
    stdio: ['ignore', 'inherit', 'inherit'],
  });
  if (r1.status === 0) return;

  log(`unzip failed (status=${r1.status}); fallback tar`);
  const r2 = spawnSync('tar', ['-xf', zipFile, '-C', destDir], {
    stdio: ['ignore', 'inherit', 'inherit'],
  });
  if (r2.status === 0) return;
  fail(`extract failed: unzip exit=${r1.status}, tar exit=${r2.status}`);
}

function findChromeDir(root) {
  // 压缩包里 chrome.exe 可能在 root/ 或 root/<子目录>/
  const direct = chromeExeIn(root);
  if (fs.existsSync(direct)) return root;

  const entries = fs.readdirSync(root, { withFileTypes: true });
  for (const ent of entries) {
    if (!ent.isDirectory()) continue;
    const sub = path.join(root, ent.name);
    if (fs.existsSync(chromeExeIn(sub))) return sub;
  }
  return null;
}

function moveContents(srcDir, targetDir) {
  fs.mkdirSync(targetDir, { recursive: true });
  for (const name of fs.readdirSync(srcDir)) {
    const from = path.join(srcDir, name);
    const to = path.join(targetDir, name);
    try {
      fs.rmSync(to, { recursive: true, force: true });
    } catch {}
    fs.renameSync(from, to);
  }
}

function rmrf(p) {
  try {
    fs.rmSync(p, { recursive: true, force: true });
  } catch {}
}

async function main() {
  const targetDir = resolveTargetDir();

  if (alreadyInstalled(targetDir)) {
    log(`already installed: ${chromeExeIn(targetDir)}`);
    return;
  }

  if (process.env.NETA_CHROMIUM_PATH) {
    // 用户显式指定路径但文件缺失 — 不自动下载到别处，直接报错
    fail(
      `NETA_CHROMIUM_PATH=${process.env.NETA_CHROMIUM_PATH} but file missing. ` +
        `Unset it to trigger auto-download to the default location.`
    );
  }

  const url = process.env.NETA_CHROMIUM_URL || DEFAULT_URL;
  const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'neta-chromium-'));
  const zipFile = path.join(tmpRoot, 'chromium.zip');
  const extractDir = path.join(tmpRoot, 'extract');

  try {
    await download(url, zipFile);
    extractZip(zipFile, extractDir);

    const chromeDir = findChromeDir(extractDir);
    if (!chromeDir) {
      fail(`chrome.exe not found in extracted archive: ${extractDir}`);
    }

    log(`moving ${chromeDir} → ${targetDir}`);
    moveContents(chromeDir, targetDir);

    if (!alreadyInstalled(targetDir)) {
      fail(`post-install check failed: ${chromeExeIn(targetDir)} missing or too small`);
    }
    log(`installed ok → ${chromeExeIn(targetDir)}`);
  } finally {
    rmrf(tmpRoot);
  }
}

main().catch((err) => {
  fail(err && err.stack ? err.stack : String(err));
});
