import {
  createPlannedInput,
  createPlannedOutput,
  diagnostic,
  type SourceReference,
} from './command-utils';
import { freezeCommandPlan, type FFmpegCommandPlan } from './command-plan';

export interface WaveformOptions {
  readonly jobId: string;
  readonly source: SourceReference;
  /** Absolute ffprobe stream index. */
  readonly audioStreamIndex: number;
  readonly sampleRate?: number;
  readonly expectedDurationSeconds?: number;
}

export interface StaticWaveformOptions {
  readonly jobId: string;
  readonly source: SourceReference;
  /** Absolute ffprobe stream index. */
  readonly audioStreamIndex: number;
  readonly width?: number;
  readonly height?: number;
  readonly color?: string;
  readonly background?: string;
  readonly expectedDurationSeconds?: number;
}

export function buildStaticWaveformPlan(
  options: StaticWaveformOptions
): FFmpegCommandPlan {
  const width = options.width ?? 1200;
  const height = options.height ?? 240;
  if (
    !Number.isSafeInteger(width) ||
    !Number.isSafeInteger(height) ||
    width < 64 ||
    height < 32 ||
    width > 8192 ||
    height > 8192
  ) {
    throw new RangeError(
      'Static waveform dimensions must be integers from 64×32 through 8192×8192'
    );
  }
  if (
    !Number.isSafeInteger(options.audioStreamIndex) ||
    options.audioStreamIndex < 0
  ) {
    throw new RangeError('Invalid audio stream index');
  }
  const color = options.color ?? '#62d6c8';
  const background = options.background ?? '#101b22';
  if (
    !/^(?:#[0-9a-f]{6}|[a-z]{3,20})$/iu.test(color) ||
    !/^(?:#[0-9a-f]{6}|[a-z]{3,20})$/iu.test(background)
  ) {
    throw new TypeError('Waveform colours must be named colours or #RRGGBB');
  }
  const input = createPlannedInput(options.jobId, options.source);
  const output = createPlannedOutput(options.jobId, {
    baseName: options.source.fileName,
    operation: 'waveform',
    suffix: 'static',
    extension: 'png',
    role: 'analysis',
    mediaType: 'image/png',
  });
  return freezeCommandPlan({
    id: `${options.jobId}:static-waveform`,
    operation: 'waveform-static',
    inputs: [input],
    temporaryFiles: [],
    args: [
      '-i',
      input.path,
      '-filter_complex',
      `color=c=${background}:s=${width}x${height}:d=1[background];[0:${options.audioStreamIndex}]aformat=channel_layouts=mono,showwavespic=s=${width}x${height}:colors=${color}[foreground];[background][foreground]overlay=shortest=1[waveform]`,
      '-map',
      '[waveform]',
      '-frames:v',
      '1',
      output.path,
    ],
    outputs: [output],
    ...(options.expectedDurationSeconds !== undefined
      ? { expectedDurationSeconds: options.expectedDurationSeconds }
      : {}),
    requiredCapabilities: {
      filters: ['color', 'aformat', 'showwavespic', 'overlay'],
      encoders: ['png'],
      muxers: ['image2'],
    },
    diagnostics: [
      diagnostic(
        'static-waveform',
        'info',
        'A bounded PNG waveform is generated from the selected local audio stream.'
      ),
    ],
  });
}

export function buildWaveformAnalysisPlan(
  options: WaveformOptions
): FFmpegCommandPlan {
  const sampleRate = options.sampleRate ?? 8_000;
  if (
    !Number.isSafeInteger(sampleRate) ||
    sampleRate < 1_000 ||
    sampleRate > 48_000
  ) {
    throw new RangeError(
      'Waveform sample rate must be between 1000 and 48000 Hz'
    );
  }
  const streamIndex = options.audioStreamIndex;
  if (!Number.isSafeInteger(streamIndex) || streamIndex < 0) {
    throw new RangeError('Invalid audio stream index');
  }
  const input = createPlannedInput(options.jobId, options.source);
  const output = createPlannedOutput(options.jobId, {
    baseName: options.source.fileName,
    operation: 'waveform',
    extension: 'wav',
    role: 'analysis',
    mediaType: 'audio/wav',
  });
  return freezeCommandPlan({
    id: `${options.jobId}:waveform`,
    operation: 'waveform-analysis',
    inputs: [input],
    temporaryFiles: [],
    args: [
      '-i',
      input.path,
      '-map',
      `0:${streamIndex}`,
      '-vn',
      '-ac',
      '1',
      '-ar',
      String(sampleRate),
      '-c:a',
      'pcm_s16le',
      '-progress',
      'pipe:1',
      '-nostats',
      output.path,
    ],
    outputs: [output],
    ...(options.expectedDurationSeconds !== undefined
      ? { expectedDurationSeconds: options.expectedDurationSeconds }
      : {}),
    requiredCapabilities: { encoders: ['pcm_s16le'], muxers: ['wav'] },
    diagnostics: [
      diagnostic(
        'reduced-analysis-audio',
        'info',
        'Waveform analysis uses a reduced-rate mono PCM derivative; source media is unchanged.'
      ),
    ],
  });
}
