// HLDP-ZY://agents/zhuyuan-dev-agent/modules/executor.js
// @guardian: ICE-GL-ZY001 · 铸渊
// @sovereign: TCS-0002∞ · 冰朔
// @copyright: 国作登字-2026-A-00037559
// 铸渊开发Agent · 执行引擎 v2.1
// v2.1: substep target/content 为空时回退到父任务 + 参数验证

const fs = require("fs");
const path = require("path");
const { execSync } = require("child_process");

const WORKSPACE = process.env.WORKSPACE_DIR || "/opt/guanghu/zhuyuan-workspace";
const CONSTITUTION = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "agent-constitution.json"), "utf-8"));
const { fail_protocol } = CONSTITUTION;

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

function uniformResult(taskId, step, ok, extra = {}) {
  return { ok, task_id: taskId, step, action: extra.action || "unknown", output: extra.output || null, error: extra.error || null, retries: extra.retries || 0, skipped: !!extra.skipped, blocked: !!extra.blocked };
}

async function executeWithRetry(substep, logFn, retriesLeft) {
  const start = Date.now();
  const attempt = fail_protocol.max_retries_per_task - retriesLeft + 1;
  logFn("exec_attempt", { step: substep.step, action: substep.action, attempt, retriesLeft });
  try {
    let result;
    switch (substep.action) {
      case "write_file": result = doWriteFile(substep); break;
      case "run_cmd": result = doRunCmd(substep); break;
      case "call_api": result = await doCallApi(substep); break;
      case "check": result = doCheck(substep); break;
      default: return uniformResult(substep.task_id, substep.step, false, { action: substep.action, error: "未知动作类型: " + substep.action });
    }
    if (result.ok) {
      logFn("exec_done", { step: substep.step, result: "ok", ms: Date.now() - start, attempt });
      return uniformResult(substep.task_id, substep.step, true, { action: substep.action, output: result.output || result.path, retries: attempt - 1 });
    }
    if (retriesLeft > 1) {
      logFn("exec_retry", { step: substep.step, error: result.error, retriesLeft: retriesLeft - 1 });
      return executeWithRetry(substep, logFn, retriesLeft - 1);
    }
    logFn("exec_skip", { step: substep.step, error: result.error, reason: "重试" + fail_protocol.max_retries_per_task + "次全部失败，跳过" });
    return uniformResult(substep.task_id, substep.step, false, { action: substep.action, error: result.error, retries: fail_protocol.max_retries_per_task, skipped: true });
  } catch (err) {
    if (retriesLeft > 1) {
      logFn("exec_retry", { step: substep.step, error: err.message, retriesLeft: retriesLeft - 1 });
      return executeWithRetry(substep, logFn, retriesLeft - 1);
    }
    logFn("exec_skip", { step: substep.step, error: err.message, reason: "重试" + fail_protocol.max_retries_per_task + "次全部失败，跳过" });
    return uniformResult(substep.task_id, substep.step, false, { action: substep.action, error: err.message, retries: fail_protocol.max_retries_per_task, skipped: true });
  }
}

function doWriteFile(s) {
  const t = String(s.target || "").trim();
  if (!t || /^[\u4e00-\u9fff]+$/.test(t) || t === "无" || t === "未指定") return { ok: false, error: "无效文件路径: '" + t + "'" };
  const fp = path.resolve(WORKSPACE, t);
  const dir = path.dirname(fp);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
  fs.writeFileSync(fp, String(s.content || ""), "utf-8");
  return { ok: true, path: fp };
}

function doRunCmd(s) {
  const cmd = String(s.target || "").trim();
  if (!cmd || /^[\u4e00-\u9fff]+$/.test(cmd) || cmd === "无" || cmd === "未指定") return { ok: false, error: "无效Shell命令: '" + cmd + "'" };
  const out = execSync(cmd, { cwd: s.cwd || WORKSPACE, encoding: "utf-8", timeout: 60000, maxBuffer: 10 * 1024 * 1024 });
  return { ok: true, output: out.slice(0, 5000) };
}

async function doCallApi(s) {
  const https = require("https");
  const body = JSON.stringify(s.body || {});
  const url = new URL(s.target);
  return new Promise((resolve) => {
    const req = https.request({ hostname: url.hostname, path: url.pathname, method: s.method || "POST", headers: { "Content-Type": "application/json", ...(s.headers || {}) } }, (res) => {
      let data = ""; res.on("data", c => data += c);
      res.on("end", () => resolve({ ok: res.statusCode < 400, status: res.statusCode, body: data.slice(0, 2000) }));
    });
    req.on("error", e => resolve({ ok: false, error: e.message }));
    if (body !== "{}") req.write(body);
    req.end();
  });
}

function doCheck(s) { const fp = path.resolve(WORKSPACE, s.target); return { ok: fs.existsSync(fp), exists: fs.existsSync(fp), path: fp }; }

async function executePlan(plan, logFn) {
  console.log("[Executor v2.1] 开始执行: " + plan.plan_id + " · 宪法约束模式");
  logFn("constitution_loaded", { max_retries: fail_protocol.max_retries_per_task });
  const results = [];
  let doneCount = 0, failCount = 0, skipCount = 0, consecutiveFails = 0;
  const blockedTasks = new Set();
  for (const task of plan.tasks || []) {
    const substeps = task.substeps || [];
    for (const ss of substeps) {
      if ((!ss.target || ss.target === "待指定文件路径" || ss.target === "待指定" || ss.target === "无" || ss.target === "未指定") && task.target) {
        console.warn("[Executor v2.1] substep#" + ss.step + " target模糊, 从父任务继承=" + task.target);
        ss.target = task.target;
      }
      if ((!ss.content || ss.content === "未指定") && task.content) ss.content = task.content;
      if ((!ss.cwd) && task.cwd) ss.cwd = task.cwd;
      if (ss.depends_on && blockedTasks.has(ss.depends_on)) {
        results.push(uniformResult(task.id, ss.step, false, { action: ss.action, blocked: true, error: "上游依赖失败" }));
        failCount++; continue;
      }
      const r = await executeWithRetry({ ...ss, task_id: task.id }, logFn, fail_protocol.max_retries_per_task);
      r._substep_detail = ss.detail || "";
      r._task_target = task.target || "";
      results.push(r);
      if (r.ok) { doneCount++; consecutiveFails = 0; }
      else if (r.skipped) { skipCount++; consecutiveFails++; blockedTasks.add(task.id + ":" + ss.step); }
      else { failCount++; consecutiveFails++; blockedTasks.add(task.id + ":" + ss.step); }
      if (consecutiveFails >= 5) {
        logFn("emergency_stop", { reason: "连续" + consecutiveFails + "个任务失败" });
        return { plan_id: plan.plan_id, emergency_stop: true, done: doneCount, failed: failCount, skipped: skipCount, total: results.length, results };
      }
    }
  }
  const summary = { plan_id: plan.plan_id, total: results.length, done: doneCount, failed: failCount, skipped: skipCount, results };
  console.log("[Executor v2.1] 完成 · 成功 " + doneCount + " / 失败 " + failCount + " / 跳过 " + skipCount);
  return summary;
}

module.exports = { executePlan };
