// src/persona/runner.ts
import { setTimeout } from "timers";

// src/persona/trigger-detector.ts
import * as fs from "fs";
import * as path from "path";
function detectTrigger(projectPath) {
  const handoffPath = path.join(projectPath, ".harness", "handoff.json");
  if (!fs.existsSync(handoffPath)) {
    return { trigger: "manual" };
  }
  try {
    const raw = fs.readFileSync(handoffPath, "utf-8");
    const handoff = JSON.parse(raw);
    if (handoff.fromSkill === "harness-planning" && Array.isArray(handoff.pending) && handoff.pending.length > 0) {
      return {
        trigger: "on_plan_approved",
        handoff: {
          fromSkill: handoff.fromSkill,
          summary: handoff.summary ?? "",
          pending: handoff.pending,
          planPath: handoff.planPath
        }
      };
    }
    return { trigger: "manual" };
  } catch {
    return { trigger: "manual" };
  }
}

// src/persona/runner.ts
var TIMEOUT_ERROR_MESSAGE = "__PERSONA_RUNNER_TIMEOUT__";
function stepName(step) {
  return "command" in step ? step.command : step.skill;
}
function stepType(step) {
  return "command" in step ? "command" : "skill";
}
function matchesTrigger(step, trigger) {
  const when = step.when ?? "always";
  return when === "always" || when === trigger;
}
function skipRemaining(activeSteps, fromIndex, report) {
  for (let j = fromIndex; j < activeSteps.length; j++) {
    const remaining = activeSteps[j];
    report.steps.push({
      name: stepName(remaining),
      type: stepType(remaining),
      status: "skipped",
      durationMs: 0
    });
  }
}
async function executeCommandStep(command, remainingTime, context) {
  const timeoutResult = {
    ok: false,
    error: new Error(TIMEOUT_ERROR_MESSAGE)
  };
  const result = await Promise.race([
    context.commandExecutor(command),
    new Promise(
      (resolve) => setTimeout(() => resolve(timeoutResult), remainingTime)
    )
  ]);
  if (result.ok) {
    return { stepReport: { name: command, type: "command", status: "pass", result: result.value } };
  }
  if (result.error.message === TIMEOUT_ERROR_MESSAGE) {
    return {
      stepReport: { name: command, type: "command", status: "skipped", error: "timed out" },
      halt: "partial"
    };
  }
  return {
    stepReport: { name: command, type: "command", status: "fail", error: result.error.message },
    halt: "fail"
  };
}
async function executeSkillStep(step, trigger, remainingTime, context, handoff) {
  const SKILL_TIMEOUT_RESULT = {
    status: "fail",
    output: "timed out",
    durationMs: 0
  };
  const skillContext = {
    trigger,
    projectPath: context.projectPath,
    outputMode: step.output ?? "auto",
    ...handoff ? { handoff } : {}
  };
  const result = await Promise.race([
    context.skillExecutor(step.skill, skillContext),
    new Promise(
      (resolve) => setTimeout(() => resolve(SKILL_TIMEOUT_RESULT), remainingTime)
    )
  ]);
  if (result === SKILL_TIMEOUT_RESULT) {
    return {
      stepReport: { name: step.skill, type: "skill", status: "skipped", error: "timed out" },
      halt: "partial"
    };
  }
  if (result.status === "pass") {
    return {
      stepReport: {
        name: step.skill,
        type: "skill",
        status: "pass",
        result: result.output,
        ...result.artifactPath ? { artifactPath: result.artifactPath } : {}
      }
    };
  }
  return {
    stepReport: { name: step.skill, type: "skill", status: "fail", error: result.output },
    halt: "fail"
  };
}
async function runPersona(persona, context) {
  const startTime = Date.now();
  const timeout = persona.config.timeout;
  const report = {
    persona: persona.name.toLowerCase().replace(/\s+/g, "-"),
    status: "pass",
    steps: [],
    totalDurationMs: 0
  };
  let resolvedTrigger;
  let handoff = context.handoff;
  if (context.trigger === "auto") {
    const detection = detectTrigger(context.projectPath);
    resolvedTrigger = detection.trigger;
    handoff = detection.handoff ?? handoff;
  } else {
    resolvedTrigger = context.trigger;
  }
  const activeSteps = persona.steps.filter((s) => matchesTrigger(s, resolvedTrigger));
  for (let i = 0; i < activeSteps.length; i++) {
    const step = activeSteps[i];
    if (Date.now() - startTime >= timeout) {
      skipRemaining(activeSteps, i, report);
      report.status = "partial";
      break;
    }
    const stepStart = Date.now();
    const remainingTime = timeout - (Date.now() - startTime);
    const outcome = "command" in step ? await executeCommandStep(step.command, remainingTime, context) : await executeSkillStep(step, resolvedTrigger, remainingTime, context, handoff);
    const durationMs = Date.now() - stepStart;
    report.steps.push({ ...outcome.stepReport, durationMs });
    if (outcome.halt) {
      report.status = outcome.halt;
      skipRemaining(activeSteps, i + 1, report);
      break;
    }
  }
  report.totalDurationMs = Date.now() - startTime;
  return report;
}

export {
  detectTrigger,
  runPersona
};
