/**
 * Shared types for Heron's MCP **server** (Role B of AAP-46).
 *
 * These types lock the interface contract between the transport-agnostic
 * tool wrapper (`src/server/mcp-server.ts`) and any future Hosted
 * deployment (AAP-47). The same wrapper is mounted at stdio in OSS and at
 * HTTP transport on Hosted; the transport difference must be invisible to
 * the tool handlers, so the per-invocation `RequestContext` carries
 * everything they need without reaching into `process.stdin/stdout` or
 * raw HTTP req/res.
 *
 * Tracking: https://linear.app/theona/issue/AAP-46
 */

/**
 * Identity claims for the caller of an MCP tool, as resolved by the
 * transport. `null` on stdio (Heron OSS runs locally without auth);
 * populated by the hosted side once it validates a bearer token.
 *
 * The shape is deliberately minimal — only fields all transports can
 * produce. Extra claims live under `extra`.
 */
export interface AuthPrincipal {
  /** Stable identifier for the validated token (e.g. token jti / hash). */
  tokenId: string;
  /** OAuth-style scopes granted by the token. */
  scopes: string[];
  /** Optional client identifier (OAuth client_id) when the transport surfaces it. */
  clientId?: string;
  /** Forward-compatible bag for transport-specific extras. */
  extra?: Record<string, unknown>;
}

/**
 * Incremental progress signal emitted by a long-running tool handler. The
 * wrapper translates these into MCP `notifications/progress` so the
 * client can stream them to the user.
 *
 * `stage` is the only required field; `pct` and `message` are best-effort.
 * Stages used by `audit_agent` today: `interrogating | analyzing | mapping
 * | rendering`. Other tools may invent their own — keep them short.
 */
export interface ProgressNotification {
  stage: string;
  /** Estimated completion percentage, 0-100. Omit when unknown. */
  pct?: number;
  /** Optional human-readable detail. */
  message?: string;
}

/**
 * Opaque, per-invocation context handed to every MCP tool handler.
 *
 * Handlers MUST NOT reach into `process.stdin/stdout/argv` or raw HTTP
 * req/res. Everything they need to interact with the caller — auth,
 * session id, progress, cancellation — flows through this object. That
 * decoupling is what lets the same handler run unchanged under stdio
 * (OSS) and HTTP (Hosted) transports.
 */
export interface RequestContext {
  /** Auth principal — `null` for local stdio sessions. */
  authPrincipal: AuthPrincipal | null;
  /** Stable identifier for the MCP session this invocation belongs to. */
  sessionId: string;
  /** Emit a progress notification. Best-effort — never throws. */
  progress: (notification: ProgressNotification) => void;
  /** Cancellation signal from the caller. Handlers should check it. */
  signal: AbortSignal;
  /**
   * AAP-58 — absolute workspace paths the MCP client advertised via
   * `_meta['x-codex-turn-metadata'].workspaces` (or any future
   * equivalent). Undefined or empty array when the client did not
   * provide any. Handlers that persist sessions pass this through to
   * `createSession`/`mergeWorkspaceHints` so the dashboard scan API
   * can resolve `workspaceHints[0]` instead of falling back to
   * `process.cwd()` (Heron's own checkout). Optional so existing
   * RequestContext literals (in tests + downstream forks) keep
   * compiling.
   */
  workspaceHints?: string[];
}

/**
 * Server-side error kinds. Mirrors Role A's `MCPClientError` shape so
 * downstream code can branch on `kind` without parsing strings.
 *
 *  - `invalid_input`  — caller-supplied arguments failed validation.
 *  - `tool_failure`   — handler ran but a downstream dependency failed
 *                       (audit pipeline error, missing report, etc.).
 *  - `cancelled`      — abort signal fired before the handler completed.
 *  - `auth_required`  — handler needs an auth principal but none was set.
 *  - `internal`       — unexpected runtime error inside the wrapper.
 */
export type MCPServerError =
  | { kind: 'invalid_input'; field: string; message: string }
  | { kind: 'tool_failure'; cause: string; message: string }
  | { kind: 'cancelled'; message: string }
  | { kind: 'auth_required'; message: string }
  | { kind: 'internal'; message: string; cause?: unknown };

/**
 * Result-style return from every MCP tool handler. Callers branch on
 * `ok`; handlers never throw for conditions described by
 * `MCPServerError`. Mirrors Role A's `MCPClientResult<T>`.
 */
export type MCPServerResult<T> =
  | { ok: true; value: T }
  | { ok: false; error: MCPServerError };

// ─── Tool-specific input/output shapes (locked) ─────────────────────────

/** Input for the `get_report` MCP tool. */
export interface GetReportInput {
  report_id: string;
}

/** Output for the `get_report` MCP tool. */
export interface GetReportOutput {
  report_markdown: string;
  metadata: {
    report_id: string;
    target: string;
    created_at: string;
    risk_level?: string;
  };
}

/** Input for the `compare_reports` MCP tool. */
export interface CompareReportsInput {
  report_id_a: string;
  report_id_b: string;
}

/** Output for the `compare_reports` MCP tool. */
export interface CompareReportsOutput {
  diff_markdown: string;
}

// ─── start_audit_session (AAP-52) ─────────────────────────────────────────

/**
 * Input for the `start_audit_session` MCP tool.
 *
 * No `target_endpoint`: under AAP-52 the audited agent IS the MCP client
 * that just called this tool. Answers flow back over the same JSON-RPC
 * session via `sampling/createMessage`.
 */
export interface StartAuditSessionInput {
  /** Optional human label for the agent under audit. */
  agent_name?: string;
}

/** Output for the `start_audit_session` MCP tool. */
export interface StartAuditSessionOutput {
  /** ~/.heron/sessions/ id — pasteable into the dashboard URL. */
  session_id: string;
  /**
   * Session status at hand-off time. Values:
   *   - `interviewing`: sampling mode. Background loop is running; caller polls get_report.
   *   - `awaiting_answer`: tool-call mode (AAP-55). Caller MUST reply with submit_answer.
   *   - `complete` / `error`: terminal states (not normally observed at start_audit_session).
   */
  status: string;
  /** Number of Q/A pairs captured. */
  questions_asked: number;
  /** Overall risk level — when the analyzer surfaced one. */
  risk_level?: string;
  /** Final rendered report (markdown). */
  report_markdown?: string;
  /** AAP-55 — present when `status === 'awaiting_answer'`. */
  next_question?: string;
  /** AAP-55 — caller-facing instructions for the tool-call loop. */
  instructions?: string;
}

// ─── submit_answer (AAP-55) ───────────────────────────────────────────────

/** Input for the `submit_answer` MCP tool. */
export interface SubmitAnswerInput {
  session_id: string;
  answer: string;
}

/**
 * Output for the `submit_answer` MCP tool.
 *
 * `status` is one of:
 *  - `awaiting_answer` — continue the loop, ask the agent for the next answer.
 *  - `analyzing` — AAP-66 path. The planner is exhausted and Heron is running
 *    the analyzer + verdict pipeline as a detached background promise. The
 *    tool call returns immediately to dodge the client tool-call timeout
 *    (Codex CLI = 120s, not user-configurable). Clients retrieve the final
 *    report via `get_report` once the dashboard SSE stream publishes the
 *    `status: 'complete'` (or `status: 'analysis_failed'`) event.
 *    `report_markdown` and `risk_level` are intentionally NOT present here —
 *    they only arrive over SSE and via the persisted session.
 *  - `complete` — terminal, report fields set, analyzer produced a clean run.
 *    Kept in the union for forward/backward compatibility with older callers
 *    and tests; `submit_answer` itself no longer returns this directly under
 *    AAP-66. Reached via `get_report` or the dashboard SSE stream.
 *  - `analysis_failed` — terminal, AAP-56 path. Analyzer could not produce
 *    a structured report (double-parse failure or unreachable LLM gateway).
 *    `report_markdown` is the failure-mode markdown (no risk badge, no
 *    findings, no recommendation); `error` carries the diagnostic envelope.
 *    Same SSE-only delivery story as `complete` under AAP-66.
 */
export interface SubmitAnswerOutput {
  session_id: string;
  status: 'awaiting_answer' | 'analyzing' | 'complete' | 'analysis_failed';
  questions_asked: number;
  /** Present when status === 'awaiting_answer'. */
  next_question?: string;
  /** Present when status === 'complete' or 'analysis_failed'. */
  report_markdown?: string;
  /** Present when status === 'complete' and the analyzer surfaced one. */
  risk_level?: string;
  /** Present when status === 'analysis_failed'. */
  error?: {
    reason: 'parse_failure' | 'llm_unreachable' | 'unknown';
    message: string;
  };
}
