const vscode = require("vscode");
const fs = require("fs");
const path = require("path");

const MAX_PARALLEL = 6;
const TASK_TIMEOUT = 30 * 60 * 1000;
const ANSI_RE = /[\x1b\x9b][\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><~]/g;
const DONE_RE = /(?:Baked|Brewed|Churned|Cogitated|Cooked|Crunched|Saut[eé]+d|Worked)\s+for\s+\d+/;
const INTERACT_RE = /Allow|Deny|[Yy]\/[Nn]|yes\/no|允许|拒绝|Do you want/;

let activeSession = null;

function activate(context) {
  const root = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
  if (!root) return;
  const triggerFile = path.join(root, "_fix_trigger.json");
  const watcher = vscode.workspace.createFileSystemWatcher(
    new vscode.RelativePattern(root, "_fix_trigger.json")
  );
  const run = () => runFixBugs(triggerFile, root);
  watcher.onDidCreate(run);
  watcher.onDidChange(run);
  context.subscriptions.push(watcher);
  if (fs.existsSync(triggerFile)) run();
}

function markBugDone(root, bugLineNum) {
  const bugsFile = path.join(root, "bugs.md");
  const lockFile = bugsFile + ".lock";
  const maxRetries = 5;
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      // 简易文件锁：用 wx 标志创建锁文件，已存在则抛异常
      const lockFd = fs.openSync(lockFile, "wx");
      fs.closeSync(lockFd);
      try {
        const content = fs.readFileSync(bugsFile, "utf-8");
        const lines = content.split("\n");
        const idx = bugLineNum - 1;
        if (idx >= 0 && idx < lines.length && !lines[idx].includes("[已修复]")) {
          lines[idx] = lines[idx].trimEnd() + " [已修复]";
          fs.writeFileSync(bugsFile, lines.join("\n"), "utf-8");
        }
      } finally {
        try { fs.unlinkSync(lockFile); } catch {}
      }
      return;
    } catch (e) {
      if (e.code === "EEXIST" && attempt < maxRetries - 1) {
        // 锁被占用，等待后重试
        const wait = (attempt + 1) * 200;
        Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, wait);
        continue;
      }
      // 最后一次重试或非锁错误，无锁直接写
      try {
        const content = fs.readFileSync(bugsFile, "utf-8");
        const lines = content.split("\n");
        const idx = bugLineNum - 1;
        if (idx >= 0 && idx < lines.length && !lines[idx].includes("[已修复]")) {
          lines[idx] = lines[idx].trimEnd() + " [已修复]";
          fs.writeFileSync(bugsFile, lines.join("\n"), "utf-8");
        }
      } catch {}
      return;
    }
  }
}

function runFixBugs(triggerFile, root) {
  if (activeSession) {
    vscode.window.showWarningMessage("已有修复任务在运行中");
    try { fs.unlinkSync(triggerFile); } catch {}
    return;
  }

  let data;
  try { data = JSON.parse(fs.readFileSync(triggerFile, "utf-8")); } catch { return; }
  try { fs.unlinkSync(triggerFile); } catch {}
  const bugs = data.bugs || [];
  if (!bugs.length) return;

  const doneDir = path.join(root, "_fix_tmp");
  const scriptsDir = path.join(doneDir, "scripts");
  if (fs.existsSync(doneDir)) fs.rmSync(doneDir, { recursive: true, force: true });
  fs.mkdirSync(scriptsDir, { recursive: true });

  const groupQueues = new Map();
  let soloId = 0;
  for (const bug of bugs) {
    const key = bug.conflictGroup || `__solo_${soloId++}`;
    if (!groupQueues.has(key)) groupQueues.set(key, []);
    groupQueues.get(key).push(bug);
  }

  const running = new Map();
  const activeGroups = new Set();
  let finished = 0;
  const disposables = [];

  function donePath(lineNum) { return path.join(doneDir, `done_${lineNum}.txt`); }

  function writePrompt(lineNum, desc) {
    const f = path.join(scriptsDir, `prompt_${lineNum}.txt`);
    const prompt = `你是一个 bug 修复专家。请在当前项目中修复以下 bug：${desc}。修复流程：1. 修复 bug 代码 2. 编写针对该 bug 的测试用例 3. 运行测试用例 4. 如果测试失败，仔细阅读测试报错信息，根据报错信息分析原因并修复代码，然后重新运行测试，重复此过程直到测试通过 5. 测试全部通过后删除测试用例文件`;
    fs.writeFileSync(f, prompt, "utf-8");
    return f.replace(/\\/g, "/");
  }

  function launchBug(lineNum, desc, groupKey) {
    const promptFile = writePrompt(lineNum, desc);
    const logFile = path.join(scriptsDir, `log_${lineNum}.txt`).replace(/\\/g, "/");

    groupQueues.get(groupKey).shift();
    activeGroups.add(groupKey);

    const terminal = vscode.window.createTerminal({ name: `Bug L${lineNum}` });
    terminal.show(false);

    terminal.sendText(`Start-Transcript -Path "${logFile}" -Force`);
    terminal.sendText(`$p = Get-Content -Path "${promptFile}" -Raw -Encoding utf8`);
    terminal.sendText(`claude $p`);

    const info = {
      lineNum, desc, groupKey, logFile,
      notified: false, completed: false, exiting: false,
      logOffset: 0, logEncoding: null,
      startTime: Date.now(),
      monitorDisposables: [],
    };
    running.set(terminal, info);
    setupOutputMonitor(terminal, info);
  }

  function setupOutputMonitor(terminal, info) {
    const d = vscode.window.onDidStartTerminalShellExecution((e) => {
      if (e.terminal !== terminal) return;
      const cmd = e.execution.commandLine?.value || "";
      if (!cmd.includes("claude")) return;
      (async () => {
        try {
          for await (const chunk of e.execution.read()) {
            if (info.completed) return;
            const clean = chunk.replace(ANSI_RE, "");
            checkOutput(terminal, info, clean);
          }
        } catch {}
      })();
    });
    info.monitorDisposables.push(d);
  }

  function checkOutput(terminal, info, text) {
    if (info.completed || info.exiting) return;
    if (!info.notified && INTERACT_RE.test(text)) {
      info.notified = true;
      vscode.window.showWarningMessage(
        `Bug L${info.lineNum} 需要你的操作，请查看终端`, "查看终端"
      ).then(c => { if (c) terminal.show(); });
    }
    if (DONE_RE.test(text)) {
      info.exiting = true;
      terminal.sendText("/exit");
      setTimeout(() => handleComplete(terminal, info, true), 2000);
    }
  }

  function getReadyBugs() {
    const ready = [];
    for (const [key, q] of groupQueues) {
      if (q.length > 0 && !activeGroups.has(key)) ready.push({ ...q[0], groupKey: key });
    }
    return ready;
  }

  function launchNext() {
    for (const bug of getReadyBugs()) {
      if (running.size >= MAX_PARALLEL) break;
      launchBug(bug.lineNum, bug.desc, bug.groupKey);
    }
  }

  function allDone() {
    return running.size === 0 && [...groupQueues.values()].every(q => q.length === 0);
  }

  function cleanup() {
    activeSession = null;
    disposables.forEach(d => d.dispose());
    clearInterval(pollInterval);
  }

  function handleComplete(terminal, info, success) {
    if (info.completed) return;
    info.completed = true;
    info.monitorDisposables.forEach(d => d.dispose());
    running.delete(terminal);
    activeGroups.delete(info.groupKey);
    finished++;

    if (success) {
      markBugDone(root, info.lineNum);
      vscode.window.showInformationMessage(`Bug L${info.lineNum} 已修复 (${finished}/${bugs.length})`);
    } else {
      vscode.window.showWarningMessage(`Bug L${info.lineNum} 未完成 (${finished}/${bugs.length})`);
    }

    try {
      const dp = donePath(info.lineNum);
      if (!fs.existsSync(doneDir)) fs.mkdirSync(doneDir, { recursive: true });
      if (!fs.existsSync(dp)) fs.writeFileSync(dp, success ? "done" : "manual", "utf-8");
    } catch {}

    try { terminal.dispose(); } catch {}
    launchNext();
    if (allDone()) {
      vscode.window.showInformationMessage(`全部完成: ${bugs.length} 个 bug 已处理`);
      cleanup();
    }
  }

  // 终端关闭时处理
  disposables.push(vscode.window.onDidCloseTerminal((t) => {
    if (!running.has(t)) return;
    const info = running.get(t);
    handleComplete(t, info, info.exiting);
  }));

  // 轮询日志文件，检测完成/交互/超时
  const pollInterval = setInterval(() => {
    for (const [terminal, info] of running) {
      if (info.completed || info.exiting) continue;

      if (Date.now() - info.startTime > TASK_TIMEOUT) {
        vscode.window.showWarningMessage(
          `Bug L${info.lineNum} 已超时（30分钟），请手动检查`, "查看终端"
        ).then(c => { if (c) terminal.show(); });
        handleComplete(terminal, info, false);
        break;
      }

      let fd;
      try {
        if (!fs.existsSync(info.logFile)) continue;
        const stat = fs.statSync(info.logFile);
        if (stat.size <= info.logOffset) continue;

        fd = fs.openSync(info.logFile, "r");

        if (info.logEncoding === null && info.logOffset === 0 && stat.size >= 2) {
          const bom = Buffer.alloc(3);
          fs.readSync(fd, bom, 0, Math.min(3, stat.size), 0);
          if (bom[0] === 0xFF && bom[1] === 0xFE) {
            info.logEncoding = "utf16le";
            info.logOffset = 2;
          } else if (bom[0] === 0xEF && bom[1] === 0xBB && bom[2] === 0xBF) {
            info.logEncoding = "utf-8";
            info.logOffset = 3;
          } else {
            info.logEncoding = "utf-8";
          }
        }

        const readLen = stat.size - info.logOffset;
        if (readLen <= 0) continue;
        const buf = Buffer.alloc(readLen);
        fs.readSync(fd, buf, 0, readLen, info.logOffset);
        info.logOffset = stat.size;

        const newText = buf.toString(info.logEncoding || "utf-8").replace(ANSI_RE, "");
        checkOutput(terminal, info, newText);
      } catch {} finally {
        if (fd !== undefined) try { fs.closeSync(fd); } catch {}
      }
    }
    if (allDone()) clearInterval(pollInterval);
  }, 3000);

  activeSession = { cleanup };
  launchNext();
}

function deactivate() {
  if (activeSession) {
    activeSession.cleanup();
    activeSession = null;
  }
}

module.exports = { activate, deactivate };
