/**
 * Type definitions for mcpc
 * Re-exports MCP SDK types and defines additional application-specific types
 */

// Import types for use in interface definitions
import type {
  Tool,
  Resource,
  ResourceTemplate,
  Prompt,
  PromptArgument,
  Implementation,
  ClientCapabilities,
  ServerCapabilities,
  InitializeRequest,
  InitializeResult,
  CallToolRequest,
  CallToolResult,
  ListToolsRequest,
  ListToolsResult,
  ListResourcesRequest,
  ListResourcesResult,
  ReadResourceRequest,
  ReadResourceResult,
  ListPromptsRequest,
  ListPromptsResult,
  GetPromptRequest,
  GetPromptResult,
  SubscribeRequest,
  UnsubscribeRequest,
  LoggingLevel,
  ListResourceTemplatesResult,
  Task,
  GetTaskResult,
  ListTasksResult,
  CancelTaskResult,
} from '@modelcontextprotocol/sdk/types.js';

// Re-export core MCP types for external use
export type {
  Tool,
  Resource,
  ResourceTemplate,
  Prompt,
  PromptArgument,
  Implementation,
  ClientCapabilities,
  ServerCapabilities,
  InitializeRequest,
  InitializeResult,
  CallToolRequest,
  CallToolResult,
  ListToolsRequest,
  ListToolsResult,
  ListResourcesRequest,
  ListResourcesResult,
  ReadResourceRequest,
  ReadResourceResult,
  ListPromptsRequest,
  ListPromptsResult,
  GetPromptRequest,
  GetPromptResult,
  SubscribeRequest,
  UnsubscribeRequest,
  LoggingLevel,
  Task,
  GetTaskResult,
  ListTasksResult,
  CancelTaskResult,
};

// Re-export protocol version constants
export { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/sdk/types.js';

/** Keepalive ping interval in milliseconds (30 seconds) */
export const KEEPALIVE_INTERVAL_MS = 30_000;

/** Threshold for considering a session disconnected (bridge alive but server unreachable) */
export const DISCONNECTED_THRESHOLD_MS = 2 * KEEPALIVE_INTERVAL_MS + 5000; // ~2 missed pings + 5s buffer

/** Valid x402 scheme preferences. Canonical source for CLI validation and type-narrowing. */
export const X402_SCHEME_PREFERENCES = ['auto', 'upto', 'exact'] as const;
export type X402SchemePreference = (typeof X402_SCHEME_PREFERENCES)[number];

/**
 * Configuration for a connection to MCP server
 * Used both for config file format and internal representation
 */
export interface ServerConfig {
  url?: string; // Mandatory tor http transport
  headers?: Record<string, string>; // For http transport
  command?: string; // Mandatory for stdio transport
  args?: string[]; // For stdio transport
  env?: Record<string, string>; // Environment variables for stdio transport
  timeout?: number; // Connection timeout in seconds
}

/**
 * Configuration for proxy MCP server
 * When enabled, bridge launches an HTTP MCP server that forwards requests
 * to upstream server without exposing original auth tokens
 */
export interface ProxyConfig {
  host: string; // Host to bind proxy server (default: 127.0.0.1)
  port: number; // Port to bind proxy server
}

/**
 * Session status
 * - active: Session is healthy and can be used
 * - connecting: Bridge is starting up for the first time (initial connect in progress)
 * - reconnecting: Bridge crashed and is being automatically restarted
 * - unauthorized: Server rejected authentication (401/403) or token refresh failed. Recovery: login then restart.
 * - expired: Server indicated session is no longer valid (e.g., 404 response). Recovery: restart.
 * - crashed: Bridge process crashed, session might or might not be usable. Bridge will be restarted on next command.
 */
export type SessionStatus =
  | 'active'
  | 'connecting'
  | 'reconnecting'
  | 'unauthorized'
  | 'expired'
  | 'crashed';

/**
 * The connection's session model — whether the MCP connection carries server-side session state.
 * - stateful: a persistent connection — a stdio child process, or a Streamable HTTP
 *   server that assigned a session id (legacy `initialize`, or the optional
 *   `sessions/create` in 2026-07-28+), in which case it is resumable via `MCP-Session-Id`.
 * - stateless: a Streamable HTTP server that assigned no session id (the `2026-07-28`
 *   model where any request may hit any server instance). Not resumable; cannot "expire".
 * - unknown: not yet determined (e.g. before the first successful connect).
 * Derived at connect time from the transport and the presence of a session id.
 */
export type ConnectionMode = 'stateful' | 'stateless' | 'unknown';

/**
 * Notification timestamps for list change events
 * Tracks when the server last notified about changes to tools, prompts, or resources
 */
export interface SessionNotifications {
  tools?: {
    listChangedAt?: string; // ISO 8601 timestamp of last tools/list_changed notification
  };
  prompts?: {
    listChangedAt?: string; // ISO 8601 timestamp of last prompts/list_changed notification
  };
  resources?: {
    listChangedAt?: string; // ISO 8601 timestamp of last resources/list_changed notification
  };
}

/**
 * Session data stored in sessions.json
 */
export interface SessionData {
  name: string;
  server: ServerConfig; // Transport configuration (header values redacted to "<redacted>")
  profileName?: string; // Name of auth profile (for OAuth servers)
  /**
   * x402 auto-payment scheme preference. Presence enables x402 for the session;
   * the value is the preference (`auto` = prefer upto, fall back to exact).
   * Absent / undefined means x402 is disabled.
   */
  x402?: X402SchemePreference;
  insecure?: boolean; // Skip TLS certificate verification
  pid?: number; // Bridge process PID
  protocolVersion?: string; // Negotiated MCP version
  mcpSessionId?: string; // Server-assigned MCP session ID for resumption (stateful Streamable HTTP only)
  connectionMode?: ConnectionMode; // Whether the connection carries server-side session state (derived at connect)
  serverInfo?: {
    name: string;
    version: string;
  };
  status?: SessionStatus; // Session health status (default: active)
  proxy?: ProxyConfig; // Proxy server configuration (if enabled)
  notifications?: SessionNotifications; // Last list change notification timestamps
  activeTasks?: Record<string, ActiveTaskEntry>; // Active async tasks for crash recovery
  // Timestamps (ISO 8601 strings)
  createdAt: string; // When the session was created
  lastSeenAt?: string; // Last successful server response (ping, command, etc.)
  lastConnectionAttemptAt?: string; // Last connection/reconnection attempt (ISO 8601, for cooldown)
}

/**
 * Entry for an active async task persisted for crash recovery
 */
export interface ActiveTaskEntry {
  taskId: string;
  toolName: string;
  createdAt: string;
}

/**
 * Sessions storage structure (sessions.json)
 */
export interface SessionsStorage {
  sessions: Record<string, SessionData>; // sessionName -> SessionData
}

/**
 * Authentication profile data stored in ~/.mcpc/profiles.json
 * Only OAuth authentication is supported for profiles
 * NOTE: Tokens are stored securely in OS keychain, not in this file
 */
export interface AuthProfile {
  name: string;
  serverUrl: string;
  authType: 'oauth';
  // OAuth metadata
  oauthIssuer: string;
  scopes?: string[];
  // User info (from OIDC id_token, if available)
  userEmail?: string;
  userName?: string;
  userSubject?: string; // 'sub' claim - unique user identifier
  // Timestamps (ISO 8601 strings)
  createdAt: string;
  authenticatedAt?: string; // Last time the token was successfully used for authentication
  refreshedAt?: string; // Last time the token was refreshed
}

/**
 * Auth profiles storage structure (~/.mcpc/profiles.json)
 */
export interface AuthProfilesStorage {
  profiles: Record<string, Record<string, AuthProfile>>; // serverUrl -> profileName -> AuthProfile
}

/**
 * IPC message types for CLI-bridge communication
 */
export type IpcMessageType =
  | 'request'
  | 'response'
  | 'shutdown'
  | 'task-update'
  | 'set-auth-credentials'
  | 'set-x402-wallet';

/**
 * Auth credentials sent from CLI to bridge via IPC
 * Supports both OAuth (with refresh token) and HTTP headers
 */
export interface AuthCredentials {
  serverUrl: string;
  profileName: string;
  // OAuth credentials (for refresh flow)
  clientId?: string;
  refreshToken?: string;
  // OAuth access token (used as static Bearer token when no refresh token available)
  accessToken?: string;
  // HTTP headers (from --header flags, stored in keychain)
  headers?: Record<string, string>;
  // Bearer token the bridge's proxy server requires (from --proxy-bearer-token).
  // Read by the CLI before spawn and delivered via IPC so the bridge never reads it
  // from the keychain itself — keeping the bridge's only keychain access on the
  // sanctioned OAuth-refresh path (see #55).
  proxyBearerToken?: string;
}

/**
 * x402 wallet credentials sent from CLI to bridge via IPC
 */
export interface X402WalletCredentials {
  address: string;
  privateKey: string; // Hex with 0x prefix
}

/**
 * Task status update sent from bridge to CLI during task-augmented tool calls
 */
export interface TaskUpdate {
  taskId: string;
  status: 'working' | 'input_required' | 'completed' | 'failed' | 'cancelled';
  statusMessage?: string;
  progressMessage?: string; // Message from notifications/progress
  progress?: number; // Current progress value from notifications/progress
  progressTotal?: number; // Total progress value from notifications/progress
  createdAt?: string;
  lastUpdatedAt?: string;
}

/**
 * IPC message structure
 */
export interface IpcMessage {
  type: IpcMessageType;
  id?: string; // Request ID for correlation
  method?: string; // MCP method name
  params?: unknown; // Method parameters
  timeout?: number; // Per-request timeout in seconds (overrides default)
  result?: unknown; // Response result
  taskUpdate?: TaskUpdate; // Task progress update (for type='task-update')
  authCredentials?: AuthCredentials; // Auth credentials (for type='set-auth-credentials')
  x402Wallet?: X402WalletCredentials; // x402 wallet (for type='set-x402-wallet')
  error?: {
    code: number;
    message: string;
    data?: unknown;
  };
}

/**
 * Output format modes
 */
export type OutputMode = 'human' | 'json';

/**
 * Standard options passed to command handlers
 */
export interface CommandOptions {
  outputMode: OutputMode;
  config?: string;
  headers?: string[];
  timeout?: number;
  verbose?: boolean;
  insecure?: boolean; // Skip TLS certificate verification (for self-signed certs)
  hideTarget?: boolean; // Suppress session info prefix
  schema?: string; // Path to expected schema file for validation
  schemaMode?: 'strict' | 'compatible' | 'ignore'; // Schema validation mode
  maxChars?: number; // Maximum output characters for tool/prompt results (truncate if exceeded)
}

/**
 * Log levels matching MCP SDK
 */
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';

/**
 * Configuration file format (compatible with Claude Desktop)
 */
export interface McpConfig {
  mcpServers: Record<string, ServerConfig>;
}

/**
 * x402 wallet data stored in ~/.mcpc/wallets.json
 * Only a single wallet is supported (no names needed)
 */
export interface WalletData {
  address: string;
  privateKey: string; // Hex string starting with 0x
  createdAt: string; // ISO 8601
}

/**
 * Wallets storage structure (~/.mcpc/wallets.json)
 * Versioned for future migration (e.g. multi-wallet support)
 */
export interface WalletsStorage {
  version: 1;
  wallet?: WalletData;
}

/**
 * Combined server details returned by getServerDetails()
 * Structure matches MCP InitializeResult for consistency
 * Fetched once during initialization, cached locally
 */
export interface ServerDetails {
  /** Negotiated protocol version */
  protocolVersion?: string;
  /** Server capabilities */
  capabilities?: ServerCapabilities;
  /** Server implementation details (name, version, etc.) - matches MCP serverInfo field */
  serverInfo?: Implementation;
  /** Server-provided instructions for the client */
  instructions?: string;
  /** Whether the connection carries server-side session state (derived from transport + session id) */
  connectionMode?: ConnectionMode;
}

/**
 * Common interface for MCP clients
 * Both McpClient (direct SDK wrapper) and SessionClient (bridge IPC wrapper) implement this
 *
 * Note: Server info methods return Promises to accommodate SessionClient's IPC calls.
 * McpClient wraps synchronous SDK calls in resolved Promises for consistency.
 */
export interface IMcpClient {
  // Connection management
  close(): Promise<void>;

  // Server information (capabilities, instructions, version etc.)
  // single call returns all info to avoid multiple IPC roundtrips)
  getServerDetails(): Promise<ServerDetails>;

  // MCP operations
  ping(): Promise<void>;
  listTools(cursor?: string): Promise<ListToolsResult>;
  listAllTools(options?: { refreshCache?: boolean }): Promise<ListToolsResult>;
  callTool(
    name: string,
    args?: Record<string, unknown>,
    meta?: Record<string, unknown>
  ): Promise<CallToolResult>;
  listResources(cursor?: string): Promise<ListResourcesResult>;
  listResourceTemplates(cursor?: string): Promise<ListResourceTemplatesResult>;
  readResource(uri: string): Promise<ReadResourceResult>;
  subscribeResource(uri: string): Promise<void>;
  unsubscribeResource(uri: string): Promise<void>;
  listPrompts(cursor?: string): Promise<ListPromptsResult>;
  getPrompt(name: string, args?: Record<string, string>): Promise<GetPromptResult>;
  setLoggingLevel(level: LoggingLevel): Promise<void>;

  // Task operations (async tool execution)
  callToolWithTask(
    name: string,
    args?: Record<string, unknown>,
    onUpdate?: (update: TaskUpdate) => void,
    meta?: Record<string, unknown>
  ): Promise<CallToolResult>;
  callToolDetached(
    name: string,
    args?: Record<string, unknown>,
    meta?: Record<string, unknown>
  ): Promise<TaskUpdate>;
  pollTask(taskId: string, onUpdate?: (update: TaskUpdate) => void): Promise<CallToolResult>;
  listTasks(cursor?: string): Promise<ListTasksResult>;
  getTask(taskId: string): Promise<GetTaskResult>;
  getTaskResult(taskId: string): Promise<CallToolResult>;
  cancelTask(taskId: string): Promise<CancelTaskResult>;
}
