"use client";
import { useEffect, useRef, useState } from "react";
import { ChevronDown, Captions, Sparkles, Loader2, X } from "lucide-react";
import { cn } from "@/lib/utils";

export type LangIso = "eng" | "zho" | "jpn";

export interface EmbeddedEntry {
  id: string;
  streamIndex: number;
  codec: string;
  language: LangIso | null;
  label: string;
  renderable: boolean;
}

export interface SidecarEntry {
  id: string;
  abs: string;
  filename: string;
  ext: string;
  language: LangIso | null;
  label: string;
  origin: "same-folder" | "library" | "browsed" | "manual";
}

export interface SubtitleSources {
  embedded: EmbeddedEntry[];
  sidecar: SidecarEntry[];
}

export interface RunningJobSummary {
  jobId: string;
  /** Wall-clock seconds since startedAt, computed by parent. */
  elapsedSec: number;
  /** "Step 4/6: Transcribing Scenes" or null when not yet known. */
  status: string | null;
  /** Estimated remaining seconds. Null when ETA can't be computed. */
  etaSec: number | null;
}

interface Props {
  sources: SubtitleSources | null;
  selectedId: string | "off";
  loading: boolean;
  onChange: (id: string | "off") => void;
  onBrowse: () => void;
  /** When set, picker hides the Generate row and shows "Generating ..." */
  runningJob?: RunningJobSummary | null;
  /** Called when user clicks Generate Subtitles / Regenerate. */
  onGenerate?: (overwrite: boolean) => void;
  /** Called when user clicks Cancel during a running job. */
  onCancel?: () => void;
  /** True iff WhisperJAV CLI path is configured. Hides Generate row otherwise. */
  generateEnabled?: boolean;
  /** True iff a generated sidecar already exists for the current video.
   *  Switches "Generate Subtitles" → "Regenerate Subtitles" (overwrite=true). */
  hasGenerated?: boolean;
  /** Current timing offset in seconds. Positive = subs appear later. */
  offsetSec?: number;
  /** Called when user changes the offset (delta in seconds, signed). */
  onOffsetChange?: (newOffsetSec: number) => void;
}

function shortLabel(
  sources: SubtitleSources | null,
  selectedId: string | "off",
  loading: boolean,
): string {
  // While the initial source list is in flight the sticky-pref auto-
  // select hasn't fired yet — masking with "..." avoids a misleading
  // "Off" flash that would flip to "EN"/"JP"/etc once the fetch lands.
  if (loading && selectedId === "off") return "...";
  if (selectedId === "off") return "Off";
  if (!sources) return "Subtitles";
  const emb = sources.embedded.find((e) => e.id === selectedId);
  if (emb) return emb.language ? langShort(emb.language) : labelShort(emb.label, "Embedded");
  const side = sources.sidecar.find((s) => s.id === selectedId);
  if (side) return side.language ? langShort(side.language) : labelShort(side.label, "Subtitles");
  return "Subtitles";
}

// For compound-language entries (lang=null, label="English/Japanese")
// derive a short code from the label so the trigger reads "EN" rather
// than the generic fallback.
function labelShort(label: string, fallback: string): string {
  if (/\benglish\b/i.test(label)) return "EN";
  if (/\bchinese\b/i.test(label)) return "CN";
  if (/\bjapanese\b/i.test(label)) return "JP";
  return fallback;
}

function langShort(iso: LangIso): string {
  if (iso === "eng") return "EN";
  if (iso === "zho") return "CN";
  return "JP";
}

/**
 * Subtitle source picker. Trigger always renders with a stable min-width
 * to avoid layout shift when sources resolve. Menu groups Embedded /
 * Sidecar with "Off" and "Browse..." as fixed entries.
 */
export function SubtitlePicker({
  sources,
  selectedId,
  loading,
  onChange,
  onBrowse,
  runningJob,
  onGenerate,
  onCancel,
  generateEnabled,
  hasGenerated,
  offsetSec,
  onOffsetChange,
}: Props) {
  const [open, setOpen] = useState(false);
  const ref = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) return;
    const onDocDown = (e: MouseEvent) => {
      if (!ref.current) return;
      if (!ref.current.contains(e.target as Node)) setOpen(false);
    };
    const onKey = (e: KeyboardEvent) => {
      if (e.key === "Escape") setOpen(false);
    };
    document.addEventListener("mousedown", onDocDown);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("mousedown", onDocDown);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);

  const embedded = sources?.embedded ?? [];
  const sidecar = sources?.sidecar ?? [];
  const hasAny = embedded.length > 0 || sidecar.length > 0;
  const trigger = shortLabel(sources, selectedId, loading);

  return (
    <div ref={ref} className="relative shrink-0">
      <button
        type="button"
        onClick={() => setOpen((v) => !v)}
        title="Subtitles"
        className={cn(
          "inline-flex items-center justify-between gap-1.5 min-w-[140px] text-xs px-3 py-1.5 rounded-lg glass glass-hover",
          open && "bg-[var(--color-glass-strong)]",
        )}
      >
        <span className="inline-flex items-center gap-1.5">
          <Captions className="w-3.5 h-3.5" />
          <span className="font-mono">{trigger}</span>
        </span>
        <ChevronDown className={cn("w-3 h-3 transition-transform", open && "rotate-180")} />
      </button>
      {open && (
        <div
          role="menu"
          className="absolute right-0 top-full mt-1 z-30 min-w-[280px] max-w-[440px] rounded-lg border border-[var(--color-glass-border-strong)] bg-[var(--color-bg-1)] shadow-2xl p-1"
        >
          <MenuItem
            label="Off"
            mono
            active={selectedId === "off"}
            onClick={() => { onChange("off"); setOpen(false); }}
          />

          {embedded.length > 0 && <MenuHeader label="Embedded" />}
          {embedded.map((e) => (
            <MenuItem
              key={e.id}
              label={e.label}
              detail={`#${e.streamIndex}`}
              active={selectedId === e.id}
              disabled={!e.renderable}
              disabledReason="Image Subs Not Supported"
              onClick={() => { onChange(e.id); setOpen(false); }}
            />
          ))}

          {sidecar.length > 0 && <MenuHeader label="Subtitles" />}
          {sidecar.map((s) => (
            <MenuItem
              key={s.id}
              label={s.label}
              detail={s.filename}
              active={selectedId === s.id}
              onClick={() => { onChange(s.id); setOpen(false); }}
            />
          ))}

          {!hasAny && !loading && (
            <div className="px-2 py-2 text-[11px] text-[var(--color-fg-muted)]">
              No subtitles found for this video.
            </div>
          )}
          {loading && !hasAny && (
            <div className="px-2 py-2 text-[11px] text-[var(--color-fg-muted)]">
              Looking for subtitles…
            </div>
          )}

          {selectedId !== "off" && onOffsetChange && (
            <>
              <div className="my-1 border-t border-[var(--color-glass-border)]" />
              <SyncControls
                offsetSec={offsetSec ?? 0}
                onChange={onOffsetChange}
              />
            </>
          )}

          <div className="my-1 border-t border-[var(--color-glass-border)]" />
          <MenuItem
            label="Browse..."
            mono
            onClick={() => { onBrowse(); setOpen(false); }}
          />

          {generateEnabled && !runningJob && (
            <>
              <div className="my-1 border-t border-[var(--color-glass-border)]" />
              <button
                type="button"
                role="menuitem"
                onClick={() => { onGenerate?.(!!hasGenerated); setOpen(false); }}
                className="w-full flex items-center gap-2 text-left text-xs px-2 py-1.5 rounded-md hover:bg-[var(--color-glass)] text-[var(--color-fg)] cursor-pointer"
              >
                <Sparkles className="w-3.5 h-3.5 text-[var(--color-cyan)] shrink-0" />
                <span className="font-mono">{hasGenerated ? "Regenerate Subtitles" : "Generate Subtitles"}</span>
              </button>
            </>
          )}
        </div>
      )}
    </div>
  );
}

function formatElapsed(sec: number): string {
  const s = Math.max(0, Math.floor(sec));
  const m = Math.floor(s / 60);
  const r = s % 60;
  return `${String(m).padStart(2, "0")}:${String(r).padStart(2, "0")}`;
}

function SyncControls({
  offsetSec,
  onChange,
}: {
  offsetSec: number;
  onChange: (next: number) => void;
}) {
  const adjust = (delta: number) => onChange(Math.round((offsetSec + delta) * 100) / 100);
  // Local string so the user can type freely — including transient
  // states like "-" or "1." — without React clobbering it on every
  // keystroke. Commit to the parent on blur or Enter.
  const [draft, setDraft] = useState<string>(() => formatOffset(offsetSec));
  // Re-sync when the parent value changes from outside (e.g. button
  // press, code change). Using a ref to avoid clobbering active typing.
  const lastSyncedRef = useRef(offsetSec);
  useEffect(() => {
    if (lastSyncedRef.current !== offsetSec) {
      setDraft(formatOffset(offsetSec));
      lastSyncedRef.current = offsetSec;
    }
  }, [offsetSec]);

  const commit = () => {
    const parsed = parseFloat(draft);
    if (Number.isFinite(parsed)) {
      const rounded = Math.round(parsed * 100) / 100;
      lastSyncedRef.current = rounded;
      onChange(rounded);
      setDraft(formatOffset(rounded));
    } else {
      // Reject garbage — revert.
      setDraft(formatOffset(offsetSec));
    }
  };

  return (
    <div
      className="px-2 py-2 flex items-center gap-1.5"
      onClick={(e) => e.stopPropagation()}
    >
      <span className="text-[10px] uppercase tracking-wider font-mono text-[var(--color-fg-muted)] mr-1">
        Sync
      </span>
      <SyncBtn label="−1s" onClick={() => adjust(-1)} title="Shift earlier 1s" />
      <SyncBtn label="−.1" onClick={() => adjust(-0.1)} title="Shift earlier 0.1s" />
      <input
        type="text"
        inputMode="decimal"
        value={draft}
        onChange={(e) => setDraft(e.target.value)}
        onBlur={commit}
        onKeyDown={(e) => {
          if (e.key === "Enter") { e.currentTarget.blur(); }
          else if (e.key === "Escape") { setDraft(formatOffset(offsetSec)); e.currentTarget.blur(); }
        }}
        title="Type a value (seconds), press Enter to apply"
        className={cn(
          "w-[60px] text-center text-xs font-mono px-1.5 py-0.5 rounded outline-none border",
          offsetSec !== 0
            ? "text-[var(--color-cyan)] bg-[var(--color-cyan)]/10 border-[var(--color-cyan)]/40"
            : "text-[var(--color-fg-dim)] bg-[var(--color-glass)] border-[var(--color-glass-border)]",
          "focus:border-[var(--color-cyan)] focus:bg-[var(--color-cyan)]/10",
        )}
      />
      <SyncBtn label="+.1" onClick={() => adjust(0.1)} title="Shift later 0.1s" />
      <SyncBtn label="+1s" onClick={() => adjust(1)} title="Shift later 1s" />
      {offsetSec !== 0 && (
        <button
          type="button"
          onClick={() => onChange(0)}
          title="Reset to 0"
          className="ml-1 text-[10px] uppercase tracking-wider font-mono px-1.5 py-0.5 rounded text-[var(--color-fg-dim)] hover:text-[var(--color-fg)] hover:bg-[var(--color-glass)] cursor-pointer"
        >
          Reset
        </button>
      )}
    </div>
  );
}

function formatOffset(sec: number): string {
  if (sec === 0) return "0.0s";
  return `${sec > 0 ? "+" : ""}${sec.toFixed(1)}s`;
}

function SyncBtn({ label, onClick, title }: { label: string; onClick: () => void; title: string }) {
  return (
    <button
      type="button"
      onClick={onClick}
      title={title}
      className="text-xs font-mono px-2 py-0.5 rounded glass glass-hover cursor-pointer"
    >
      {label}
    </button>
  );
}

function MenuHeader({ label }: { label: string }) {
  return (
    <div className="px-2 pt-2 pb-1 text-[10px] uppercase tracking-wider font-mono text-[var(--color-fg-muted)]">
      {label}
    </div>
  );
}

function MenuItem({
  label,
  detail,
  active,
  disabled,
  disabledReason,
  mono,
  onClick,
}: {
  label: string;
  detail?: string;
  active?: boolean;
  disabled?: boolean;
  disabledReason?: string;
  mono?: boolean;
  onClick: () => void;
}) {
  return (
    <button
      type="button"
      role="menuitem"
      disabled={disabled}
      onClick={disabled ? undefined : onClick}
      title={disabled ? disabledReason : detail}
      className={cn(
        "w-full flex items-center gap-2 text-left text-xs px-2 py-1.5 rounded-md",
        disabled
          ? "opacity-40 cursor-not-allowed"
          : active
            ? "bg-[var(--color-cyan)]/15 text-[var(--color-cyan)] cursor-pointer"
            : "hover:bg-[var(--color-glass)] text-[var(--color-fg)] cursor-pointer",
      )}
    >
      <span className={cn("shrink-0", mono && "font-mono")}>{label}</span>
      {detail && (
        <span className="font-mono text-[10px] text-[var(--color-fg-dim)] truncate min-w-0">
          {detail}
        </span>
      )}
    </button>
  );
}
