/**
 * PM decision layer — outputs next action only; no FCoP writes, no dispatch side effects.
 */

import { filterThreadTasks, type ExecutionGateContext } from "./taskDispatchContext.ts";
import {
  artifactPathExists,
  isTaskCancelled,
  isTaskCompleted,
  isTaskSuperseded,
  normalizeTaskIdPrefix,
  resolveArtifactPathForThread,
  resolveExecutionState,
  type ExecutionState,
} from "./executionState.ts";
import {
  isDoneReportStatus,
  normalizeWorkerRole,
  reportsForTask,
  type DispatchGateTaskRef,
} from "./taskDispatchGate.ts";

export type PmEvaluateAction =
  | "WAKE_DEV"
  | "WAIT"
  | "RETRY_QA"
  | "REVIEW_REPORT"
  | "RESOLVE_BLOCKED"
  | "ESCALATE_ADMIN"
  | "OK";

export interface PmEvaluateResult {
  action: PmEvaluateAction;
  reason: string;
  execution_state?: ExecutionState;
}

export function pmEvaluate(
  target: DispatchGateTaskRef,
  ctx: ExecutionGateContext,
  threadTasks?: DispatchGateTaskRef[],
): PmEvaluateResult {
  const scoped =
    threadTasks ?? filterThreadTasks(ctx.tasks, target.threadKey);
  const execution_state = resolveExecutionState(target, ctx, scoped);
  const meta =
    ctx.taskMeta.get(normalizeTaskIdPrefix(target.taskId)) ??
    ctx.taskMeta.get(target.taskId);
  const role = normalizeWorkerRole(target.recipient);
  const displayStatus = String(target.displayStatus ?? "").toLowerCase();

  if (execution_state === "superseded" || isTaskSuperseded(target, ctx)) {
    return { action: "WAIT", reason: "task superseded", execution_state };
  }

  // Lifecycle terminal state is authoritative. Never let an old queue entry
  // or an earlier rejected QA round turn a done task back into RETRY_QA.
  if (execution_state === "completed" || isTaskCompleted(target)) {
    return { action: "OK", reason: "task already completed", execution_state: "completed" };
  }

  if (isTaskCancelled(target, meta)) {
    return { action: "WAIT", reason: "task cancelled", execution_state: "blocked" };
  }

  if (displayStatus === "worker_report_blocked") {
    return {
      action: "RESOLVE_BLOCKED",
      reason: "formal worker blocked report requires PM acknowledgement or rework",
      execution_state,
    };
  }

  const isWorker = role === "DEV" || role === "OPS" || role === "QA";
  if (isWorker) {
    const taskReports = reportsForTask(target, ctx.reports);
    const doneReport = taskReports.find((report) =>
      isDoneReportStatus(report.status),
    );
    if (doneReport) {
      return {
        action: "REVIEW_REPORT",
        reason: `${role} REPORT_READY_FOR_PM_REVIEW`,
        execution_state,
      };
    }

    const blockedReport = taskReports.find(
      (report) => String(report.status ?? "").trim().toLowerCase() === "blocked",
    );
    if (blockedReport) {
      return {
        action: "RESOLVE_BLOCKED",
        reason: `${role} BLOCKED_REPORT_RECEIVED`,
        execution_state,
      };
    }

    if (taskReports.length > 0) {
      return {
        action: "WAIT",
        reason: `${role} REPORT_NOT_TERMINAL`,
        execution_state,
      };
    }
  }

  if (displayStatus.includes("qa_fail") || displayStatus.includes("admin_reject")) {
    return {
      action: "WAIT",
      reason: `PM_DECISION_REQUIRED display_status=${displayStatus}`,
      execution_state,
    };
  }

  if (role === "DEV") {
    if (
      execution_state === "runnable" &&
      (target.lifecycleBucket === "inbox" || target.fmState === "inbox")
    ) {
      return { action: "WAKE_DEV", reason: "DEV inbox runnable", execution_state };
    }
    return { action: "WAIT", reason: "DEV REPORT_PENDING", execution_state };
  }

  if (role === "QA") {
    if (execution_state === "waiting_dependency") {
      return {
        action: "WAIT",
        reason: "DEPENDENCY_REPORT_PENDING",
        execution_state,
      };
    }

    const artifact = resolveArtifactPathForThread(
      target.threadKey,
      scoped,
      ctx,
    );
    if (artifact && !artifactPathExists(ctx, artifact)) {
      return {
        action: "WAIT",
        reason: "ARTIFACT_MISSING_PM_DECISION_REQUIRED",
        execution_state: "blocked",
      };
    }

    if (
      target.lifecycleBucket === "active" ||
      target.fmState === "active" ||
      target.lifecycleBucket === "review"
    ) {
      return { action: "WAIT", reason: "QA REPORT_PENDING", execution_state };
    }

    if (execution_state === "runnable") {
      return {
        action: "RETRY_QA",
        reason: "QA ready to dispatch",
        execution_state,
      };
    }

    return { action: "RETRY_QA", reason: "QA blocked", execution_state };
  }

  if (role === "OPS") {
    return { action: "WAIT", reason: "OPS REPORT_PENDING", execution_state };
  }

  return { action: "WAIT", reason: "no PM action for role", execution_state };
}
