import type { ExportPreset } from './convert';
import { buildConvertPlan } from './convert';
import {
  createPlannedInput,
  createPlannedOutput,
  diagnostic,
  normalizeSeconds,
  streamMapArguments,
  validateRange,
  type SourceReference,
  type StreamSelection,
} from './command-utils';
import { freezeCommandPlan, type FFmpegCommandPlan } from './command-plan';

export interface TrimOptions {
  readonly jobId: string;
  readonly source: SourceReference;
  readonly mode: 'fast' | 'accurate';
  readonly startSeconds: number;
  readonly endSeconds: number;
  readonly sourceDurationSeconds?: number;
  readonly streamSelection?: StreamSelection;
  readonly preset?: ExportPreset;
  readonly targetExtension?: string;
}

export function buildTrimPlan(options: TrimOptions): FFmpegCommandPlan {
  validateRange(
    options.startSeconds,
    options.endSeconds,
    options.sourceDurationSeconds
  );
  const expectedDurationSeconds = options.endSeconds - options.startSeconds;
  if (options.mode === 'accurate') {
    if (!options.preset) {
      throw new TypeError('Accurate trim requires an export preset');
    }
    const base = buildConvertPlan({
      jobId: options.jobId,
      source: options.source,
      preset: options.preset,
      ...(options.streamSelection
        ? { streamSelection: options.streamSelection }
        : {}),
      expectedDurationSeconds,
      operation: 'trim',
    });
    const inputIndex = base.args.indexOf('-i');
    const args = [...base.args];
    args.splice(
      inputIndex,
      0,
      '-ss',
      normalizeSeconds(options.startSeconds),
      '-to',
      normalizeSeconds(options.endSeconds)
    );
    return freezeCommandPlan({
      ...base,
      args,
      diagnostics: [
        diagnostic(
          'accurate-trim',
          'info',
          'Accurate trim decodes and re-encodes selected streams at the requested boundaries.'
        ),
      ],
    });
  }

  if (!options.targetExtension) {
    throw new TypeError('Fast trim requires a targetExtension');
  }
  const input = createPlannedInput(options.jobId, options.source);
  const output = createPlannedOutput(options.jobId, {
    baseName: options.source.fileName,
    operation: 'trim',
    extension: options.targetExtension,
  });
  const args: string[] = [
    '-ss',
    normalizeSeconds(options.startSeconds),
    '-to',
    normalizeSeconds(options.endSeconds),
    '-i',
    input.path,
  ];
  if (options.streamSelection) {
    args.push(...streamMapArguments(options.streamSelection));
  } else {
    args.push('-map', '0');
  }
  args.push('-c', 'copy', '-progress', 'pipe:1', '-nostats', output.path);
  return freezeCommandPlan({
    id: `${options.jobId}:trim`,
    operation: 'trim-fast',
    inputs: [input],
    temporaryFiles: [],
    args,
    outputs: [output],
    expectedDurationSeconds,
    requiredCapabilities: {},
    diagnostics: [
      diagnostic(
        'keyframe-boundary',
        'warning',
        'Fast trim uses stream copy and can begin at a nearby keyframe rather than the exact requested frame.'
      ),
    ],
  });
}
