import type { ChannelAttachmentDto } from '../channel/index.js';
import type {
  CommandManifestPayload,
  SlashCommandApprovalResultPayload,
  SlashCommandPayload,
  SlashCommandResultPayload,
  SystemReloadPayload,
} from '../commands/index.js';
import type { HarnessErrorCode, HarnessSelection, HarnessTurnState } from '../harness/index.js';

export interface MessageAckPayload {
  conversationId: string;
  messageId: string;
}

export interface AgentStartPayload {
  conversationId: string;
}

export interface AgentEndPayload {
  conversationId: string;
  usage?: SessionUsagePayload;
}

/** Session metadata emitted with agent:end and on session:info */
export interface SessionUsagePayload {
  provider: string;
  modelId: string;
  thinkingLevel: string;
  tokens: {
    input: number;
    output: number;
    cacheRead: number;
    cacheWrite: number;
    total: number;
  };
  cost: number;
  context: {
    percent: number | null;
    window: number;
  };
}

export interface AgentTextPayload {
  conversationId: string;
  text: string;
}

export interface AgentThinkingPayload {
  conversationId: string;
  text: string;
}

export interface ToolStartPayload {
  conversationId: string;
  toolCallId: string;
  toolName: string;
}

export interface ToolEndPayload {
  conversationId: string;
  toolCallId: string;
  toolName: string;
  isError: boolean;
}

export interface ErrorPayload {
  conversationId: string;
  error: string;
}

export interface ChatMessagePayload {
  conversationId?: string;
  content: string;
  provider?: string;
  modelId?: string;
  agentId?: string;
  attachments?: readonly ChannelAttachmentDto[];
}

/** Routing decision summary included in session:info for transparency */
export interface RoutingDecisionInfo {
  model: string;
  provider: string;
  ruleName: string;
  reason: string;
}

/** Session info pushed when session is created or model changes */
export interface SessionInfoPayload {
  conversationId: string;
  provider: string;
  modelId: string;
  thinkingLevel: string;
  availableThinkingLevels: string[];
  /** Present when automatic routing determined the model for this session */
  routingDecision?: RoutingDecisionInfo;
}

/** Client request to change thinking level */
export interface SetThinkingPayload {
  conversationId: string;
  level: string;
}

/** Client request to abort the current agent operation */
export interface AbortPayload {
  conversationId: string;
}

/**
 * The frozen P3 `turn:send` wire contract (Task Five; reused unchanged by Tasks 15 and 16).
 * Accepts no attachments or authority-bearing fields in Slice Zero. Gateway validation
 * requires a UUID conversation id, non-empty bounded content, a nested selection with exactly
 * `harnessId`/`providerId`/`modelId` (each 1..255 chars), and a UUID-v4 idempotency key; it
 * rejects unknown fields, top-level `provider`/`modelId`, malformed nesting, and empty values
 * before any runtime dispatch.
 */
export interface HarnessTurnSendPayload {
  readonly conversationId: string; // UUID; required before send
  readonly content: string; // trimmed, 1..10_000 characters
  readonly selection: HarnessSelection; // nested; exactly three ids
  readonly idempotencyKey: string; // browser-generated UUID v4
}

/**
 * The frozen `turn:ack` wire contract. Success echoes the accepted idempotency key and the
 * exact requested selection tuple; failure carries only fixed/safe text and never a
 * substituted effective selection or raw exception text.
 */
export type HarnessTurnAckPayload =
  | {
      readonly ok: true;
      readonly conversationId: string;
      readonly idempotencyKey: string;
      readonly turnId: string;
      readonly correlationId: string;
      readonly state: HarnessTurnState;
      readonly selection: HarnessSelection;
    }
  | {
      readonly ok: false;
      readonly conversationId?: string;
      readonly idempotencyKey?: string;
      readonly code: HarnessErrorCode | 'request_invalid' | 'runtime_unsupported';
      readonly message: string; // fixed/safe text only
      readonly retryable: boolean;
      readonly correlationId: string;
      /** Present only when a complete tuple was validated; always the requested tuple. */
      readonly selection?: HarnessSelection;
    };

/**
 * The frozen browser send-protocol advertisement (Task Five; server → client only).
 *
 * A conversation id or a harness selection never proves that the connected Gateway actually
 * handles a given wire event, so after BetterAuth authenticates a browser Socket connection the
 * Gateway advertises — exactly once, targeted to that socket — which send event the client may
 * use. `legacy-message` in legacy mode, `unavailable` in `pi-rpc` (including test-ready Pi
 * graphs); Task Five never advertises `turn-send` (its authenticated handler lands in Task 15).
 * Capability is routing information, never authorization: every server handler still enforces
 * authentication, ownership, DTO, mode, and runtime checks.
 */
export type ChatSendProtocol = 'legacy-message' | 'turn-send' | 'unavailable';

export interface ChatSendCapabilityPayload {
  readonly protocol: ChatSendProtocol;
  /** Exact Socket.IO id for the authenticated browser connection this advertisement is bound to. */
  readonly connectionId: string;
}

/** Socket.IO typed event map: server → client */
export interface ServerToClientEvents {
  'chat:send-capability': (payload: ChatSendCapabilityPayload) => void;
  'message:ack': (payload: MessageAckPayload) => void;
  'agent:start': (payload: AgentStartPayload) => void;
  'agent:end': (payload: AgentEndPayload) => void;
  'agent:text': (payload: AgentTextPayload) => void;
  'agent:thinking': (payload: AgentThinkingPayload) => void;
  'agent:tool:start': (payload: ToolStartPayload) => void;
  'agent:tool:end': (payload: ToolEndPayload) => void;
  'session:info': (payload: SessionInfoPayload) => void;
  'commands:manifest': (payload: CommandManifestPayload) => void;
  'command:result': (payload: SlashCommandResultPayload) => void;
  'command:approval': (payload: SlashCommandApprovalResultPayload) => void;
  'system:reload': (payload: SystemReloadPayload) => void;
  'turn:ack': (payload: HarnessTurnAckPayload) => void;
  error: (payload: ErrorPayload) => void;
}

/** Socket.IO typed event map: client → server */
export interface ClientToServerEvents {
  message: (data: ChatMessagePayload) => void;
  'turn:send': (data: HarnessTurnSendPayload) => void;
  'set:thinking': (data: SetThinkingPayload) => void;
  'command:execute': (data: SlashCommandPayload) => void;
  'command:approve': (data: SlashCommandPayload) => void;
  abort: (data: AbortPayload) => void;
}
