import type { HarnessCapability, HarnessEvent, HarnessEventEnvelope } from './events.js';

/** Server-derived actor and seat authority. Browser input must not supply these values. */
export interface HarnessActorContext {
  readonly actorId: string;
  readonly tenantId: string;
  readonly seatId: string;
  readonly correlationId: string;
}

/** Exact harness/provider/model tuple. These concepts must never be merged into one identifier. */
export interface HarnessSelection {
  readonly harnessId: string;
  readonly providerId: string;
  readonly modelId: string;
}

export interface HarnessDescriptor {
  readonly id: string;
  readonly displayName: string;
  readonly capabilities: readonly HarnessCapability[];
}

export type HarnessInputType = 'text' | 'image';
export type HarnessAuthState = 'ready' | 'auth_required' | 'unavailable';
export type HarnessModelAvailability = 'available' | 'unavailable';

export interface HarnessCatalogEntry extends HarnessSelection {
  readonly displayName: string;
  readonly reasoningCapability: boolean;
  readonly thinkingLevels?: readonly string[];
  readonly inputTypes: readonly HarnessInputType[];
  readonly contextWindow?: number;
  readonly authState: HarnessAuthState;
  readonly availability: HarnessModelAvailability;
}

export interface HarnessCatalog {
  readonly harnessId: string;
  readonly version: string;
  readonly fingerprint: string;
  readonly models: readonly HarnessCatalogEntry[];
}

export interface CreateHarnessSession {
  readonly context: HarnessActorContext;
  readonly conversationId: string;
  readonly selection: HarnessSelection;
}

export interface ResumeHarnessSession {
  readonly context: HarnessActorContext;
  readonly conversationId: string;
  readonly nativeSessionId: string;
  readonly selection: HarnessSelection;
}

export type HarnessSessionState = 'starting' | 'idle' | 'busy' | 'evicted' | 'ended' | 'failed';

export interface HarnessSessionSnapshot {
  readonly conversationId: string;
  readonly nativeSessionId: string;
  /** Absent when the resumable native session has no active process. */
  readonly processId?: string;
  readonly seatId: string;
  readonly selection: HarnessSelection;
  readonly state: HarnessSessionState;
  readonly attachedClientIds: readonly string[];
}

export interface AttachClient {
  readonly clientId: string;
}

export interface HarnessPrompt {
  readonly turnId: string;
  readonly correlationId: string;
  readonly content: string;
}

export type HarnessTurnState =
  | 'prepared'
  | 'dispatching'
  | 'accepted'
  | 'streaming'
  | 'settled'
  | 'failed'
  | 'aborted'
  | 'interrupted'
  | 'outcome_unknown';

/** A successful receipt reports only the selected tuple; no substitute tuple is representable. */
export interface HarnessPromptReceipt {
  readonly conversationId: string;
  readonly turnId: string;
  readonly correlationId: string;
  readonly state: HarnessTurnState;
  readonly selection: HarnessSelection;
}

export interface HarnessConfirmInteractionResponse {
  readonly requestId: string;
  readonly type: 'confirm';
  readonly accepted: boolean;
}

export interface HarnessSelectInteractionResponse {
  readonly requestId: string;
  readonly type: 'select';
  readonly value: string;
}

export interface HarnessInputInteractionResponse {
  readonly requestId: string;
  readonly type: 'input';
  readonly value: string;
}

export interface HarnessEditorInteractionResponse {
  readonly requestId: string;
  readonly type: 'editor';
  readonly value: string;
}

export interface HarnessCancelInteractionResponse {
  readonly requestId: string;
  readonly type: 'cancel';
}

export type HarnessInteractionResponse =
  | HarnessConfirmInteractionResponse
  | HarnessSelectInteractionResponse
  | HarnessInputInteractionResponse
  | HarnessEditorInteractionResponse
  | HarnessCancelInteractionResponse;

export type HarnessCloseReason =
  | 'client_request'
  | 'idle_timeout'
  | 'gateway_shutdown'
  | 'process_crash'
  | 'composition_changed'
  | 'session_ended';

export interface AttachConversation {
  readonly context: HarnessActorContext;
  readonly conversationId: string;
  readonly clientId: string;
  readonly selection: HarnessSelection;
}

export interface ConversationSnapshot {
  readonly session: HarnessSessionSnapshot;
  readonly lastSequence: number;
  /** Journal rows replayed after the caller's sequence, never best-effort socket history. */
  readonly replay: readonly HarnessEventEnvelope[];
}

export interface DetachConversation {
  readonly context: HarnessActorContext;
  readonly conversationId: string;
  readonly clientId: string;
}

export interface SendHarnessTurn extends HarnessPrompt {
  readonly context: HarnessActorContext;
  readonly conversationId: string;
  readonly selection: HarnessSelection;
}

export interface TurnReceipt extends HarnessPromptReceipt {}

export interface HarnessAdapter {
  readonly id: string;
  describe(context: HarnessActorContext): Promise<HarnessDescriptor>;
  catalog(context: HarnessActorContext): Promise<HarnessCatalog>;
  create(input: CreateHarnessSession): Promise<HarnessSessionHandle>;
  resume(input: ResumeHarnessSession): Promise<HarnessSessionHandle>;
}

export interface HarnessSessionHandle {
  snapshot(): Promise<HarnessSessionSnapshot>;
  attach(input: AttachClient): Promise<void>;
  /** Removes a browser attachment; it does not terminate the process or native session. */
  detach(clientId: string): Promise<void>;
  prompt(input: HarnessPrompt & { idempotencyKey: string }): Promise<HarnessPromptReceipt>;
  setModel(selection: HarnessSelection): Promise<HarnessSelection>;
  abort(turnId: string): Promise<void>;
  respondInteraction(input: HarnessInteractionResponse): Promise<void>;
  events(listener: (event: HarnessEvent) => void): () => void;
  /** Stops the active process while retaining the resumable native session. */
  evictProcess(reason: HarnessCloseReason): Promise<void>;
  /** Explicitly and destructively ends the native session. */
  endSession(reason: HarnessCloseReason): Promise<void>;
}

export interface HarnessConversationService {
  attach(input: AttachConversation & { afterSequence?: number }): Promise<ConversationSnapshot>;
  /** Removes only the browser attachment represented by the input. */
  detach(input: DetachConversation): Promise<void>;
  send(input: SendHarnessTurn & { idempotencyKey: string }): Promise<TurnReceipt>;
  /** Replays persisted Gateway journal rows after the supplied monotonic sequence. */
  subscribeFrom(conversationId: string, afterSequence: number): AsyncIterable<HarnessEventEnvelope>;
}
