import { useState } from "react";
import { useTranslation } from "react-i18next";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Bell, Monitor, RefreshCw, Trash2, User } from "lucide-react";
import { SCHEMES, type Scheme, type ThemePrefs } from "../lib/theme";
import { api, type Me } from "../lib/api";
import { LS, useAccountPersistedState } from "../lib/storage";
import Avatar from "./Avatar";
import { notify, type NotifSettings } from "../lib/notifications";
import Tooltip from "./Tooltip";
import { Section, SettingRow, Switch } from "./ui/form";
import { DraftSaveBar, type SaveState } from "./ui/DraftSaveBar";
import { useConfirm } from "./ConfirmProvider";

// The Settings page edits server-persisted preferences as a draft: changes apply locally
// for instant preview but reach the server only on an explicit Save (or revert on Discard).
// App owns the draft + baseline (so the leave-the-page guard can see it); this controller is
// how the panel reads/writes it. See App.tsx.
export type PrefsSaveState = SaveState;
export interface PrefsController {
  theme: ThemePrefs;
  setTheme: (t: ThemePrefs) => void;
  view: "grid" | "list";
  setView: (v: "grid" | "list") => void;
  perf: boolean;
  setPerf: (v: boolean) => void;
  hints: boolean;
  setHints: (v: boolean) => void;
  notif: NotifSettings;
  setNotif: (n: NotifSettings) => void;
  dirty: boolean;
  save: () => void;
  discard: () => void;
  saveState: PrefsSaveState;
}

type TabId = "appearance" | "notifications" | "account";
const TABS: { id: TabId; icon: typeof Monitor }[] = [
  { id: "appearance", icon: Monitor },
  { id: "notifications", icon: Bell },
  { id: "account", icon: User },
];

// Settings as a page (Design B): rendered in the main content area, not an overlay. It keeps
// the frosted `.glass` surface so it still refracts the ambient backdrop. The page title is
// shown by the Header; the tab rail stays as in-page sub-navigation.
export default function SettingsPanel({
  me,
  prefs,
  onOpenWizard,
}: {
  me: Me;
  prefs: PrefsController;
  onOpenWizard: () => void;
}) {
  const { t } = useTranslation();
  const [tabRaw, setTab] = useAccountPersistedState(LS.settingsTab, "appearance");
  // Clamp at render so a stale/removed tab id falls back to Appearance.
  const tab: TabId = TABS.some((tabItem) => tabItem.id === tabRaw)
    ? (tabRaw as TabId)
    : "appearance";

  return (
    <div className="p-4 max-w-3xl w-full mx-auto">
      <div className="glass rounded-2xl overflow-hidden">
        <div className="flex">
          {/* Vertical tab rail — never wraps; active is a clear accent pill. */}
          <nav className="w-32 sm:w-36 shrink-0 border-r border-border/60 p-2 flex flex-col gap-1">
            {TABS.map((tabItem) => {
              const active = tab === tabItem.id;
              return (
                <button
                  key={tabItem.id}
                  onClick={() => setTab(tabItem.id)}
                  className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm text-left transition ${
                    active
                      ? "bg-accent text-accent-fg font-medium shadow-md shadow-accent/20"
                      : "text-muted hover:text-fg hover:bg-card/60"
                  }`}
                >
                  <tabItem.icon className="w-4 h-4 shrink-0" />
                  {t(`settings.tabs.${tabItem.id}`)}
                </button>
              );
            })}
          </nav>

          {/* Render only the active tab so the panel sizes to its actual content. (Stacking
              every tab in one grid cell made the whole panel as tall as the tallest tab —
              Account — leaving the short tabs with dead space and a needless scrollbar.) */}
          <div className="flex-1 min-w-0 p-4">
            {tab === "appearance" && <Appearance prefs={prefs} />}
            {tab === "notifications" && <Notifications prefs={prefs} />}
            {tab === "account" && <Account me={me} onOpenWizard={onOpenWizard} />}
          </div>
        </div>

        {/* Save/Discard bar — spans the whole card (a change can come from any tab). Only the
            Appearance/Notifications prefs are drafted; the Sync/Account tabs act immediately. */}
        <PrefsSaveBar prefs={prefs} />
      </div>
    </div>
  );
}

function PrefsSaveBar({ prefs }: { prefs: PrefsController }) {
  const { t } = useTranslation();
  return (
    <DraftSaveBar
      variant="inline"
      dirty={prefs.dirty}
      state={prefs.saveState}
      onSave={prefs.save}
      onDiscard={prefs.discard}
      labels={{
        saved: t("settings.save.saved"),
        failed: t("settings.save.failed"),
        unsaved: t("settings.save.unsaved"),
        discard: t("settings.save.discard"),
        save: t("settings.save.save"),
        saving: t("settings.save.saving"),
      }}
    />
  );
}

function Appearance({ prefs }: { prefs: PrefsController }) {
  const { t } = useTranslation();
  // Controlled by App's prefs draft: each change applies locally for preview but is only
  // persisted on an explicit Save (handled by the panel's Save/Discard bar).
  const { theme, setTheme, view, setView, perf, setPerf, hints, setHints } = prefs;

  return (
    <>
      <Section title={t("settings.appearance.colorScheme")}>
        <div className="grid grid-cols-4 gap-2">
          {SCHEMES.map((s) => (
            <Tooltip key={s.id} hint={s.name}>
              <button
                onClick={() => setTheme({ ...theme, scheme: s.id as Scheme })}
                aria-label={s.name}
                aria-pressed={theme.scheme === s.id}
                className={`h-9 w-full rounded-lg border-2 transition ${
                  theme.scheme === s.id ? "border-fg" : "border-transparent"
                }`}
                style={{ background: s.swatch }}
              />
            </Tooltip>
          ))}
        </div>
      </Section>

      <Section title={t("settings.appearance.display")}>
        <SettingRow label={t("settings.appearance.darkMode")}>
          <Switch
            label={t("settings.appearance.darkMode")}
            checked={theme.mode === "dark"}
            onChange={(v) => setTheme({ ...theme, mode: v ? "dark" : "light" })}
          />
        </SettingRow>
        <SettingRow
          label={t("settings.appearance.backgroundImage")}
          hint={t("settings.appearance.backgroundImageHint")}
        >
          <Switch
            label={t("settings.appearance.backgroundImage")}
            checked={theme.bgImage}
            disabled={theme.mode !== "dark"}
            onChange={(v) => setTheme({ ...theme, bgImage: v })}
          />
        </SettingRow>
        {theme.mode === "dark" && theme.bgImage && (
          <div className="pl-1 pb-1">
            <div className="flex items-center justify-between text-xs text-muted mb-1">
              <span>{t("settings.appearance.backgroundFade")}</span>
              <span className="tabular-nums">{theme.bgFade}%</span>
            </div>
            <input
              type="range"
              min={0}
              max={100}
              step={1}
              value={theme.bgFade}
              onChange={(e) => setTheme({ ...theme, bgFade: Number(e.target.value) })}
              aria-label={t("settings.appearance.backgroundFade")}
              className="w-full accent-accent"
            />
          </div>
        )}
        <SettingRow label={t("settings.appearance.listView")} hint={t("settings.appearance.listViewHint")}>
          <Switch
            label={t("settings.appearance.listView")}
            checked={view === "list"}
            onChange={(v) => setView(v ? "list" : "grid")}
          />
        </SettingRow>
        <SettingRow
          label={t("settings.appearance.performanceMode")}
          hint={t("settings.appearance.performanceModeHint")}
        >
          <Switch label={t("settings.appearance.performanceMode")} checked={perf} onChange={setPerf} />
        </SettingRow>
        <SettingRow
          label={t("settings.appearance.showHints")}
          hint={t("settings.appearance.showHintsHint")}
        >
          <Switch label={t("settings.appearance.showHints")} checked={hints} onChange={setHints} />
        </SettingRow>
      </Section>

      <Section title={t("settings.appearance.textSize")}>
        <input
          type="range"
          min={0.9}
          max={1.3}
          step={0.02}
          value={theme.fontScale}
          onChange={(e) => setTheme({ ...theme, fontScale: Number(e.target.value) })}
          aria-label={t("settings.appearance.textSize")}
          className="w-full accent-accent"
        />
        <div className="text-right text-xs text-muted">{Math.round(theme.fontScale * 100)}%</div>
      </Section>
    </>
  );
}

function Notifications({ prefs }: { prefs: PrefsController }) {
  const { t } = useTranslation();
  // Controlled by App's prefs draft (live preview via configureNotifications there); the
  // panel's Save bar persists it. The "send test" button below still fires immediately.
  const { notif: cfg, setNotif } = prefs;
  const update = (p: Partial<NotifSettings>) => setNotif({ ...cfg, ...p });
  const autoOn = cfg.durationMs > 0;

  return (
    <Section title={t("settings.notifications.title")}>
      <SettingRow
        label={t("settings.notifications.sound")}
        hint={t("settings.notifications.soundHint")}
      >
        <Switch
          label={t("settings.notifications.sound")}
          checked={cfg.sound}
          onChange={(v) => update({ sound: v })}
        />
      </SettingRow>
      <SettingRow
        label={t("settings.notifications.autoDismiss")}
        hint={t("settings.notifications.autoDismissHint")}
      >
        <Switch
          label={t("settings.notifications.autoDismiss")}
          checked={autoOn}
          onChange={(v) => update({ durationMs: v ? 6000 : 0 })}
        />
      </SettingRow>
      {autoOn && (
        <div className="py-1.5 text-sm">
          <div className="flex items-center justify-between">
            <span className="text-muted text-xs">{t("settings.notifications.dismissAfter")}</span>
            <span className="text-muted text-xs">{(cfg.durationMs / 1000).toFixed(0)}s</span>
          </div>
          <input
            type="range"
            min={2000}
            max={15000}
            step={1000}
            value={cfg.durationMs}
            onChange={(e) => update({ durationMs: Number(e.target.value) })}
            aria-label={t("settings.notifications.dismissAfter")}
            className="w-full accent-accent mt-1"
          />
        </div>
      )}
      <button
        onClick={() =>
          notify({
            level: "info",
            title: t("settings.notifications.testTitle"),
            message: t("settings.notifications.testMessage"),
            sound: true,
          })
        }
        className="mt-2 text-sm text-accent hover:underline"
      >
        {t("settings.notifications.sendTest")}
      </button>
    </Section>
  );
}

function AccessRow({
  title,
  granted,
  grantedHint,
  enableHint,
  onEnable,
}: {
  title: string;
  granted: boolean;
  grantedHint: string;
  enableHint: string;
  onEnable: () => void;
}) {
  const { t } = useTranslation();
  return (
    <div className="flex items-start justify-between gap-3 py-2">
      <div className="min-w-0">
        <div className="text-sm font-medium">{title}</div>
        <p className="text-xs text-muted leading-relaxed mt-0.5">
          {granted ? grantedHint : enableHint}
        </p>
      </div>
      {granted ? (
        <span className="shrink-0 text-[11px] px-2 py-1 rounded-full border border-accent/40 text-accent">
          {t("settings.account.granted")}
        </span>
      ) : (
        <Tooltip hint={t("settings.account.enableHint")}>
          <button
            onClick={onEnable}
            className="shrink-0 glass-card glass-hover px-3 py-1.5 rounded-xl text-sm transition"
          >
            {t("settings.account.enable")}
          </button>
        </Tooltip>
      )}
    </div>
  );
}

// Sign-in methods: link a Google account to a password account (or vice-versa set a password),
// so either method can reach the same account. Google connect is a full-page OAuth round-trip
// (auth.py attaches the identity to the current session via /auth/link); the password form posts
// directly with inline errors. Demo accounts never see this (handled by the caller).
function SignInMethods({ me }: { me: Me }) {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const confirm = useConfirm();
  const [open, setOpen] = useState(false);
  const [current, setCurrent] = useState("");
  const [next, setNext] = useState("");
  const [err, setErr] = useState<string | null>(null);
  const [busy, setBusy] = useState(false);
  const [sessBusy, setSessBusy] = useState(false);

  // SA4: sign every OTHER session for this account out (this one stays). For a shared/left-behind
  // device or a suspected compromise.
  const logoutOthers = async () => {
    if (
      !(await confirm({
        title: t("settings.account.sessions.confirmTitle"),
        message: t("settings.account.sessions.confirmBody"),
        confirmLabel: t("settings.account.sessions.action"),
        danger: true,
      }))
    )
      return;
    setSessBusy(true);
    try {
      await api.logoutOthers();
      notify({ level: "success", message: t("settings.account.sessions.done") });
    } catch {
      notify({ level: "error", message: t("settings.account.sessions.failed") });
    } finally {
      setSessBusy(false);
    }
  };

  const submit = async (e: React.FormEvent) => {
    e.preventDefault();
    setErr(null);
    setBusy(true);
    try {
      await api.setPassword(next, me.has_password ? current : undefined);
      setOpen(false);
      setCurrent("");
      setNext("");
      // Refresh `me` so has_password flips and the section switches to "Change password".
      await qc.invalidateQueries({ queryKey: ["me"] });
      notify({ level: "success", message: t("settings.account.password.saved", { email: me.email }) });
    } catch (e: any) {
      setErr(e?.detail ?? t("settings.account.password.failed"));
    } finally {
      setBusy(false);
    }
  };

  const inputCls =
    "w-full bg-card border border-border rounded-xl px-3 py-2 text-sm outline-none focus:border-accent";

  // Offer Google only when this instance has Google OAuth configured (or the account is already
  // linked — then we still show its "Connected" status even if the instance creds were removed).
  const showGoogle = me.google_enabled || me.has_google;

  return (
    <Section title={t("settings.account.signInMethods")}>
      {showGoogle && (
        <div className="flex items-start justify-between gap-3 py-2">
          <div className="min-w-0">
            <div className="text-sm font-medium">{t("settings.account.googleLink.title")}</div>
            <p className="text-xs text-muted leading-relaxed mt-0.5">
              {me.has_google
                ? t("settings.account.googleLink.connectedHint")
                : t("settings.account.googleLink.connectHint")}
            </p>
          </div>
          {me.has_google ? (
            <span className="shrink-0 text-[11px] px-2 py-1 rounded-full border border-accent/40 text-accent">
              {t("settings.account.googleLink.connected")}
            </span>
          ) : (
            <button
              onClick={() => {
                window.location.href = "/auth/link";
              }}
              className="shrink-0 glass-card glass-hover px-3 py-1.5 rounded-xl text-sm transition"
            >
              {t("settings.account.googleLink.connect")}
            </button>
          )}
        </div>
      )}

      <div className={showGoogle ? "border-t border-border mt-1 pt-1" : ""}>
        <div className="flex items-start justify-between gap-3 py-2">
          <div className="min-w-0">
            <div className="text-sm font-medium">{t("settings.account.password.title")}</div>
            <p className="text-xs text-muted leading-relaxed mt-0.5">
              {me.has_password
                ? t("settings.account.password.setHint")
                : t("settings.account.password.unsetHint")}
            </p>
          </div>
          <button
            onClick={() => {
              setOpen((o) => !o);
              setErr(null);
            }}
            className="shrink-0 glass-card glass-hover px-3 py-1.5 rounded-xl text-sm transition"
          >
            {me.has_password
              ? t("settings.account.password.change")
              : t("settings.account.password.set")}
          </button>
        </div>
        {open && (
          <form onSubmit={submit} className="space-y-2 pt-1 pb-1">
            {me.has_password && (
              <input
                type="password"
                value={current}
                onChange={(e) => setCurrent(e.target.value)}
                placeholder={t("settings.account.password.current")}
                autoComplete="current-password"
                className={inputCls}
              />
            )}
            <input
              type="password"
              value={next}
              onChange={(e) => setNext(e.target.value)}
              placeholder={t("settings.account.password.new")}
              autoComplete="new-password"
              className={inputCls}
            />
            {err && <p className="text-xs text-red-400">{err}</p>}
            <button
              type="submit"
              disabled={busy || !next}
              className="px-3 py-1.5 rounded-lg text-sm bg-accent text-accent-fg font-medium disabled:opacity-40 transition"
            >
              {busy ? t("settings.account.password.saving") : t("settings.account.password.save")}
            </button>
          </form>
        )}
      </div>

      <div className="border-t border-border mt-1 pt-1">
        <div className="flex items-start justify-between gap-3 py-2">
          <div className="min-w-0">
            <div className="text-sm font-medium">{t("settings.account.sessions.title")}</div>
            <p className="text-xs text-muted leading-relaxed mt-0.5">
              {t("settings.account.sessions.hint")}
            </p>
          </div>
          <button
            onClick={logoutOthers}
            disabled={sessBusy}
            className="shrink-0 glass-card glass-hover px-3 py-1.5 rounded-xl text-sm transition disabled:opacity-40"
          >
            {t("settings.account.sessions.action")}
          </button>
        </div>
      </div>
    </Section>
  );
}

// Two-way Plex watch-state sync (Phase A): the owner links their account to the Plex admin account
// and does a one-time "Plex is master" import. Admin-only (rides the server admin token) and only
// shown when the Plex module is enabled. Two-way push/pull arrive in later phases.
function PlexWatchSync() {
  const { t, i18n } = useTranslation();
  const qc = useQueryClient();
  const link = useQuery({ queryKey: ["plex-watch-link"], queryFn: () => api.plexWatchLink() });
  const enabled = link.data?.sync_enabled ?? false;

  const announce = (imp: { watched: number; in_progress: number } | null | undefined) => {
    if (imp) {
      notify({
        level: "success",
        message: t("settings.plexSync.imported", { watched: imp.watched, in_progress: imp.in_progress }),
      });
      // The browse grid's watch badges read plex_states — refresh them.
      qc.invalidateQueries({ queryKey: ["plex"] });
    }
  };

  const toggle = useMutation({
    mutationFn: (next: boolean) => api.plexWatchSetLink(next),
    onSuccess: (res) => {
      qc.setQueryData(["plex-watch-link"], res);
      announce(res.import);
    },
  });
  const reimport = useMutation({
    mutationFn: () => api.plexWatchImport(),
    onSuccess: (res) => {
      qc.setQueryData(["plex-watch-link"], res);
      announce(res.import);
    },
  });

  const busy = toggle.isPending || reimport.isPending;
  // Reflect the toggle's target immediately (optimistic) so the switch responds on click rather than
  // only flipping once the multi-second import returns — otherwise it reads as "nothing happened".
  const shown = toggle.isPending ? Boolean(toggle.variables) : enabled;
  const last = link.data?.last_watch_sync_at
    ? new Date(link.data.last_watch_sync_at).toLocaleString(i18n.language)
    : null;

  return (
    <Section title={t("settings.plexSync.title")}>
      <p className="text-xs text-muted leading-relaxed mb-2">{t("settings.plexSync.intro")}</p>
      <SettingRow label={t("settings.plexSync.toggle")} hint={t("settings.plexSync.toggleHint")}>
        <Switch
          checked={shown}
          disabled={busy}
          onChange={(v) => !busy && toggle.mutate(v)}
        />
      </SettingRow>
      {busy ? (
        <div className="mt-2 flex items-center gap-2 text-xs text-muted">
          <RefreshCw className="w-3.5 h-3.5 animate-spin" />
          {t("settings.plexSync.importing")}
        </div>
      ) : (
        enabled && (
          <div className="mt-2 flex items-center justify-between gap-3">
            <p className="text-xs text-muted">
              {last ? t("settings.plexSync.lastSync", { when: last }) : t("settings.plexSync.never")}
            </p>
            <button
              onClick={() => reimport.mutate()}
              className="inline-flex items-center gap-1.5 px-3 py-2 rounded-xl text-sm border border-border hover:bg-card/60 transition shrink-0"
            >
              <RefreshCw className="w-4 h-4" />
              {t("settings.plexSync.importNow")}
            </button>
          </div>
        )
      )}
    </Section>
  );
}

function Account({ me, onOpenWizard }: { me: Me; onOpenWizard: () => void }) {
  const { t } = useTranslation();
  const confirm = useConfirm();
  const onDeleteAccount = async () => {
    const ok = await confirm({
      title: t("settings.account.deleteTitle"),
      message: t("settings.account.deleteConfirm"),
      confirmLabel: t("settings.account.deleteConfirmButton"),
      danger: true,
    });
    if (!ok) return;
    try {
      await api.deleteAccount();
      // Session cleared server-side → /api/me 401s → Welcome. The flag shows the confirmation banner.
      window.location.href = "/?deleted=1";
    } catch {
      /* the global error dialog surfaces the reason (e.g. last admin) */
    }
  };
  return (
    <>
    <Section title={t("settings.account.title")}>
      <div className="flex items-center gap-3 mb-3">
        <Avatar
          src={me.avatar_url}
          fallback={me.display_name ?? me.email}
          className="w-12 h-12 rounded-full"
        />
        <div className="min-w-0">
          <div className="font-semibold truncate">{me.display_name ?? me.email.split("@")[0]}</div>
          <div className="text-xs text-muted truncate">{me.email}</div>
          <div className="text-xs text-muted capitalize">{me.role}</div>
        </div>
      </div>
    </Section>

    {!me.is_demo && <SignInMethods me={me} />}

    {me.role === "admin" && me.plex_enabled && <PlexWatchSync />}

    {me.is_demo ? (
      <Section title={t("settings.account.youtubeAccess")}>
        <p className="text-xs text-muted leading-relaxed">
          {t("settings.account.demoNotice")}
        </p>
      </Section>
    ) : me.google_enabled ? (
      // YouTube access requires Google OAuth; hidden when this instance has no Google configured.
      <Section title={t("settings.account.youtubeAccess")}>
        <p className="text-xs text-muted leading-relaxed mb-1">
          {t("settings.account.youtubeAccessIntro")}
        </p>
        <div className="divide-y divide-border">
          <AccessRow
            title={t("settings.account.readTitle")}
            granted={me.can_read}
            grantedHint={t("settings.account.readGrantedHint")}
            enableHint={t("settings.account.readEnableHint")}
            onEnable={() => {
              window.location.href = "/auth/upgrade?access=read";
            }}
          />
          <AccessRow
            title={t("settings.account.writeTitle")}
            granted={me.can_write}
            grantedHint={t("settings.account.writeGrantedHint")}
            enableHint={t("settings.account.writeEnableHint")}
            onEnable={() => {
              window.location.href = "/auth/upgrade?access=write";
            }}
          />
        </div>
        <button
          onClick={onOpenWizard}
          className="mt-2 text-sm text-accent hover:underline"
        >
          {t("settings.account.walkMeThrough")}
        </button>
      </Section>
    ) : null}

    {!me.is_demo && (
      <Section title={t("settings.account.dangerZone")}>
        <p className="text-xs text-muted leading-relaxed mb-2">{t("settings.account.deleteHint")}</p>
        <button
          onClick={onDeleteAccount}
          className="inline-flex items-center gap-1.5 px-3 py-2 rounded-xl text-sm border border-red-500/40 text-red-400 hover:bg-red-500/10 transition"
        >
          <Trash2 className="w-4 h-4" />
          {t("settings.account.deleteAccount")}
        </button>
      </Section>
    )}
    </>
  );
}
