/**
 * @fileoverview ExecutionKernel Interface definition
 * @description Define the core execution engine interface of Record-Replay V3
 */

import type { JsonObject } from '../../domain/json';
import type { FlowId, NodeId, RunId } from '../../domain/ids';
import type { RRError } from '../../domain/errors';
import type { FlowV3 } from '../../domain/flow';
import type { DebuggerCommand, DebuggerState } from '../../domain/debug';
import type { RunEvent, RunStatus, Unsubscribe } from '../../domain/events';

/**
 * Run Start request
 */
export interface RunStartRequest {
  /** Run ID（generated by the caller) */
  runId: RunId;
  /** Flow ID */
  flowId: FlowId;
  /** Flow Snapshot (the complete Flow definition used during execution) */
  flowSnapshot: FlowV3;
  /** Operating parameters */
  args?: JsonObject;
  /** Starting node ID (default is Flow's entryNodeId) */
  startNodeId?: NodeId;
  /** Stop before this node is executed, used for segment validation. */
  stopBeforeNodeId?: NodeId;
  /** Stop after this node succeeds or is skipped, used for segment validation. */
  endNodeId?: NodeId;
  /** Tab ID（Must be allocated by the caller, exclusive per Run) */
  tabId: number;
  /** Debug configuration */
  debug?: { breakpoints?: NodeId[]; pauseOnStart?: boolean };
}

/**
 * Run Execution result
 */
export interface RunResult {
  /** Run ID */
  runId: RunId;
  /** final state */
  status: Extract<RunStatus, 'succeeded' | 'failed' | 'canceled' | 'stopped_at_boundary'>;
  /** Total time spent (milliseconds) */
  tookMs: number;
  /** Error message (if failure) */
  error?: RRError;
  /** Output results */
  outputs?: JsonObject;
  /** Segment boundary reached by a stopped_at_boundary run. */
  boundary?: { kind: 'stopBeforeNode'; nodeId: NodeId } | { kind: 'endNode'; nodeId: NodeId };
}

/**
 * Run Status query results
 */
export interface RunStatusInfo {
  /** Current status */
  status: RunStatus;
  /** Current node ID */
  currentNodeId?: NodeId;
  /** start time */
  startedAt?: number;
  /** Last updated */
  updatedAt: number;
  /** Tab ID */
  tabId?: number;
}

/**
 * ExecutionKernel interface
 * @description Record-Replay V3 core execution engine
 */
export interface ExecutionKernel {
  /**
   * Subscribe to event stream
   * @param listener event listener
   * @returns unsubscribe function
   */
  onEvent(listener: (event: RunEvent) => void): Unsubscribe;

  /**
   * Start Run
   * @description Queue Run and start execution
   */
  startRun(req: RunStartRequest): Promise<void>;

  /**
   * Pause Run
   * @param runId Run ID
   * @param reason Reason for suspension
   */
  pauseRun(runId: RunId, reason?: { kind: 'command' }): Promise<void>;

  /**
   * Resume Run
   * @param runId Run ID
   */
  resumeRun(runId: RunId): Promise<void>;

  /**
   * Cancel Run
   * @param runId Run ID
   * @param reason Reason for cancellation
   */
  cancelRun(runId: RunId, reason?: string): Promise<void>;

  /**
   * Execute debugging command
   * @param runId Run ID
   * @param cmd debug command
   */
  debug(
    runId: RunId,
    cmd: DebuggerCommand,
  ): Promise<{ ok: true; state?: DebuggerState } | { ok: false; error: string }>;

  /**
   * Get Run status
   * @param runId Run ID
   * @returns Run status information or null if not present
   */
  getRunStatus(runId: RunId): Promise<RunStatusInfo | null>;

  /**
   * Resume execution
   * @description Called after the Service Worker restarts to resume the interrupted run
   */
  recover(): Promise<void>;
}

/**
 * Create NotImplemented ExecutionKernel
 * @description Phase 0 Placeholder implementation
 */
export function createNotImplementedKernel(): ExecutionKernel {
  const notImplemented = () => {
    throw new Error('ExecutionKernel not implemented');
  };

  return {
    onEvent: () => {
      notImplemented();
      return () => {};
    },
    startRun: async () => notImplemented(),
    pauseRun: async () => notImplemented(),
    resumeRun: async () => notImplemented(),
    cancelRun: async () => notImplemented(),
    debug: async () => notImplemented(),
    getRunStatus: async () => notImplemented(),
    recover: async () => notImplemented(),
  };
}
