/**
 * Agent Runtime Layer — State Machine
 *
 * PR-35 Part B：统一状态机，管理 Task / Step 生命周期。
 *
 * 状态流转：
 *
 *   created → planned → queued → running → checking
 *                                                 ├── passed  → archived
 *                                                 ├── failed  → queued (retry)
 *                                                 ├── blocked → queued (unblock)
 *                                                 └── cancelled → archived
 *
 * 特性：
 *   - 合法迁移验证
 *   - 状态审计日志
 *   - 自动时间戳
 *   - 恢复策略
 *
 * 纯函数，无副作用。
 *
 * @module agent-runtime/state-machine
 */

import { TASK_STATE, STEP_STATE } from "./domain.mjs";

// ═══════════════════════════════════════════════════════
// 合法迁移表
// ═══════════════════════════════════════════════════════

export const TASK_TRANSITIONS = Object.freeze({
  [TASK_STATE.CREATED]:   [TASK_STATE.PLANNED, TASK_STATE.CANCELLED],
  [TASK_STATE.PLANNED]:   [TASK_STATE.QUEUED, TASK_STATE.CANCELLED],
  [TASK_STATE.QUEUED]:    [TASK_STATE.RUNNING, TASK_STATE.CANCELLED],
  [TASK_STATE.RUNNING]:   [TASK_STATE.CHECKING, TASK_STATE.FAILED, TASK_STATE.BLOCKED, TASK_STATE.CANCELLED],
  [TASK_STATE.CHECKING]:  [TASK_STATE.PASSED, TASK_STATE.FAILED, TASK_STATE.BLOCKED],
  [TASK_STATE.PASSED]:    [TASK_STATE.ARCHIVED],
  [TASK_STATE.FAILED]:    [TASK_STATE.QUEUED, TASK_STATE.CANCELLED],  // retry
  [TASK_STATE.BLOCKED]:   [TASK_STATE.QUEUED, TASK_STATE.CANCELLED],  // unblock
  [TASK_STATE.CANCELLED]: [TASK_STATE.ARCHIVED],
  [TASK_STATE.ARCHIVED]:  [],
});

export const STEP_TRANSITIONS = Object.freeze({
  [STEP_STATE.PENDING]:   [STEP_STATE.QUEUED, STEP_STATE.SKIPPED, STEP_STATE.BLOCKED],
  [STEP_STATE.QUEUED]:    [STEP_STATE.RUNNING, STEP_STATE.SKIPPED],
  [STEP_STATE.RUNNING]:   [STEP_STATE.COMPLETED, STEP_STATE.FAILED, STEP_STATE.BLOCKED],
  [STEP_STATE.COMPLETED]: [],
  [STEP_STATE.FAILED]:    [STEP_STATE.QUEUED, STEP_STATE.SKIPPED],   // retry
  [STEP_STATE.SKIPPED]:   [],
  [STEP_STATE.BLOCKED]:   [STEP_STATE.QUEUED, STEP_STATE.SKIPPED],   // unblock
});

// ═══════════════════════════════════════════════════════
// 迁移验证
// ═══════════════════════════════════════════════════════

/**
 * @param {string} from
 * @param {string} to
 * @param {"task"|"step"} kind
 * @returns {boolean}
 */
export function canTransition(from, to, kind = "task") {
  const table = kind === "step" ? STEP_TRANSITIONS : TASK_TRANSITIONS;
  const allowed = table[from];
  return !!allowed && allowed.includes(to);
}

/**
 * @param {object} entity  — Task 或 Step
 * @param {string} to
 * @param {"task"|"step"} kind
 * @returns {object} entity（已修改）
 * @throws {Error} 非法迁移
 */
export function transition(entity, to, kind = "task") {
  const from = entity.state;
  if (!canTransition(from, to, kind)) {
    const table = kind === "step" ? STEP_TRANSITIONS : TASK_TRANSITIONS;
    throw new Error(
      `Illegal ${kind} state transition: ${from} → ${to}. ` +
      `Allowed: [${(table[from] || []).join(", ")}]`
    );
  }

  const prev = entity.state;
  entity.state = to;

  // 审计日志
  if (Array.isArray(entity.stateHistory)) {
    entity.stateHistory.push({ state: to, from: prev, at: new Date().toISOString() });
  }
  if (Array.isArray(entity.transitionLog)) {
    entity.transitionLog.push({ from: prev, to, at: new Date().toISOString() });
  }

  // 自动时间戳
  const now = new Date().toISOString();
  if (to === TASK_STATE.PLANNED && !entity.plannedAt) entity.plannedAt = now;
  if (to === TASK_STATE.RUNNING && !entity.startedAt && kind === "task") entity.startedAt = now;
  if (to === TASK_STATE.CHECKING && !entity.checkedAt) entity.checkedAt = now;
  if ((to === TASK_STATE.PASSED || to === TASK_STATE.FAILED || to === TASK_STATE.CANCELLED) && kind === "task") {
    if (!entity.completedAt) entity.completedAt = now;
  }
  if (to === TASK_STATE.ARCHIVED && !entity.archivedAt) entity.archivedAt = now;

  return entity;
}

// ═══════════════════════════════════════════════════════
// 步骤依赖检查
// ═══════════════════════════════════════════════════════

/**
 * @param {object} step
 * @param {object[]} allSteps
 * @returns {boolean}
 */
export function areStepDependenciesMet(step, allSteps) {
  if (!step.dependsOn || step.dependsOn.length === 0) return true;
  return step.dependsOn.every(depId => {
    const dep = allSteps.find(s => s.id === depId);
    return dep && (dep.state === STEP_STATE.COMPLETED || dep.state === STEP_STATE.SKIPPED);
  });
}

/**
 * @param {object[]} steps
 * @returns {object[]}
 */
export function getReadySteps(steps) {
  return steps.filter(s =>
    s.state === STEP_STATE.PENDING &&
    areStepDependenciesMet(s, steps)
  );
}

// ═══════════════════════════════════════════════════════
// 任务依赖检查
// ═══════════════════════════════════════════════════════

/**
 * @param {object} task
 * @param {Map<string,object>} taskMap
 * @returns {boolean}
 */
export function areTaskDependenciesMet(task, taskMap) {
  if (!task.dependsOn || task.dependsOn.length === 0) return true;
  return task.dependsOn.every(depId => {
    const dep = taskMap.get(depId);
    return dep && dep.state === TASK_STATE.PASSED;
  });
}

// ═══════════════════════════════════════════════════════
// 可取消状态
// ═══════════════════════════════════════════════════════

export const CANCELLABLE_STATES = Object.freeze([
  TASK_STATE.CREATED,
  TASK_STATE.PLANNED,
  TASK_STATE.QUEUED,
  TASK_STATE.RUNNING,
  TASK_STATE.FAILED,
  TASK_STATE.BLOCKED,
]);

/**
 * @param {string} state
 * @returns {boolean}
 */
export function isCancellable(state) {
  return CANCELLABLE_STATES.includes(state);
}

// ═══════════════════════════════════════════════════════
// 恢复策略
// ═══════════════════════════════════════════════════════

/**
 * 为失败任务计算恢复策略。
 * @param {object} task
 * @returns {{ canRetry: boolean, canRecover: boolean, nextState: string|null, reason: string }}
 */
export function computeRecoveryStrategy(task) {
  if (task.state !== TASK_STATE.FAILED) {
    return { canRetry: false, canRecover: false, nextState: null, reason: "Task is not in FAILED state." };
  }

  const maxRetries = task.maxRetries ?? 3;
  const retryCount = task.retryCount ?? 0;

  if (retryCount < maxRetries) {
    return {
      canRetry: true,
      canRecover: true,
      nextState: TASK_STATE.QUEUED,
      reason: `Retry ${retryCount + 1}/${maxRetries} available.`,
    };
  }

  return {
    canRetry: false,
    canRecover: false,
    nextState: null,
    reason: `Max retries (${maxRetries}) exhausted. Manual intervention required.`,
  };
}

/**
 * 执行重试。
 * @param {object} task
 * @returns {object} task（已修改）
 * @throws {Error} 不可重试
 */
export function retryTask(task) {
  const strategy = computeRecoveryStrategy(task);
  if (!strategy.canRetry) {
    throw new Error(`Cannot retry task ${task.id}: ${strategy.reason}`);
  }
  task.retryCount = (task.retryCount ?? 0) + 1;
  transition(task, TASK_STATE.QUEUED);
  return task;
}

// ═══════════════════════════════════════════════════════
// 步骤摘要
// ═══════════════════════════════════════════════════════

/**
 * @param {object[]} steps
 * @returns {{ total:number, completed:number, failed:number, skipped:number, pending:number, running:number, blocked:number }}
 */
export function getStepsSummary(steps) {
  const s = { total: 0, completed: 0, failed: 0, skipped: 0, pending: 0, running: 0, blocked: 0 };
  for (const step of steps) {
    s.total++;
    switch (step.state) {
      case STEP_STATE.COMPLETED: s.completed++; break;
      case STEP_STATE.FAILED:    s.failed++;    break;
      case STEP_STATE.SKIPPED:   s.skipped++;   break;
      case STEP_STATE.PENDING:   s.pending++;   break;
      case STEP_STATE.RUNNING:   s.running++;   break;
      case STEP_STATE.BLOCKED:   s.blocked++;   break;
    }
  }
  return s;
}

/**
 * @param {object[]} steps
 * @returns {boolean}
 */
export function isAllStepsDone(steps) {
  if (!steps || steps.length === 0) return false;
  return steps.every(s =>
    s.state === STEP_STATE.COMPLETED ||
    s.state === STEP_STATE.SKIPPED ||
    s.state === STEP_STATE.FAILED
  );
}

/**
 * @param {object[]} steps
 * @returns {boolean}
 */
export function hasFailedSteps(steps) {
  return steps.some(s => s.state === STEP_STATE.FAILED);
}

// ═══════════════════════════════════════════════════════
// 终端状态
// ═══════════════════════════════════════════════════════

export const TERMINAL_STATES = Object.freeze([
  TASK_STATE.PASSED,
  TASK_STATE.CANCELLED,
  TASK_STATE.ARCHIVED,
]);

/**
 * @param {string} state
 * @returns {boolean}
 */
export function isTerminal(state) {
  return TERMINAL_STATES.includes(state);
}
