// Smart Trim (V22.4)
//
// Two-pass automatic timeline trimmer:
//
//   1. Detect silence segments (already implemented in
//      `src/components/timeline/detectSilence.ts` — but we re-implement a
//      simpler client-side version that uses AudioContext directly).
//
//   2. Detect filler words ("อ่า", "เอ่อ", "uh", "um"...) inside the
//      caption stream and merge them with silence gaps.
//
// Output: a list of "remove" segments ready to feed into
// `applySilenceRemoval`. User can preview before commit.

import type { SubtitleCue } from '@/types';

export interface TrimCandidate {
  /** Seconds in source asset. */
  start: number;
  end: number;
  kind: 'silence' | 'filler' | 'silence+filler';
  /** Severity score 0..1 — higher = more confident it should be cut. */
  score: number;
}

// Thai + English filler words. Multi-language list is small because the
// detection is purely substring-based.
const THAI_FILLERS = [
  'อ่า', 'เอ่อ', 'อืม', 'อือ', 'เอ้า', 'อ้าว', 'เออ', 'อ่ะ',
  'แบบว่า', 'แบบ', 'คือ', 'เอ้อ', 'หืม', 'ห่ะ',
];
const ENG_FILLERS = [
  'uh', 'um', 'uhh', 'umm', 'hmm', 'er', 'ah', 'eh',
  'like', 'you know', 'i mean', 'kinda', 'sort of',
];

export interface SmartTrimOptions {
  /** Minimum silence length in seconds to consider trimming (default 0.4). */
  minSilenceSec?: number;
  /** Filler detection sensitivity 0..1 (default 0.5). */
  fillerSensitivity?: number;
  /** Padding around cuts (seconds) — gives a small breath. */
  padSec?: number;
}

/**
 * Detect filler words in a list of subtitle cues.
 * Returns segments that are likely candidates for trimming.
 */
export function detectFillerWords(
  cues: Pick<SubtitleCue, 'start' | 'end' | 'text'>[],
  options: { padSec?: number; sensitivity?: number } = {},
): TrimCandidate[] {
  const pad = options.padSec ?? 0.05;
  const sens = options.sensitivity ?? 0.5;
  const out: TrimCandidate[] = [];
  const allFillers = [...THAI_FILLERS, ...ENG_FILLERS];

  for (const c of cues) {
    const txt = (c.text || '').toLowerCase().trim();
    if (!txt) continue;
    // Single-word filler — only that word → trim entire cue
    const words = txt.split(/\s+/);
    const onlyFillers = words.every((w) =>
      allFillers.some((f) => w === f.toLowerCase()),
    );
    if (onlyFillers) {
      const dur = c.end - c.start;
      // Score scales with filler confidence + duration
      const score = Math.min(1, 0.5 + dur * 0.3 + sens * 0.2);
      out.push({
        start: Math.max(0, c.start + pad),
        end: c.end - pad,
        kind: 'filler',
        score,
      });
      continue;
    }
    // Mixed: contains filler but has content — flag with low score
    const hasFiller = allFillers.some((f) =>
      new RegExp(`(^|\\s)${f}(\\s|$|[.,!?])`, 'i').test(txt),
    );
    if (hasFiller && sens > 0.6) {
      out.push({
        start: c.start + pad,
        end: c.end - pad,
        kind: 'filler',
        score: 0.3,
      });
    }
  }
  return out;
}

/**
 * Combine silence + filler candidates into a final trim list.
 * Overlapping ranges are merged (silence+filler > either alone).
 */
export function combineTrimCandidates(
  silences: TrimCandidate[],
  fillers: TrimCandidate[],
): TrimCandidate[] {
  const all = [...silences, ...fillers].sort((a, b) => a.start - b.start);
  const merged: TrimCandidate[] = [];
  for (const c of all) {
    const last = merged[merged.length - 1];
    if (last && c.start <= last.end) {
      last.end = Math.max(last.end, c.end);
      last.kind =
        last.kind === 'silence' && c.kind === 'filler'
          ? 'silence+filler'
          : last.kind === 'filler' && c.kind === 'silence'
            ? 'silence+filler'
            : last.kind;
      last.score = Math.max(last.score, c.score);
    } else {
      merged.push({ ...c });
    }
  }
  return merged;
}

/** Filter by score threshold + min length. */
export function applyTrimThreshold(
  candidates: TrimCandidate[],
  options: { minScore?: number; minSec?: number } = {},
): TrimCandidate[] {
  const minScore = options.minScore ?? 0.4;
  const minSec = options.minSec ?? 0.15;
  return candidates.filter((c) => c.score >= minScore && c.end - c.start >= minSec);
}

/** Compute total seconds to be cut from the source. */
export function totalCutSeconds(candidates: TrimCandidate[]): number {
  return candidates.reduce((acc, c) => acc + (c.end - c.start), 0);
}