import { useEffect, useRef, useState } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog';
import { Checkbox } from '@/components/ui/checkbox';
import { Download, Loader2, ChevronLeft, AlertCircle, Check, Music } from 'lucide-react';
import type { VideoInfo } from '../../../../hooks/useFilesAPI';
import type { UseFileBrowserAppType } from '../useFileBrowserApp';

type VideoDownloadDialogProps = {
  fileBrowserManager: UseFileBrowserAppType;
};

type DlPhase = 'idle' | 'downloading' | 'saving' | 'done' | 'error';
type Entry = {
  url: string;
  status: 'loading' | 'ready' | 'error';
  title?: string;
  thumbnail?: string;
  duration?: number;
  uploader?: string;
  error?: string;
  dl: DlPhase;
  dlError?: string;
  filename?: string;
};

const fmtDuration = (sec?: number): string => {
  if (!sec || sec <= 0) return '';
  const s = Math.round(sec);
  const h = Math.floor(s / 3600);
  const m = Math.floor((s % 3600) / 60);
  const ss = String(s % 60).padStart(2, '0');
  return h > 0 ? `${h}:${String(m).padStart(2, '0')}:${ss}` : `${m}:${ss}`;
};

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
// Keep a subfolder name to a single safe path segment.
const sanitizeFolder = (name: string) => name.replace(/[/\\]/g, '').replace(/^\.+/, '').trim();

export const VideoDownloadDialog = ({ fileBrowserManager }: VideoDownloadDialogProps) => {
  const { showVideoDownload, setShowVideoDownload, currentPath, refresh, files } = fileBrowserManager;

  const [phase, setPhase] = useState<'input' | 'preview'>('input');
  const [url, setUrl] = useState('');
  const [audioOnly, setAudioOnly] = useState(false);
  const [subfolder, setSubfolder] = useState('');
  const [fetching, setFetching] = useState(false);
  const [entries, setEntries] = useState<Entry[]>([]);
  const entriesRef = useRef(entries);
  entriesRef.current = entries;

  const isPlaylist = entries.length > 1;
  const readyCount = entries.filter((e) => e.status === 'ready').length;

  const reset = () => {
    setPhase('input');
    setUrl('');
    setAudioOnly(false);
    setSubfolder('');
    setFetching(false);
    setEntries([]);
  };

  // Reset whenever the dialog is (re)opened, so a new session starts clean.
  useEffect(() => {
    if (showVideoDownload) reset();
  }, [showVideoDownload]);

  const handleClose = () => setShowVideoDownload(false);

  const patch = (i: number, p: Partial<Entry>) =>
    setEntries((prev) => prev.map((e, idx) => (idx === i ? { ...e, ...p } : e)));

  // Fetch metadata: expand a playlist URL to its entries, then prefetch each one's info progressively.
  const fetchMeta = async () => {
    const u = url.trim();
    if (!u) return;
    setFetching(true);
    setEntries([]);

    let urls = [u];
    if (u.includes('list=')) {
      const pl = await files.videoPlaylist(u).catch(() => null);
      if (pl?.error) {
        setEntries([{ url: u, status: 'error', error: pl.error, dl: 'idle' }]);
        setPhase('preview');
        setFetching(false);
        return;
      }
      if (pl?.urls?.length) urls = pl.urls;
    }

    setEntries(urls.map((v) => ({ url: v, status: 'loading', dl: 'idle' })));
    setPhase('preview');

    // Sequentially (ReClip does yt-dlp per video — parallel would hammer it); cards fill in as they resolve.
    for (let i = 0; i < urls.length; i++) {
      const info = await files.videoInfo(urls[i]!).catch((): VideoInfo => ({ error: 'Could not fetch info' }));
      if (info.error) patch(i, { status: 'error', error: info.error });
      else
        patch(i, {
          status: 'ready',
          title: info.title,
          thumbnail: info.thumbnail,
          duration: info.duration,
          uploader: info.uploader,
        });
    }
    setFetching(false);
  };

  const targetDir = () => {
    const sub = sanitizeFolder(subfolder);
    if (!sub) return currentPath;
    return currentPath === '/' ? `/${sub}` : `${currentPath}/${sub}`;
  };

  // Download one entry as a background job (the server delegates to ReClip), polling to completion.
  const downloadEntry = async (i: number, entryUrl: string) => {
    patch(i, { dl: 'downloading', dlError: undefined });
    try {
      const { jobId } = await files.downloadVideo(entryUrl, targetDir(), audioOnly);
      const deadline = Date.now() + 60 * 60 * 1000;
      for (;;) {
        if (Date.now() > deadline) return patch(i, { dl: 'error', dlError: 'Timed out' });
        await sleep(2000);
        const st = await files.downloadVideoStatus(jobId).catch(() => null);
        if (!st) continue;
        if (st.status === 'error') return patch(i, { dl: 'error', dlError: st.error || 'Download failed' });
        if (st.status === 'transferring') patch(i, { dl: 'saving' });
        if (st.status === 'done') {
          patch(i, { dl: 'done', filename: st.filename });
          void refresh();
          return;
        }
      }
    } catch {
      patch(i, { dl: 'error', dlError: 'Could not start the download' });
    }
  };

  const [downloadingAll, setDownloadingAll] = useState(false);
  const downloadAll = async () => {
    setDownloadingAll(true);
    const snapshot = entriesRef.current;
    for (let i = 0; i < snapshot.length; i++) {
      const e = snapshot[i]!;
      if (e.status === 'ready' && e.dl !== 'done' && e.dl !== 'downloading' && e.dl !== 'saving') {
        await downloadEntry(i, e.url);
      }
    }
    setDownloadingAll(false);
  };

  const dlLabel = (e: Entry) =>
    e.dl === 'downloading' ? 'Downloading…' : e.dl === 'saving' ? 'Saving…' : e.dl === 'done' ? 'Saved' : '';

  return (
    <Dialog open={showVideoDownload} onOpenChange={(open) => !open && handleClose()}>
      <DialogContent className="max-w-lg">
        <DialogHeader>
          <DialogTitle>Download video</DialogTitle>
          <DialogDescription>
            {phase === 'input'
              ? 'Paste a video or playlist URL — it fetches details before downloading.'
              : 'Review and download.'}
          </DialogDescription>
        </DialogHeader>

        {phase === 'input' ? (
          <form
            onSubmit={(ev) => {
              ev.preventDefault();
              void fetchMeta();
            }}
            className="flex flex-col gap-4"
          >
            <input
              autoFocus
              value={url}
              onChange={(ev) => setUrl(ev.target.value)}
              placeholder="https://www.youtube.com/watch?v=…  or  …/playlist?list=…"
              className="h-10 w-full rounded-md border border-duck-dark/20 bg-background/60 px-3 text-sm text-duck-dark outline-none placeholder:text-duck-dark/40 focus:border-duck-teal/50"
            />
            <label className="flex cursor-pointer items-center gap-2 text-sm text-duck-dark/70">
              <Checkbox checked={audioOnly} onCheckedChange={(v) => setAudioOnly(v === true)} />
              Extract audio only (mp3)
            </label>
            <div className="flex justify-end gap-2">
              <button
                type="button"
                onClick={handleClose}
                className="cursor-pointer rounded-md px-4 py-2 text-sm text-duck-dark/70 transition-colors hover:bg-duck-dark/5"
              >
                Cancel
              </button>
              <button
                type="submit"
                disabled={!url.trim() || fetching}
                className="flex cursor-pointer items-center gap-2 rounded-md bg-duck-teal px-4 py-2 text-sm text-duck-yellow transition-colors hover:bg-duck-teal/90 disabled:cursor-not-allowed disabled:opacity-40"
              >
                {fetching && <Loader2 size={14} className="animate-spin" />}
                Fetch
              </button>
            </div>
          </form>
        ) : (
          <div className="flex flex-col gap-3">
            <div className="flex items-center justify-between gap-2">
              <button
                type="button"
                onClick={() => setPhase('input')}
                className="flex cursor-pointer items-center gap-1 text-sm text-duck-dark/60 hover:text-duck-dark"
              >
                <ChevronLeft size={15} /> Back
              </button>
              <span className="text-xs text-duck-dark/50">
                {audioOnly ? 'Audio only' : 'Video'}
                {isPlaylist ? ` · ${entries.length} items` : ''}
              </span>
            </div>

            {isPlaylist && (
              <div className="flex items-center gap-2">
                <input
                  value={subfolder}
                  onChange={(ev) => setSubfolder(ev.target.value)}
                  placeholder="Subfolder (optional) — leave blank for this folder"
                  className="h-9 min-w-0 flex-1 rounded-md border border-duck-dark/20 bg-background/60 px-3 text-sm text-duck-dark outline-none placeholder:text-duck-dark/40 focus:border-duck-teal/50"
                />
                <button
                  type="button"
                  onClick={() => void downloadAll()}
                  disabled={downloadingAll || readyCount === 0}
                  className="flex shrink-0 cursor-pointer items-center gap-1.5 rounded-md bg-duck-teal px-3 py-2 text-sm text-duck-yellow transition-colors hover:bg-duck-teal/90 disabled:cursor-not-allowed disabled:opacity-40"
                >
                  {downloadingAll ? <Loader2 size={14} className="animate-spin" /> : <Download size={14} />}
                  Download all
                </button>
              </div>
            )}

            <div className="flex max-h-[52vh] flex-col gap-2 overflow-y-auto pr-1">
              {entries.map((e, i) => (
                <div
                  key={`${e.url}-${i}`}
                  className="flex items-center gap-3 rounded-md border border-duck-dark/10 bg-background/40 p-2"
                >
                  <div className="flex h-12 w-20 shrink-0 items-center justify-center overflow-hidden rounded bg-duck-dark/10">
                    {e.status === 'loading' ? (
                      <Loader2 size={16} className="animate-spin text-duck-dark/40" />
                    ) : e.status === 'error' ? (
                      <AlertCircle size={16} className="text-red-500/70" />
                    ) : e.thumbnail && !audioOnly ? (
                      <img src={e.thumbnail} alt="" className="h-full w-full object-cover" />
                    ) : (
                      <Music size={16} className="text-duck-dark/40" />
                    )}
                  </div>

                  <div className="min-w-0 flex-1">
                    {e.status === 'loading' ? (
                      <div className="space-y-1.5">
                        <div className="h-3 w-3/4 rounded bg-duck-dark/10" />
                        <div className="h-2.5 w-1/3 rounded bg-duck-dark/10" />
                      </div>
                    ) : e.status === 'error' ? (
                      <>
                        <p className="truncate text-sm font-medium text-red-500">Could not fetch</p>
                        <p className="truncate text-xs text-duck-dark/50">{e.error || e.url}</p>
                      </>
                    ) : (
                      <>
                        <p className="truncate text-sm font-medium text-duck-dark" title={e.title}>
                          {e.title || e.url}
                        </p>
                        <p className="truncate text-xs text-duck-dark/50">
                          {[e.uploader, fmtDuration(e.duration)].filter(Boolean).join(' · ')}
                        </p>
                        {e.dl === 'error' && <p className="truncate text-xs text-red-500">{e.dlError}</p>}
                      </>
                    )}
                  </div>

                  {e.status === 'ready' && (
                    <div className="shrink-0">
                      {e.dl === 'done' ? (
                        <span className="flex items-center gap-1 text-xs font-medium text-duck-teal">
                          <Check size={14} /> Saved
                        </span>
                      ) : e.dl === 'downloading' || e.dl === 'saving' ? (
                        <span className="flex items-center gap-1.5 text-xs text-duck-dark/60">
                          <Loader2 size={13} className="animate-spin" /> {dlLabel(e)}
                        </span>
                      ) : (
                        <button
                          type="button"
                          onClick={() => void downloadEntry(i, e.url)}
                          className="flex cursor-pointer items-center gap-1.5 rounded-md border border-duck-teal/40 px-2.5 py-1.5 text-xs font-medium text-duck-teal transition-colors hover:bg-duck-teal/10"
                        >
                          <Download size={13} />
                          {e.dl === 'error' ? 'Retry' : 'Download'}
                        </button>
                      )}
                    </div>
                  )}
                </div>
              ))}
            </div>
          </div>
        )}
      </DialogContent>
    </Dialog>
  );
};
