import type { GateEntry } from '@mosaicstack/macp';

import type {
  AuthorityGate,
  ForgeGate,
  ForgeGateResult,
  ForgeOutcome,
  ForgeTaskResult,
  ProviderGate,
} from './types.js';

/**
 * Gate and dependency satisfaction predicate (SDLC-D-035).
 *
 * ONLY a verified `passed` outcome satisfies. Every other member of the closed
 * outcome set — including `simulated` — is non-satisfying, so a simulated or
 * authority-blocked result can never be read as success-by-verification.
 */
export function isSatisfyingOutcome(outcome: ForgeOutcome): boolean {
  return outcome === 'passed';
}

/** Whether a gate is an authority or provider gate (capability-based, command-less). */
export function isCapabilityGate(gate: ForgeGate): gate is AuthorityGate | ProviderGate {
  if (typeof gate !== 'object' || gate === null) return false;
  const kind = (gate as Record<string, unknown>)['kind'];
  return kind === 'authority' || kind === 'provider';
}

/** Whether a gate definition carries a real command a mechanical runner can execute. */
export function isCommandGate(gate: ForgeGate): gate is string | GateEntry {
  if (typeof gate === 'string') {
    return gate.trim().length > 0;
  }
  if (isCapabilityGate(gate)) {
    // Authority and provider gates are satisfied by a capability, not a command.
    return false;
  }
  return typeof gate.command === 'string' && gate.command.trim().length > 0;
}

/** Typed label identifying a gate in results and logs. */
export function gateLabel(gate: ForgeGate): string {
  if (typeof gate === 'string') return gate;
  if (isCapabilityGate(gate)) return `${gate.kind}:${gate.capability}`;
  return gate.command || gate.type || 'unnamed-gate';
}

/** Reason string stamped on every simulated gate result. */
export const SIMULATED_GATE_REASON =
  'simulated execution (--simulate): gate was not evaluated by a real implementation';

/** Build typed gate results with a uniform outcome for a stage's declared gates. */
export function uniformGateResults(
  gates: ForgeGate[],
  outcome: ForgeOutcome,
  reason: string,
): ForgeGateResult[] {
  return gates.map((gate) => ({ gate: gateLabel(gate), outcome, reason }));
}

/** Typed simulated gate results — used exclusively in `--simulate` runs. */
export function simulatedGateResults(gates: ForgeGate[]): ForgeGateResult[] {
  return uniformGateResults(gates, 'simulated', SIMULATED_GATE_REASON);
}

/** Typed waiting-for-authority gate results for approval-based stages. */
export function waitingGateResults(gates: ForgeGate[], reason: string): ForgeGateResult[] {
  return uniformGateResults(gates, 'waiting-for-authority', reason);
}

/** Typed blocked gate results for stages whose provider capability is not wired. */
export function blockedGateResults(gates: ForgeGate[], reason: string): ForgeGateResult[] {
  return uniformGateResults(gates, 'blocked', reason);
}

/** Outcome of evaluating a completed stage in normal mode. */
export interface StageEvaluation {
  outcome: ForgeOutcome;
  reason: string;
  gateResults: ForgeGateResult[];
}

/**
 * Evaluate a stage's declared gates against the executor's typed result.
 *
 * Fail-closed mapping:
 * - a `simulated` task or gate outcome in normal mode maps to `error`
 * - a missing gate result for a required command gate maps to `blocked`
 * - a non-passing task outcome propagates as the stage outcome
 * - only verified `passed` task and gate outcomes yield a `passed` stage
 */
export function evaluateStageGates(
  stageName: string,
  gates: ForgeGate[],
  result: ForgeTaskResult,
): StageEvaluation {
  const gateResults = result.gate_results ?? [];

  if (result.outcome === 'simulated') {
    return {
      outcome: 'error',
      reason: `executor reported a simulated outcome for stage '${stageName}' in normal mode — refusing to treat simulated results as verified`,
      gateResults,
    };
  }

  if (!isSatisfyingOutcome(result.outcome)) {
    return {
      outcome: result.outcome,
      reason: `task outcome is '${result.outcome}': ${result.reason}`,
      gateResults,
    };
  }

  for (const gate of gates) {
    // Authority and provider gates are pre-flighted before execution; they have
    // no mechanical result to verify here.
    if (!isCommandGate(gate)) continue;

    const label = gateLabel(gate);
    const gateResult = gateResults.find((r) => r.gate === label);
    if (!gateResult) {
      return {
        outcome: 'blocked',
        reason: `no gate result was reported for required gate '${label}' (stage '${stageName}')`,
        gateResults,
      };
    }
    if (!isSatisfyingOutcome(gateResult.outcome)) {
      return {
        outcome: gateResult.outcome === 'simulated' ? 'error' : gateResult.outcome,
        reason: `gate '${label}' outcome is '${gateResult.outcome}': ${gateResult.reason}`,
        gateResults,
      };
    }
  }

  return {
    outcome: 'passed',
    reason:
      gates.length === 0
        ? "stage declares no gates; task outcome 'passed' accepted"
        : 'all declared gates verified passed',
    gateResults,
  };
}
