import type { FFmpegCommandPlan } from '../commands/command-plan';

export type MediaJobStatus =
  | 'queued'
  | 'preparing'
  | 'running'
  | 'reading-output'
  | 'cancelling'
  | 'recovering'
  | 'completed'
  | 'failed'
  | 'cancelled';

export type MediaJobStepStatus =
  'queued' | 'running' | 'completed' | 'failed' | 'cancelled';

export interface MediaJobError {
  readonly code: string;
  readonly message: string;
  readonly stepId?: string;
  readonly recoverable: boolean;
  readonly details?: string;
}

export interface MediaJobStep {
  readonly id: string;
  readonly name: string;
  readonly weight: number;
  readonly status: MediaJobStepStatus;
  readonly progress: number;
  readonly planId: string;
}

export interface MediaJob {
  readonly id: string;
  readonly operation: string;
  readonly status: MediaJobStatus;
  readonly steps: readonly MediaJobStep[];
  readonly progress: number;
  readonly createdAt: string;
  readonly updatedAt: string;
  readonly startedAt?: string;
  readonly completedAt?: string;
  readonly retryCount: number;
  readonly error?: MediaJobError;
  readonly recoveryNote?: string;
}

export interface MediaJobDefinition {
  readonly id: string;
  readonly operation: string;
  readonly steps: readonly {
    readonly id: string;
    readonly name: string;
    readonly weight: number;
    readonly plan: FFmpegCommandPlan;
  }[];
}

export interface JobExecutionResult {
  readonly outputs: ReadonlyMap<string, Blob | Uint8Array>;
  readonly diagnostics?: readonly string[];
}

export interface JobProgressUpdate {
  readonly progress?: number;
  readonly elapsedSeconds?: number;
  readonly speed?: number;
  readonly frame?: number;
  readonly message?: string;
}

export interface JobExecutionContext {
  readonly signal: AbortSignal;
  readonly onProgress: (update: JobProgressUpdate) => void;
  readonly onLog: (message: string) => void;
}

/**
 * Engine-facing interface. Cancellation must terminate the active FFmpeg
 * worker; aborting the signal alone is deliberately insufficient.
 */
export interface MediaJobExecutor {
  prepare?(job: MediaJob, signal: AbortSignal): Promise<void>;
  execute(
    plan: FFmpegCommandPlan,
    context: JobExecutionContext
  ): Promise<JobExecutionResult>;
  readOutputs?(
    plan: FFmpegCommandPlan,
    result: JobExecutionResult,
    signal: AbortSignal
  ): Promise<JobExecutionResult>;
  cleanup(jobId: string): Promise<void>;
  cancelCurrentJob(): Promise<void>;
  recoverAfterCancellation(): Promise<void>;
}

export interface JobMetadataStore {
  list(): Promise<readonly MediaJob[]>;
  put(job: MediaJob): Promise<void>;
  delete(id: string): Promise<void>;
  clear(): Promise<void>;
}

export interface JobRecoveryProvider {
  restore(job: MediaJob): Promise<MediaJobDefinition | undefined>;
}

export type JobQueueListener = (
  jobs: readonly MediaJob[],
  activeJobId: string | undefined
) => void;
