import type { FFmpeg, FileData, LogEvent, ProgressEvent } from '@ffmpeg/ffmpeg';

export type EngineMode = 'multithread' | 'single-thread';
export type EngineInputMode = 'workerfs' | 'memory-copy';
export type EnginePreference =
  'automatic' | 'prefer-multithread' | 'force-single-thread';

export interface EngineEnvironment {
  secureContext: boolean;
  crossOriginIsolated: boolean;
  sharedArrayBuffer: boolean;
  hardwareConcurrency?: number;
}

export interface FFmpegCapabilities {
  versionText: string;
  buildConfiguration: string[];
  demuxers: Set<string>;
  muxers: Set<string>;
  decoders: Set<string>;
  encoders: Set<string>;
  filters: Set<string>;
}

export interface SerializableFFmpegCapabilities {
  versionText: string;
  buildConfiguration: string[];
  demuxers: string[];
  muxers: string[];
  decoders: string[];
  encoders: string[];
  filters: string[];
}

export interface EngineWarning {
  code: 'multithread-unavailable' | 'multithread-load-failed';
  message: string;
}

export type EngineState =
  | { status: 'idle' }
  | {
      status: 'loading';
      mode: EngineMode;
      progress?: number;
      stage?: string;
    }
  | {
      status: 'ready';
      mode: EngineMode;
      capabilities: FFmpegCapabilities;
      warning?: EngineWarning;
    }
  | {
      status: 'running';
      mode: EngineMode;
      jobId: string;
      operation: string;
      inputMode: EngineInputMode;
      inputBytes: number;
      copiedInputBytes: number;
      progress?: number;
      elapsedSeconds?: number;
      speed?: number;
      frame?: number;
    }
  | {
      status: 'recovering';
      previousMode: EngineMode;
      warning?: EngineWarning;
    }
  | { status: 'error'; error: EngineErrorShape };

export interface EngineErrorShape {
  code:
    | 'unsupported-environment'
    | 'load-failed'
    | 'execution-failed'
    | 'probe-failed'
    | 'cancelled'
    | 'busy'
    | 'filesystem-failed';
  message: string;
  details?: string;
  exitCode?: number;
}

export interface EngineOutputRequest {
  name: string;
  mimeType: string;
}

export interface EngineJobContext {
  inputPaths: readonly string[];
  temporaryPaths: readonly string[];
  outputPaths: readonly string[];
  workDirectory: string;
}

export interface EngineTemporaryFileRequest {
  name: string;
  content:
    string | Uint8Array | ((context: EngineJobContext) => string | Uint8Array);
}

export interface EngineJobRequest {
  id: string;
  /** Human-readable operation shown in the engine state. */
  operation: string;
  /** Stable typed plan operation used for core-lifecycle workload policy. */
  coreWorkload?: string;
  inputs: readonly File[];
  temporaryFiles?: readonly EngineTemporaryFileRequest[];
  outputs: readonly EngineOutputRequest[];
  buildArguments: (context: EngineJobContext) => readonly string[];
  expectedDurationSeconds?: number;
  timeoutMilliseconds?: number;
}

export interface EngineJobOutput {
  name: string;
  mimeType: string;
  bytes: Uint8Array;
}

export interface EngineJobTimings {
  /** Time spent inside FFmpeg's exec call. */
  conversionMilliseconds: number;
  /** Time spent copying expected outputs out of the WebAssembly filesystem. */
  outputReadMilliseconds: number;
  /** Time spent deleting temporary paths and unmounting the job filesystem. */
  cleanupMilliseconds: number;
}

export interface EngineJobResult {
  id: string;
  exitCode: 0;
  inputMode: EngineInputMode;
  inputBytes: number;
  copiedInputBytes: number;
  outputs: readonly EngineJobOutput[];
  logs: readonly LogEvent[];
  timings: EngineJobTimings;
}

export interface ProbeResult {
  json: unknown;
  inputMode: EngineInputMode;
  inputBytes: number;
  copiedInputBytes: number;
  logs: readonly LogEvent[];
}

export interface FFmpegAdapter extends Pick<
  FFmpeg,
  | 'loaded'
  | 'load'
  | 'exec'
  | 'ffprobe'
  | 'terminate'
  | 'on'
  | 'off'
  | 'writeFile'
  | 'mount'
  | 'unmount'
  | 'readFile'
  | 'listDir'
  | 'deleteFile'
  | 'createDir'
  | 'deleteDir'
> {
  readFile(path: string, encoding?: string): Promise<FileData>;
}

export type EngineStateListener = (state: EngineState) => void;
export type EngineLogListener = (event: LogEvent) => void;
export type EngineProgressListener = (event: ProgressEvent) => void;
