import { useEffect, useState } from "react";
import {
  checkPrereqs, checkYtDlpUpdate, importTakeoutCsv, listBrowsers, pickLibraryFolder,
  pickTakeoutFile, previewRemoveAllSubscriptions, previewTakeoutImport,
  removeAllSubscriptions, setCookieSource, testYoutube, updateYtDlp,
} from "../api";
import { APPEARANCE_MODES, type Appearance } from "../hooks/useAppearance";
import {
  QUALITIES, STREAM_QUALITIES, SUB_LANGS,
  BULK_LIMITS,
  type ImportPreview, type Prereqs, type Quality, type UpdateStatus,
} from "../types";
import TakeoutGuide from "./TakeoutGuide";
import {
  BTN, BTN_PRIMARY, CONTROL_H, Dialog, HELP, ICON_BTN, LABEL, SectionHeading, Segmented,

} from "./ui";

interface Props {
  onClose: () => void;
  onImported: (count: number) => void;
  onRemovedAll: (count: number) => void;
  appearance: Appearance;
  onAppearance: (a: Appearance) => void;
  quality: Quality;
  onQuality: (q: Quality) => void;
  bulkLimit: number;
  onBulkLimit: (n: number) => void;
  streamQuality: Quality;
  onStreamQuality: (q: Quality) => void;
  subLang: string;
  onSubLang: (l: string) => void;
  hideShorts: boolean;
  onHideShorts: (v: boolean) => void;
  autoplayNext: boolean;
  onAutoplayNext: (v: boolean) => void;
  browser: string;
  onBrowser: (b: string) => void;
  onError: (message: string) => void;
}

const SELECT =
  `w-full ${CONTROL_H} cursor-pointer rounded-lg border border-slate-300 bg-white px-2 ` +
  "text-[12px] outline-none dark:border-slate-700 dark:bg-slate-800";

function StatusRow({ label, value }: { label: string; value: string | null }) {
  return (
    <div className="flex items-start justify-between gap-4 border-b border-slate-200 py-2 last:border-b-0 dark:border-slate-800">
      <span className={LABEL}>{label}</span>
      <span
        className={`text-right text-[12px] ${
          value ? "text-slate-600 dark:text-slate-300" : "text-red-600 dark:text-red-400"
        }`}
      >
        {value ?? "Not found"}
      </span>
    </div>
  );
}

export default function Settings({
  onClose, onImported, onRemovedAll, appearance, onAppearance, quality, onQuality,
  bulkLimit, onBulkLimit,
  streamQuality, onStreamQuality, subLang, onSubLang, hideShorts, onHideShorts,
  autoplayNext, onAutoplayNext,
  browser, onBrowser, onError,
}: Props) {
  const [prereqs, setPrereqs] = useState<Prereqs | null>(null);
  const [pending, setPending] = useState<{ path: string; preview: ImportPreview } | null>(null);
  // Emptying the list is confirmed against what it would actually remove.
  const [wipe, setWipe] = useState<ImportPreview | null>(null);
  const [busy, setBusy] = useState(false);
  const [browsers, setBrowsers] = useState<Array<[string, string]>>([]);
  const [check, setCheck] = useState<{ ok: boolean; message: string } | null>(null);
  const [checking, setChecking] = useState(false);
  const [guide, setGuide] = useState(false);
  const [update, setUpdate] = useState<UpdateStatus | null>(null);
  const [updating, setUpdating] = useState(false);

  useEffect(() => {
    checkYtDlpUpdate().then(setUpdate).catch(() => setUpdate(null));
  }, []);

  const runUpdate = async () => {
    setUpdating(true);
    try {
      const version = await updateYtDlp();
      setUpdate({ current: version, latest: update?.latest ?? version, up_to_date: true });
      await load();
    } catch (e) {
      onError(String(e));
    } finally {
      setUpdating(false);
    }
  };

  useEffect(() => {
    listBrowsers().then(setBrowsers).catch(() => setBrowsers([]));
  }, []);

  const chooseBrowser = async (id: string) => {
    onBrowser(id);
    setCheck(null);
    try {
      await setCookieSource(id);
    } catch (e) {
      onError(String(e));
    }
  };

  const runCheck = async () => {
    setChecking(true);
    setCheck(null);
    try {
      setCheck({ ok: true, message: await testYoutube() });
    } catch (e) {
      setCheck({ ok: false, message: String(e) });
    } finally {
      setChecking(false);
    }
  };

  const load = () => checkPrereqs().then(setPrereqs).catch(() => setPrereqs(null));
  useEffect(() => { load(); }, []);

  const startImport = async () => {
    try {
      const path = await pickTakeoutFile();
      if (!path) return;
      setPending({ path, preview: await previewTakeoutImport(path) });
    } catch (e) {
      onError(String(e));
    }
  };

  const confirmImport = async () => {
    if (!pending) return;
    setBusy(true);
    try {
      const n = await importTakeoutCsv(pending.path);
      setPending(null);
      onImported(n);
    } catch (e) {
      setPending(null);
      onError(String(e));
    } finally {
      setBusy(false);
    }
  };

  const pickFolder = async () => {
    try {
      if (await pickLibraryFolder()) load();
    } catch (e) {
      onError(String(e));
    }
  };

  const missing = prereqs && (!prereqs.yt_dlp || !prereqs.ffmpeg);
  const p = pending?.preview;
  const destructive = !!p && (p.removed_channels > 0 || p.removed_downloads > 0);

  return (
    <>
      <div
        className="fixed inset-0 z-[70] flex items-center justify-center bg-slate-950/70 p-5"
        onClick={onClose}
      >
        <div
          role="dialog"
          aria-modal="true"
          onClick={(e) => e.stopPropagation()}
          className="flex max-h-[82vh] w-full max-w-lg flex-col rounded-2xl border border-slate-300
                     bg-white shadow-2xl dark:border-slate-700 dark:bg-slate-900"
        >
          <header className="flex items-center justify-between gap-2 border-b border-slate-200 px-5 py-4 dark:border-slate-800">
            <h2 className="text-[15px] font-semibold tracking-tight">Settings</h2>
            <button
              onClick={onClose}
              title="Close settings"
              aria-label="Close settings"
              className={`${ICON_BTN} text-slate-500 hover:bg-slate-100 hover:text-slate-900
                          dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-white`}
            >
              <svg viewBox="0 0 24 24" className="size-4" fill="none" stroke="currentColor" strokeWidth="2">
                <path strokeLinecap="round" d="M6 6l12 12M18 6L6 18" />
              </svg>
            </button>
          </header>

          <div className="min-h-0 flex-1 overflow-y-auto">
            <section className="border-b border-slate-200 px-5 py-4 dark:border-slate-800">
              <SectionHeading>Subscriptions</SectionHeading>
              <p className={`mt-1.5 ${HELP}`}>
                Importing <b>replaces</b> your current list — the CSV becomes the whole truth.
                Channels no longer in it are removed along with their videos and downloads.
                You'll see exactly what goes before anything is deleted.
              </p>
              <div className="mt-3 flex flex-wrap items-center gap-2">
                <button onClick={startImport} className={`${BTN_PRIMARY} cursor-pointer`}>
                  Import subscriptions.csv
                </button>
                <button onClick={() => setGuide(true)} className={`${BTN} cursor-pointer`}>
                  How do I get the file?
                </button>
                <button
                  onClick={() =>
                    previewRemoveAllSubscriptions()
                      .then(setWipe)
                      .catch((e) => onError(String(e)))
                  }
                  className={`${BTN} cursor-pointer hover:border-red-500! hover:text-red-600!
                              dark:hover:border-red-500! dark:hover:text-red-400!`}
                >
                  Remove all
                </button>
              </div>
            </section>

            <section className="border-b border-slate-200 px-5 py-4 dark:border-slate-800">
              <SectionHeading>Download quality</SectionHeading>
              <p className={`mt-1.5 ${HELP}`}>
                Above 1080p YouTube only serves VP9 and AV1, and <b>4K AV1 currently plays
                back with visible artefacts in this player</b> — the downloaded file is
                fine, it is the built-in decoder that struggles. 1080p gets H.264, which
                plays cleanly and is several times smaller. Audio is AAC at every setting.
              </p>
              <label className="mt-2 grid grid-cols-[92px_1fr] items-center gap-2">
                <span className={LABEL}>Quality</span>
                <select
                  value={quality}
                  onChange={(e) => onQuality(e.target.value as Quality)}
                  className={SELECT}
                >
                  {QUALITIES.map((q) => (
                    <option key={q.value} value={q.value}>
                      {q.label}
                    </option>
                  ))}
                </select>
              </label>

              <label className="mt-2 grid grid-cols-[92px_1fr] items-center gap-2">
                <span className={LABEL}>Download all</span>
                <select
                  value={bulkLimit}
                  onChange={(e) => onBulkLimit(Number(e.target.value))}
                  className={SELECT}
                >
                  {BULK_LIMITS.map((b) => (
                    <option key={b.value} value={b.value}>
                      {b.label}
                    </option>
                  ))}
                </select>
              </label>
              <p className={`mt-2 ${HELP}`}>
                The cap on one <b>Download all</b>. It takes the newest first, so from
                All subscriptions you get the latest across every channel rather than
                several hundred videos at once. Press it again for the next batch.
              </p>

              <label className="mt-2 grid grid-cols-[92px_1fr] items-center gap-2">
                <span className={LABEL}>Streaming</span>
                <select
                  value={streamQuality}
                  onChange={(e) => onStreamQuality(e.target.value as Quality)}
                  className={SELECT}
                >
                  {STREAM_QUALITIES.map((q) => (
                    <option key={q.value} value={q.value}>
                      {q.label}
                    </option>
                  ))}
                </select>
              </label>
              <p className={`mt-2 ${HELP}`}>
                Streaming quality applies when you play something you have not downloaded.
                Best lets the player adapt to your connection; a fixed height pins it.
              </p>

              <label className="mt-3 grid grid-cols-[92px_1fr] items-center gap-2">
                <span className={LABEL}>Subtitles</span>
                <select
                  value={subLang}
                  onChange={(e) => onSubLang(e.target.value)}
                  className={SELECT}
                >
                  {SUB_LANGS.map((l) => (
                    <option key={l.value} value={l.value}>
                      {l.label}
                    </option>
                  ))}
                </select>
              </label>
              <p className={`mt-2 ${HELP}`}>
                Which language switches itself on, including YouTube's auto-generated
                captions — on most videos those are the only ones there are. Downloads keep
                them beside the file for offline use; streams fetch them separately, since
                YouTube's live manifest carries no subtitles at all. <b>Off</b> only means
                nothing comes on by itself: subtitles are still embedded in downloads and
                still listed in the player's subtitle menu.
              </p>
            </section>

            <section className="border-b border-slate-200 px-5 py-4 dark:border-slate-800">
              <SectionHeading>Feed</SectionHeading>
              <label className="mt-2 flex cursor-pointer items-center justify-between gap-3">
                <span className={LABEL}>Hide Shorts</span>
                <input
                  type="checkbox"
                  checked={hideShorts}
                  onChange={(e) => onHideShorts(e.target.checked)}
                  className="size-4 cursor-pointer accent-sky-500"
                />
              </label>
              <p className={`mt-2 ${HELP}`}>
                Keeps YouTube Shorts out of the feed entirely.
              </p>
            </section>

            <section className="border-b border-slate-200 px-5 py-4 dark:border-slate-800">
              <SectionHeading>Playback</SectionHeading>
              <label className="mt-2 flex cursor-pointer items-center justify-between gap-3">
                <span className={LABEL}>Play next automatically</span>
                <input
                  type="checkbox"
                  checked={autoplayNext}
                  onChange={(e) => onAutoplayNext(e.target.checked)}
                  className="size-4 cursor-pointer accent-sky-500"
                />
              </label>
              <p className={`mt-2 ${HELP}`}>
                When a video ends, the next one in the list starts on its own. Meant for
                offline viewing, where it plays through your downloads one after another;
                online it will stream the next video, and it stops at the end of the list.
              </p>
            </section>

            <section className="border-b border-slate-200 px-5 py-4 dark:border-slate-800">
              <SectionHeading>Sign in to YouTube</SectionHeading>
              <p className={`mt-1.5 ${HELP}`}>
                YouTube sometimes asks a machine to prove it is not a bot, and then
                nothing will stream or download. Pointing the app at a browser you are
                already signed into clears that. The cookies are read on this Mac and
                sent only to YouTube.
              </p>
              <label className="mt-2 grid grid-cols-[92px_1fr] items-center gap-2">
                <span className={LABEL}>Use cookies</span>
                <select
                  value={browser}
                  onChange={(e) => void chooseBrowser(e.target.value)}
                  className={SELECT}
                >
                  <option value="">Not signed in</option>
                  {browsers.map(([id, label]) => (
                    <option key={id} value={id}>
                      {label}
                    </option>
                  ))}
                </select>
              </label>

              <div className="mt-2 flex items-center gap-2">
                <button onClick={runCheck} disabled={checking} className={`${BTN} cursor-pointer`}>
                  {checking ? "Checking…" : "Check connection"}
                </button>
                {check && (
                  <span
                    className={`text-[11px] leading-snug ${
                      check.ok
                        ? "text-slate-500 dark:text-slate-400"
                        : "text-red-600 dark:text-red-400"
                    }`}
                  >
                    {check.message}
                  </span>
                )}
              </div>
            </section>

            <section className="border-b border-slate-200 px-5 py-4 dark:border-slate-800">
              <SectionHeading>Appearance</SectionHeading>
              <div className="mt-2 flex items-center justify-between gap-3">
                <span className={LABEL}>Theme</span>
                <Segmented
                  value={appearance}
                  onChange={onAppearance}
                  options={APPEARANCE_MODES.map((m) => ({
                    value: m,
                    label: m[0].toUpperCase() + m.slice(1),
                  }))}
                />
              </div>
            </section>

            <section className="px-5 py-4">
              <SectionHeading>Status</SectionHeading>
              <div className="mt-2">
                <div className="flex items-start justify-between gap-4 border-b border-slate-200 py-2 dark:border-slate-800">
                  <span className={LABEL}>yt-dlp</span>
                  <span className="flex items-center gap-2 text-right">
                    <span className="text-[12px] text-slate-600 dark:text-slate-300">
                      {prereqs?.yt_dlp ?? "Not found"}
                    </span>
                    {update && !update.up_to_date && update.latest && (
                      <button
                        onClick={runUpdate}
                        disabled={updating}
                        title={`Update to ${update.latest}`}
                        className={`${BTN} cursor-pointer`}
                      >
                        {updating ? "Updating…" : `Update to ${update.latest}`}
                      </button>
                    )}
                    {update?.up_to_date && (
                      <span className="text-[11px] text-slate-400 dark:text-slate-500">
                        up to date
                      </span>
                    )}
                  </span>
                </div>
                <StatusRow label="ffmpeg" value={prereqs?.ffmpeg ?? null} />
                <div className="flex items-start justify-between gap-4 py-2">
                  <span className={LABEL}>Library</span>
                  <button
                    onClick={pickFolder}
                    className="cursor-pointer break-all text-right text-[12px] text-sky-600
                               underline underline-offset-2 hover:text-sky-500 dark:text-sky-400"
                  >
                    {prereqs?.library_path ?? "…"}
                  </button>
                </div>
              </div>

              <p className={`mt-2 ${HELP}`}>
                Both ship inside the app, so nothing needs installing. yt-dlp breaks
                whenever YouTube changes something, so it can be updated here; ffmpeg is
                stable and comes with each release. A copy on your system takes precedence
                over either.
              </p>

              {missing && (
                <div className="mt-2 rounded-lg border border-red-500/30 bg-red-500/5 p-3">
                  <p className="text-[11px] leading-snug text-red-700 dark:text-red-300">
                    A bundled tool is missing, which should not happen. Reinstalling the app
                    will restore it; meanwhile <code>brew install yt-dlp ffmpeg</code> also
                    works.
                  </p>
                </div>
              )}
            </section>
          </div>
        </div>
      </div>

      {wipe && (
        <Dialog
          title="Remove every subscription?"
          onCancel={() => setWipe(null)}
          onConfirm={() => {
            setWipe(null);
            setBusy(true);
            removeAllSubscriptions()
              .then(onRemovedAll)
              .catch((e) => onError(String(e)))
              .finally(() => setBusy(false));
          }}
          confirmLabel="Remove all"
          destructive
        >
          <p>
            All <b>{wipe.removed_channels}</b> channels leave FlightTube, taking{" "}
            <b>{wipe.removed_videos}</b> videos and <b>{wipe.removed_downloads}</b> downloaded
            file{wipe.removed_downloads === 1 ? "" : "s"} with them.
          </p>
          <p className="mt-2">
            Nothing changes on YouTube — you stay subscribed there, and importing or reading
            the list again brings everything back.
          </p>
        </Dialog>
      )}

      {guide && (
        <Dialog title="Getting your subscriptions" onCancel={() => setGuide(false)} wide>
          <p className={`mb-3 ${HELP}`}>
            YouTube has no public API for someone else's subscription list, so FlightTube
            reads the export Google gives you. It takes about two minutes.
          </p>
          <TakeoutGuide />
        </Dialog>
      )}

      {pending && p && (
        <Dialog
          title={destructive ? "Replace your subscriptions?" : "Import subscriptions"}
          onCancel={() => setPending(null)}
          onConfirm={busy ? undefined : confirmImport}
          confirmLabel={destructive ? "Replace" : "Import"}
          destructive={destructive}
        >
          <p>
            The file lists <b>{p.incoming}</b> subscription{p.incoming === 1 ? "" : "s"}, which
            will become your complete list.
          </p>
          {destructive ? (
            <ul className="mt-2 space-y-1">
              <li>
                <b>{p.removed_channels}</b> channel{p.removed_channels === 1 ? "" : "s"} no longer
                subscribed will be removed
              </li>
              <li>
                <b>{p.removed_videos}</b> of their video{p.removed_videos === 1 ? "" : "s"} will
                disappear from your feed
              </li>
              {p.removed_downloads > 0 && (
                <li className="text-red-600 dark:text-red-400">
                  <b>{p.removed_downloads}</b> downloaded file
                  {p.removed_downloads === 1 ? "" : "s"} will be deleted from disk
                </li>
              )}
            </ul>
          ) : (
            <p className="mt-2">Nothing currently stored will be removed.</p>
          )}
        </Dialog>
      )}
    </>
  );
}

