import type {
  McpServerConfig,
  Options,
  Query,
  SDKUserMessage,
} from '@anthropic-ai/claude-agent-sdk';

import type { ResultAsync } from './consumer-neverthrow.js';

type ConfidenceLabel = 'HIGH' | 'MEDIUM' | 'LOW';

type FindingTriggerType =
  | 'spec-deviation'
  | 'stuck-loop'
  | 'low-confidence'
  | 'dead-end'
  | 'stuck-modal'
  | 'back-nav-failure'
  | 'visual-regression'
  | 'localization-issue'
  | 'design-system-violation'
  | 'loading-regression'
  | 'motion-regression'
  | 'interaction-regression'
  | 'continuity-regression'
  | 'missing-content';

interface EvidenceFrame {
  timestamp: string;
  description: string;
}

interface FindingBase {
  triggerType: FindingTriggerType;
  flow: string;
  steps: string[];
  stepIndex?: number | undefined;
  confidence: ConfidenceLabel;
  title: string;
  description: string;
  screenshots: string[];
  agent: string;
  scenarioId?: string | undefined;
  evidenceFrames?: EvidenceFrame[] | undefined;
  videoClipPath?: string | undefined;
}

interface BlockerFinding extends FindingBase {
  category: 'blocker';
  unmetRequirement: string;
}

interface BugFinding extends FindingBase {
  category: 'bug';
  unmetRequirement?: never;
}

type Finding = BugFinding | BlockerFinding;

interface TokenUsage {
  inputTokens: number;
  outputTokens: number;
  cacheReadInputTokens: number;
  cacheCreationInputTokens: number;
  totalCostUsd: number;
}

type QueryRunner = (params: {
  prompt: string | AsyncIterable<SDKUserMessage>;
  options?: Options;
}) => Query;

export type AgentEvent =
  | { type: 'STAGE_START'; agent: string }
  | { type: 'STAGE_END'; agent: string; durationMs: number }
  | { type: 'TOOL_CALL'; agent: string; toolName: string; input: unknown }
  | { type: 'TOOL_RESULT'; agent: string; toolName: string; result: string }
  | { type: 'TOOL_ERROR'; agent: string; toolName: string; error: string }
  | { type: 'THOUGHT'; agent: string; text: string }
  | { type: 'SCREEN_STATE'; agent: string; snapshot: string }
  | { type: 'SCREENSHOT'; agent: string; data: string; mimeType: 'image/png' }
  | {
      type: 'SCREEN_MEMORY';
      agent: string;
      sessionsObserved: number;
      elements: { label: string; confidence: number; phase: 'initial' | 'after-scroll' }[];
      enrichedSnapshot: string;
    }
  | { type: 'TIMEOUT_GRACE_ENTERED'; agent: string; gracePeriodMs: number }
  | { type: 'SYSTEM_PROMPT'; agent: string; prompt: string }
  | { type: 'PROGRESS'; agent: string; message: string; transient?: boolean }
  | { type: 'ERROR'; agent: string; message: string; stack?: string }
  | {
      type: 'AGENT_RETRY';
      agent: string;
      attempt: number;
      maxAttempts: number;
      delayMs: number;
      cause: unknown;
    }
  | { type: 'AGENT_FAILED_NON_CRITICAL'; agent: string; attempts: number; cause: unknown }
  | {
      type: 'VISUAL_STEP';
      agent: string;
      stepIndex: number;
      screenshotPath: string;
      tree: string;
      screenLabel: string;
    }
  | {
      type: 'FINDING_REPORTED';
      agent: string;
      finding: Finding;
      findingIndex: number;
      screenshotPath: string;
    }
  | {
      type: 'VISUAL_PASS_RUN';
      agent: string;
      durationMs: number;
      findingsEmitted: number;
      outcome: 'ok' | 'api_failed' | 'input_invalid' | 'skipped_similar';
      similarDistance?: number;
    }
  | { type: 'TOKEN_USAGE'; agent: string; usage: TokenUsage }
  | {
      type: 'RECORDING_DROPPED';
      agent: string;
      exitCode: number | undefined;
      signal: NodeJS.Signals | undefined;
      durationMs: number;
    };

export type AgentHandler = (event: AgentEvent) => void;

export interface ProvisionerError {
  readonly type: 'AI_CALL_FAILED';
  readonly cause: unknown;
}

export interface ProvisionerRun {
  readonly objective: string;
}

export interface ProvisionerConfig {
  readonly mcpServers: Record<string, McpServerConfig>;
  readonly allowedTools: readonly string[];
  readonly maxTurns?: number | undefined;
  readonly model?: string | undefined;
  readonly timeoutMs?: number | undefined;
  readonly signal?: AbortSignal | undefined;
  readonly onEvent?: AgentHandler | undefined;
  readonly queryRunner?: QueryRunner | undefined;
}

export declare function setup(
  objective: string,
  config: ProvisionerConfig,
): ResultAsync<ProvisionerRun, ProvisionerError>;

export declare function teardown(
  objective: string,
  config: ProvisionerConfig,
): ResultAsync<ProvisionerRun, ProvisionerError>;
