/**
 * Message Types — Fully typed JSON-RPC message definitions for the AHP wire protocol.
 *
 * @module common/messages
 * @description Typed JSON-RPC request, response, and notification types for all
 * AHP methods. Narrowing on the `method` field gives fully typed `params` and
 * result types.
 */

import type {
  InitializeParams,
  InitializeResult,
  PingParams,
  ReconnectParams,
  ReconnectResult,
  SubscribeParams,
  SubscribeResult,
  ResourceReadParams,
  ResourceReadResult,
  ResourceWriteParams,
  ResourceWriteResult,
  ResourceListParams,
  ResourceListResult,
  ResourceCopyParams,
  ResourceCopyResult,
  ResourceDeleteParams,
  ResourceDeleteResult,
  ResourceMoveParams,
  ResourceMoveResult,
  ResourceResolveParams,
  ResourceResolveResult,
  ResourceMkdirParams,
  ResourceMkdirResult,
  ResourceRequestParams,
  ResourceRequestResult,
  UnsubscribeParams,
  DispatchActionParams,
  AuthenticateParams,
  AuthenticateResult,
} from './commands.js';
import type {
  ListSessionsParams,
  ListSessionsResult,
  ResolveSessionConfigParams,
  ResolveSessionConfigResult,
  SessionConfigCompletionsParams,
  SessionConfigCompletionsResult,
} from '../channels-root/commands.js';
import type {
  CreateSessionParams,
  DisposeSessionParams,
  FetchTurnsParams,
  FetchTurnsResult,
  CompletionsParams,
  CompletionsResult,
} from '../channels-session/commands.js';
import type {
  CreateTerminalParams,
  DisposeTerminalParams,
} from '../channels-terminal/commands.js';
import type {
  CreateResourceWatchParams,
  CreateResourceWatchResult,
} from '../channels-resource-watch/commands.js';
import type {
  InvokeChangesetOperationParams,
  InvokeChangesetOperationResult,
} from '../channels-changeset/commands.js';

import type { ActionEnvelope } from './actions.js';
import type {
  SessionAddedParams,
  SessionRemovedParams,
  SessionSummaryChangedParams,
} from '../channels-root/notifications.js';
import type { AuthRequiredParams } from './notifications.js';
import type {
  OtlpExportLogsParams,
  OtlpExportTracesParams,
  OtlpExportMetricsParams,
} from '../channels-otlp/notifications.js';
import type { AhpError } from './errors.js';

// ─── JSON-RPC Base Types ─────────────────────────────────────────────────────

/** A JSON-RPC request: has both `method` and `id`. */
export interface JsonRpcRequest {
  readonly jsonrpc: '2.0';
  readonly id: number;
  readonly method: string;
  readonly params?: unknown;
}

/** A JSON-RPC success response. */
export interface JsonRpcSuccessResponse {
  readonly jsonrpc: '2.0';
  readonly id: number;
  readonly result: unknown;
}

/** A JSON-RPC error response. */
export interface JsonRpcErrorResponse {
  readonly jsonrpc: '2.0';
  readonly id: number;
  readonly error: {
    readonly code: number;
    readonly message: string;
    readonly data?: unknown;
  };
}

/**
 * A typed JSON-RPC error response whose error object is a fully typed
 * {@link AhpError}. Useful when the caller knows the response is an AHP
 * application error and wants `data` narrowed by `code`.
 */
export interface AhpErrorResponse {
  readonly jsonrpc: '2.0';
  readonly id: number;
  readonly error: AhpError;
}

/** A JSON-RPC response (success or error). */
export type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;

/** A JSON-RPC notification: has `method` but no `id`. */
export interface JsonRpcNotification {
  readonly jsonrpc: '2.0';
  readonly method: string;
  readonly params?: unknown;
}

// ─── Command Map ─────────────────────────────────────────────────────────────

/**
 * Registry mapping each command method name to its params and result types.
 *
 * `CommandMap` covers methods that the client sends to the server. Methods
 * that may also be initiated by the server are duplicated in
 * {@link ServerCommandMap}; the entries in the two maps are kept identical.
 *
 * @category Commands
 */
export interface CommandMap {
  'initialize': { params: InitializeParams; result: InitializeResult };
  'ping': { params: PingParams; result: null };
  'reconnect': { params: ReconnectParams; result: ReconnectResult };
  'subscribe': { params: SubscribeParams; result: SubscribeResult };
  'createSession': { params: CreateSessionParams; result: null };
  'disposeSession': { params: DisposeSessionParams; result: null };
  'createTerminal': { params: CreateTerminalParams; result: null };
  'disposeTerminal': { params: DisposeTerminalParams; result: null };
  'createResourceWatch': { params: CreateResourceWatchParams; result: CreateResourceWatchResult };
  'listSessions': { params: ListSessionsParams; result: ListSessionsResult };
  'resourceRead': { params: ResourceReadParams; result: ResourceReadResult };
  'resourceWrite': { params: ResourceWriteParams; result: ResourceWriteResult };
  'resourceList': { params: ResourceListParams; result: ResourceListResult };
  'resourceCopy': { params: ResourceCopyParams; result: ResourceCopyResult };
  'resourceDelete': { params: ResourceDeleteParams; result: ResourceDeleteResult };
  'resourceMove': { params: ResourceMoveParams; result: ResourceMoveResult };
  'resourceResolve': { params: ResourceResolveParams; result: ResourceResolveResult };
  'resourceMkdir': { params: ResourceMkdirParams; result: ResourceMkdirResult };
  'resourceRequest': { params: ResourceRequestParams; result: ResourceRequestResult };
  'fetchTurns': { params: FetchTurnsParams; result: FetchTurnsResult };
  'authenticate': { params: AuthenticateParams; result: AuthenticateResult };
  'resolveSessionConfig': { params: ResolveSessionConfigParams; result: ResolveSessionConfigResult };
  'sessionConfigCompletions': { params: SessionConfigCompletionsParams; result: SessionConfigCompletionsResult };
  'completions': { params: CompletionsParams; result: CompletionsResult };
  'invokeChangesetOperation': { params: InvokeChangesetOperationParams; result: InvokeChangesetOperationResult };
}

/**
 * Registry mapping each server → client request method to its params and
 * result types.
 *
 * The `resource*` family is symmetrical: every method that appears in
 * {@link CommandMap} also appears here with the identical params/result
 * shape, and the receiver decides whether to allow, deny, or prompt for
 * the requested operation regardless of which peer initiated. Hosts use
 * the reverse direction to read from client-published URIs (e.g.
 * `virtual://my-client/...` plugins) and to drive per-session filesystem
 * providers without the client having to re-implement the wire schema.
 *
 * @category Commands
 */
export interface ServerCommandMap {
  'resourceRead': { params: ResourceReadParams; result: ResourceReadResult };
  'resourceWrite': { params: ResourceWriteParams; result: ResourceWriteResult };
  'resourceList': { params: ResourceListParams; result: ResourceListResult };
  'resourceCopy': { params: ResourceCopyParams; result: ResourceCopyResult };
  'resourceDelete': { params: ResourceDeleteParams; result: ResourceDeleteResult };
  'resourceMove': { params: ResourceMoveParams; result: ResourceMoveResult };
  'resourceResolve': { params: ResourceResolveParams; result: ResourceResolveResult };
  'resourceMkdir': { params: ResourceMkdirParams; result: ResourceMkdirResult };
  'resourceRequest': { params: ResourceRequestParams; result: ResourceRequestResult };
  'createResourceWatch': { params: CreateResourceWatchParams; result: CreateResourceWatchResult };
}

// ─── Notification Maps ───────────────────────────────────────────────────────

/**
 * Registry mapping each client → server notification method to its params type.
 *
 * Every notification's params MUST carry a top-level `channel: URI` so that
 * the server can route the message to the correct subscription. See
 * {@link UnsubscribeParams} for the canonical "base" shape.
 *
 * @category Notifications
 */
export interface ClientNotificationMap {
  'unsubscribe': { params: UnsubscribeParams };
  'dispatchAction': { params: DispatchActionParams };
}

/**
 * Registry mapping each server → client notification method to its params type.
 *
 * Every notification's params MUST carry a top-level `channel: URI` so that
 * the client can dispatch the message to the right subscription.
 *
 * @category Notifications
 */
export interface ServerNotificationMap {
  'action': { params: ActionEnvelope };
  'root/sessionAdded': { params: SessionAddedParams };
  'root/sessionRemoved': { params: SessionRemovedParams };
  'root/sessionSummaryChanged': { params: SessionSummaryChangedParams };
  'auth/required': { params: AuthRequiredParams };
  'otlp/exportLogs': { params: OtlpExportLogsParams };
  'otlp/exportTraces': { params: OtlpExportTracesParams };
  'otlp/exportMetrics': { params: OtlpExportMetricsParams };
}

// ─── Typed Requests ──────────────────────────────────────────────────────────

/**
 * A fully typed JSON-RPC request for a specific AHP command.
 *
 * When used as a union (default generic), narrowing on `method` gives typed `params`:
 *
 * ```ts
 * function handle(req: AhpRequest) {
 *   if (req.method === 'fetchTurns') {
 *     req.params.session; // typed as URI
 *   }
 * }
 * ```
 *
 * Defaults to client → server requests ({@link CommandMap}). Use
 * {@link AhpServerRequest} for server → client requests.
 */
export type AhpRequest<M extends keyof CommandMap = keyof CommandMap> =
  M extends unknown ? {
    readonly jsonrpc: '2.0';
    readonly id: number;
    readonly method: M;
    readonly params: CommandMap[M]['params'];
  } : never;

/**
 * A fully typed JSON-RPC request initiated by the server. Identical in shape
 * to {@link AhpRequest} but parameterised over {@link ServerCommandMap}.
 */
export type AhpServerRequest<M extends keyof ServerCommandMap = keyof ServerCommandMap> =
  M extends unknown ? {
    readonly jsonrpc: '2.0';
    readonly id: number;
    readonly method: M;
    readonly params: ServerCommandMap[M]['params'];
  } : never;

// ─── Typed Responses ─────────────────────────────────────────────────────────

/**
 * A fully typed JSON-RPC success response for a specific AHP command.
 *
 * Since JSON-RPC responses do not carry `method`, use this with an explicit
 * generic parameter when you know the method from the associated request:
 *
 * ```ts
 * const result: AhpSuccessResponse<'fetchTurns'> = ...;
 * result.result.turns; // typed as Turn[]
 * ```
 */
export type AhpSuccessResponse<M extends keyof CommandMap = keyof CommandMap> =
  M extends unknown ? {
    readonly jsonrpc: '2.0';
    readonly id: number;
    readonly result: CommandMap[M]['result'];
  } : never;

/** Typed JSON-RPC response (success with known result type, or error). */
export type AhpResponse<M extends keyof CommandMap = keyof CommandMap> =
  | AhpSuccessResponse<M>
  | JsonRpcErrorResponse;

/**
 * A fully typed JSON-RPC success response for a server → client request
 * ({@link ServerCommandMap}).
 */
export type AhpServerSuccessResponse<M extends keyof ServerCommandMap = keyof ServerCommandMap> =
  M extends unknown ? {
    readonly jsonrpc: '2.0';
    readonly id: number;
    readonly result: ServerCommandMap[M]['result'];
  } : never;

/** Typed JSON-RPC response to a server → client request. */
export type AhpServerResponse<M extends keyof ServerCommandMap = keyof ServerCommandMap> =
  | AhpServerSuccessResponse<M>
  | JsonRpcErrorResponse;

// ─── Typed Notifications ─────────────────────────────────────────────────────

/** A client → server notification. */
export type AhpClientNotification<M extends keyof ClientNotificationMap = keyof ClientNotificationMap> =
  M extends unknown ? {
    readonly jsonrpc: '2.0';
    readonly method: M;
    readonly params: ClientNotificationMap[M]['params'];
  } : never;

/** A server → client notification. */
export type AhpServerNotification<M extends keyof ServerNotificationMap = keyof ServerNotificationMap> =
  M extends unknown ? {
    readonly jsonrpc: '2.0';
    readonly method: M;
    readonly params: ServerNotificationMap[M]['params'];
  } : never;

/**
 * A fully typed JSON-RPC notification — either direction.
 *
 * The client → server `dispatchAction` method and the server → client
 * `action` method are distinct entries in the registries; their params have
 * unrelated shapes ({@link DispatchActionParams} vs {@link ActionEnvelope}).
 */
export type AhpNotification = AhpClientNotification | AhpServerNotification;

// ─── Protocol Message Union ──────────────────────────────────────────────────

/**
 * Discriminated union of all AHP protocol messages.
 *
 * Narrow using standard JSON-RPC structure:
 * - Has `method` + `id` → request ({@link AhpRequest} or {@link AhpServerRequest})
 * - Has `method`, no `id` → notification ({@link AhpNotification})
 * - Has `result` or `error` + `id` → response ({@link AhpResponse})
 *
 * Then narrow on `method` for fully typed params:
 *
 * ```ts
 * function dispatch(msg: ProtocolMessage) {
 *   if ('method' in msg && 'id' in msg) {
 *     // msg is AhpRequest | AhpServerRequest
 *     if (msg.method === 'fetchTurns') {
 *       msg.params.session; // URI
 *     }
 *   }
 * }
 * ```
 */
export type ProtocolMessage =
  | AhpRequest
  | AhpServerRequest
  | AhpSuccessResponse
  | AhpServerSuccessResponse
  | JsonRpcErrorResponse
  | AhpNotification;
