/**
 * @fileoverview Core data models for the OpenClaw Control Center API client.
 *
 * These models bridge OpenClaw Gateway primitives (session keys, status events,
 * tool responses) to the AgentCardData interface consumed by the Angular frontend.
 *
 * Design principle: every model maps 1:1 to a Gateway concept. Transformation
 * to UI-specific shapes (like AgentCardData) happens in the service layer.
 */

// ─── Agent Status ──────────────────────────────────────────────────────────

/**
 * Agent operational status, derived from session activity and Gateway events.
 *
 * Mapping from OpenClaw session `status` field:
 *   - "running"  → ACTIVE   — agent is currently processing a turn
 *   - "done"     → IDLE     — agent completed its last turn, no active work
 *   - "streaming"→ THINKING — LLM call in flight, tokens streaming
 *   - "error"    → ERROR    — agent encountered an unhandled error
 *   - (no session) → IDLE   — no active session exists for this agent
 */
export type AgentStatus = 'active' | 'idle' | 'thinking' | 'error';

/**
 * Extended status including offline — not all agents have active sessions.
 * Used internally; the UI only sees AgentStatus (offline maps to idle).
 */
export type AgentLifecycleStatus = AgentStatus | 'offline';

// ─── Agent Card Data ───────────────────────────────────────────────────────

/**
 * The primary data shape consumed by Agent Card components in the Command Hub.
 * Matches the TypeScript interface from the design spec exactly.
 */
export interface AgentCardData {
  /** Agent identifier, e.g. "otto", "dex", "hex" */
  id: string;

  /** Human-readable name, e.g. "Otto", "Dex" */
  displayName: string;

  /** Role description from agent identity, e.g. "Orchestrator Agent" */
  role: string;

  /** Current operational status */
  status: AgentStatus;

  /** Description of the current task, if any */
  currentTask?: string;

  /** Task progress percentage (0–100), if trackable */
  taskProgress?: number;

  /** Elapsed time string, e.g. "04m 12s" */
  taskElapsed?: string;

  /** Full session key, e.g. "agent:otto:telegram:direct:8787451565" */
  sessionKey: string;

  /** Channel the agent is operating on, e.g. "telegram", "discord" */
  channel: string;

  /** ISO timestamp of last activity */
  lastActivity: string;

  /** Error message when status is 'error' */
  errorMessage?: string;
}

// ─── OpenClaw Gateway Primitives ───────────────────────────────────────────

/**
 * Raw agent configuration from OpenClaw's config (openclaw.json agents.list[]).
 * Not all fields are always present — this is a partial reflection.
 */
export interface OpenClawAgentConfig {
  id: string;
  name?: string;
  emoji?: string;
  avatar?: string;
  model?: string | { primary: string };
  workspace?: string;
  agentDir?: string;
  identity?: {
    name?: string;
    emoji?: string;
    avatar?: string;
    theme?: string;
  };
  routing?: Array<{
    channel: string;
    account?: string;
    peer?: string;
    group?: string;
  }>;
  subagents?: {
    allowAgents?: string[];
    maxChildrenPerAgent?: number;
  };
}

/**
 * Session entry returned by `sessions.list` RPC or `/tools/invoke sessions_list`.
 * This is the raw shape from the Gateway.
 */
export interface OpenClawSession {
  key: string;
  kind: string;
  channel: string;
  origin?: {
    provider: string;
    accountId?: string;
    peerId?: string;
  };
  displayName?: string;
  deliveryContext?: {
    to?: string;
    channel?: string;
    accountId?: string;
    peerId?: string;
  };
  updatedAt: number;       // epoch ms
  sessionId: string;       // UUID
  model?: string;
  modelProvider?: string;
  contextTokens?: number;
  totalTokens?: number;
  totalTokensFresh?: boolean;
  estimatedCostUsd?: number;
  status?: 'done' | 'running' | 'streaming' | 'error' | 'aborted';
  startedAt?: number;      // epoch ms
  endedAt?: number;        // epoch ms
  runtimeMs?: number;
  systemSent?: boolean;
  abortedLastRun?: boolean;
  lastTo?: string;
  label?: string;
  transcriptPath?: string;
  agentId?: string;
  inputTokens?: number;
  outputTokens?: number;
  providerOverride?: string;
  modelOverride?: string;
}

/**
 * Gateway health response from `/health` endpoint.
 */
export interface GatewayHealth {
  ok: boolean;
  status: 'live' | 'stale' | 'down';
}

/**
 * Identity payload for an agent, returned by `agent.identity.get` RPC.
 */
export interface AgentIdentity {
  agentId: string;
  name: string;
  emoji?: string;
  avatar?: string;
  theme?: string;
  description?: string;
}

// ─── WebSocket Protocol Frames ─────────────────────────────────────────────

/**
 * Base frame shape for the OpenClaw Gateway WebSocket protocol v3.
 * - Request:  { type: "req", id, method, params }
 * - Response:  { type: "res", id, ok, payload|error }
 * - Event:    { type: "event", event, payload, seq?, stateVersion? }
 */
export type WsFrame =
  | WsRequest
  | WsResponse
  | WsEvent;

export interface WsRequest {
  type: 'req';
  id: string;
  method: string;
  params: Record<string, unknown>;
}

export interface WsResponse {
  type: 'res';
  id: string;
  ok: boolean;
  payload?: unknown;
  error?: {
    type: string;
    message: string;
    details?: Record<string, unknown>;
  };
}

export interface WsEvent {
  type: 'event';
  event: string;
  payload: unknown;
  seq?: number;
  stateVersion?: number;
}

// ─── Event Payloads ────────────────────────────────────────────────────────

/**
 * Event names emitted by the Gateway that the Command Hub subscribes to.
 */
export type GatewayEventName =
  | 'sessions.changed'
  | 'session.message'
  | 'session.tool'
  | 'presence'
  | 'health'
  | 'tick'
  | 'heartbeat'
  | 'cron'
  | 'shutdown'
  | 'exec.approval.requested'
  | 'exec.approval.resolved'
  | 'node.pair.requested'
  | 'node.pair.resolved'
  | 'chat'
  | 'device.pair.requested'
  | 'device.pair.resolved';

/**
 * sessions.changed event payload — broadcast when the session index updates.
 */
export interface SessionsChangedPayload {
  added?: string[];     // session keys added
  removed?: string[];   // session keys removed
  updated?: string[];   // session keys with metadata changes
  snapshot?: OpenClawSession[];
}

/**
 * session.message event payload — transcript message for a subscribed session.
 */
export interface SessionMessagePayload {
  sessionKey: string;
  role: 'user' | 'assistant' | 'system' | 'tool';
  content: string;
  timestamp: number;
  toolCalls?: ToolCallInfo[];
}

/**
 * session.tool event payload — tool execution event for a subscribed session.
 */
export interface SessionToolPayload {
  sessionKey: string;
  toolName: string;
  status: 'started' | 'completed' | 'error';
  duration?: number;
  result?: string;
  error?: string;
}

/**
 * Presence event payload — devices connected to the gateway.
 */
export interface PresencePayload {
  devices: Array<{
    deviceId: string;
    roles: string[];
    scopes: string[];
    connected: boolean;
    lastSeen?: number;
  }>;
}

/**
 * Health event payload — gateway health snapshot update.
 */
export interface HealthEventPayload {
  ok: boolean;
  status: string;
  uptime?: number;
  agentsOnline?: number;
}

/**
 * Simplified tool call info extracted from transcript events.
 */
export interface ToolCallInfo {
  name: string;
  status: 'started' | 'completed' | 'error';
  args?: Record<string, unknown>;
  result?: string;
  error?: string;
}

// ─── API Request/Response Types ────────────────────────────────────────────

/**
 * Tools invoke request body for the HTTP `/tools/invoke` endpoint.
 */
export interface ToolsInvokeRequest {
  tool: string;
  action?: string;
  args?: Record<string, unknown>;
  sessionKey?: string;
  dryRun?: boolean;
}

/**
 * Tools invoke success response.
 */
export interface ToolsInvokeResponse<T = unknown> {
  ok: true;
  result: {
    content?: Array<{ type: string; text: string }>;
    details?: T;
  };
}

/**
 * Tools invoke error response.
 */
export interface ToolsInvokeErrorResponse {
  ok: false;
  error: {
    type: string;
    message: string;
  };
}

// ─── Connect Handshake ────────────────────────────────────────────────────

/**
 * Connect request params for the WebSocket handshake.
 */
export interface ConnectParams {
  minProtocol: number;
  maxProtocol: number;
  client: {
    id: string;
    version: string;
    platform: string;
    mode: 'operator' | 'node';
  };
  role: 'operator' | 'node';
  scopes: string[];
  caps?: string[];
  commands?: string[];
  permissions?: Record<string, boolean>;
  auth: {
    token?: string;
    password?: string;
    deviceToken?: string;
  };
  locale?: string;
  userAgent?: string;
  device?: {
    id: string;
    publicKey?: string;
    signature?: string;
    signedAt?: number;
    nonce?: string;
  };
}

/**
 * Hello-ok response payload after successful connect.
 */
export interface HelloOkPayload {
  type: 'hello-ok';
  protocol: number;
  policy: {
    tickIntervalMs: number;
  };
  auth?: {
    deviceToken?: string;
    role: string;
    scopes: string[];
    deviceTokens?: Array<{
      deviceToken: string;
      role: string;
      scopes: string[];
    }>;
  };
}

/**
 * Connect challenge event from the gateway.
 */
export interface ConnectChallengePayload {
  nonce: string;
  ts: number;
}

// ─── Agent Role Map ────────────────────────────────────────────────────────

/**
 * Known agent roles. This maps agent IDs to their functional descriptions
 * for display in the Command Hub. Can be overridden by agent identity config.
 */
export const AGENT_ROLES: Record<string, string> = {
  main: 'Primary Assistant',
  otto: 'Orchestrator Agent',
  dave: 'Network Admin Agent',
  bob: 'Content Writer Agent',
  stuart: 'Image & Creative Agent',
  phil: 'Home Automation Agent',
  carl: 'Security Agent',
  larry: 'Business Agent',
  mel: 'E-Commerce Agent',
  norbert: 'Product Agent',
  jerry: 'Market Research Agent',
  rex: 'Frontend Dev Agent',
  dex: 'Backend Dev Agent',
  hex: 'Database Agent',
  pip: 'Raspberry Pi Agent',
  nano: 'ESP32/Firmware Agent',
  axiom: 'Utility Agent',
  bonnie: 'Music Agent',
  sketch: 'UI/UX Design Agent',
  flip: 'Mobile Dev Agent',
  buzz: 'SEO Agent',
  aries: 'Companion Agent',
};