import type { PresetRequirements } from '../commands/command-plan';
import { AUDIO_PRESETS } from './audio-presets';
import type {
  BuiltInExportPreset,
  ExportPresetBase,
  FFmpegCapabilitySnapshot,
  PresetAvailability,
  UserExportPreset,
} from './preset.types';
import { VIDEO_PRESETS } from './video-presets';

export const BUILT_IN_PRESETS: readonly BuiltInExportPreset[] = Object.freeze([
  ...VIDEO_PRESETS,
  ...AUDIO_PRESETS,
]);

export function findBuiltInPreset(id: string): BuiltInExportPreset | undefined {
  return BUILT_IN_PRESETS.find((preset) => preset.id === id);
}

export function presetAvailability(
  preset: Pick<ExportPresetBase, 'requirements'>,
  capabilities: FFmpegCapabilitySnapshot,
  degradedWhenMissing: PresetRequirements = {}
): PresetAvailability {
  const missing = missingRequirements(preset.requirements, capabilities);
  if (missing.length > 0) {
    return {
      status: 'unavailable',
      reasons: Object.freeze(
        missing.map(({ kind, name }) => `Missing ${kind}: ${name}`)
      ),
    };
  }
  const degraded = missingRequirements(degradedWhenMissing, capabilities);
  if (degraded.length > 0) {
    return {
      status: 'degraded',
      reasons: Object.freeze(
        degraded.map(
          ({ kind, name }) => `Optional ${kind} unavailable: ${name}`
        )
      ),
    };
  }
  return { status: 'available', reasons: Object.freeze([]) };
}

export function missingRequirements(
  requirements: PresetRequirements,
  capabilities: FFmpegCapabilitySnapshot
): readonly {
  readonly kind: keyof PresetRequirements;
  readonly name: string;
}[] {
  const result: { kind: keyof PresetRequirements; name: string }[] = [];
  const kinds = ['muxers', 'encoders', 'decoders', 'filters'] as const;
  for (const kind of kinds) {
    for (const name of requirements[kind] ?? []) {
      if (!capabilities[kind].has(name)) {
        result.push({ kind, name });
      }
    }
  }
  return Object.freeze(result);
}

export function presetEncodingArguments(
  preset: Pick<UserExportPreset, 'video' | 'audio' | 'fastStart'>
): readonly string[] {
  const args: string[] = [];
  if (preset.video) {
    args.push('-c:v', preset.video.codec);
    if (preset.video.crf !== undefined) {
      if (preset.video.codec === 'libwebp_anim') {
        args.push('-quality', String(preset.video.crf));
      } else if (preset.video.codec !== 'gif') {
        args.push('-crf', String(preset.video.crf));
      }
    }
    if (
      preset.video.bitrateKbps !== undefined &&
      preset.video.bitrateKbps > 0
    ) {
      args.push('-b:v', `${preset.video.bitrateKbps}k`);
    }
    if (preset.video.pixelFormat) {
      args.push('-pix_fmt', preset.video.pixelFormat);
    }
    if (preset.video.encoderPreset && preset.video.codec === 'libx264') {
      args.push('-preset', preset.video.encoderPreset);
    }
    if (preset.video.frameRate) {
      args.push('-r', String(preset.video.frameRate));
    }
    if (preset.video.codec === 'libwebp_anim') {
      args.push('-loop', '0');
    }
  } else {
    args.push('-vn');
  }

  if (preset.audio) {
    args.push('-c:a', preset.audio.codec);
    if (preset.audio.bitrateKbps !== undefined) {
      args.push('-b:a', `${preset.audio.bitrateKbps}k`);
    }
    if (preset.audio.sampleRate !== undefined) {
      args.push('-ar', String(preset.audio.sampleRate));
    }
    if (preset.audio.channels !== undefined) {
      args.push('-ac', String(preset.audio.channels));
    }
  } else {
    args.push('-an');
  }
  if (preset.fastStart) {
    args.push('-movflags', '+faststart');
  }
  return Object.freeze(args);
}
