"use client";

import { useEffect, useState } from "react";
import { PageHead } from "../_ui/page-head";
import { HelpButton } from "../_ui/help-button";

type Loaded = {
  qobuzEmail: string;
  qobuzUserId: string;
  slskdUrl: string;
  lastfmUsername: string;
  qobuzPasswordSet: boolean;
  qobuzTokenSet: boolean;
  slskdApiKeySet: boolean;
  lastfmApiKeySet: boolean;
};

type Tab = "Qobuz" | "Soulseek" | "Last.fm" | "Library" | "Monitor" | "Discovery";
const TABS: Tab[] = ["Qobuz", "Soulseek", "Last.fm", "Library", "Monitor", "Discovery"];

const DISCOVERY_FIELDS: [string, string][] = [
  ["discover.intervalHours", "Interval (hours)"],
  ["discover.chunkSize", "Seeds per chunk"],
  ["discover.similarPerSeed", "Similar per seed"],
  ["discover.albumsPerArtist", "Albums per artist"],
  ["discover.minScore", "Min score"],
];

const MONITOR_FIELDS: [string, string][] = [
  ["monitor.pollIntervalHours", "Poll interval (hours)"],
  ["monitor.retryIntervalHours", "Retry interval (hours)"],
  ["monitor.qualityCutoff", "Quality cutoff"],
  ["monitor.upgradeWindowDays", "Upgrade window (days)"],
  ["floor.jobDelaySeconds", "Delay between downloads (seconds)"],
];

export function SettingsForm() {
  const [tab, setTab] = useState<Tab>("Qobuz");
  const [qobuzEmail, setQobuzEmail] = useState(""); // legacy; no input, kept to detect stored creds
  const [qobuzUserId, setQobuzUserId] = useState("");
  const [qobuzToken, setQobuzToken] = useState("");
  const [slskdUrl, setSlskdUrl] = useState("");
  const [slskdApiKey, setSlskdApiKey] = useState("");
  const [lastfmUsername, setLastfmUsername] = useState("");
  const [lastfmApiKey, setLastfmApiKey] = useState("");
  const [pwSet, setPwSet] = useState(false);
  const [tokenSet, setTokenSet] = useState(false);
  const [keySet, setKeySet] = useState(false);
  const [lastfmKeySet, setLastfmKeySet] = useState(false);
  const [saved, setSaved] = useState(false);
  const [scanResult, setScanResult] = useState<string | null>(null);
  const [scanRequested, setScanRequested] = useState(false);
  const [discoverCfg, setDiscoverCfg] = useState<Record<string, string>>({});
  const [discoverSaved, setDiscoverSaved] = useState(false);
  const [monitorCfg, setMonitorCfg] = useState<Record<string, string>>({});
  const [monitorSaved, setMonitorSaved] = useState(false);
  const [pacingCfg, setPacingCfg] = useState<Record<string, string>>({});
  const [pacingSaved, setPacingSaved] = useState(false);

  useEffect(() => {
    fetch("/api/scan")
      .then((r) => r.json())
      .then((s: { result: string | null; requested: boolean }) => {
        setScanResult(s.result);
        setScanRequested(s.requested);
      });
  }, []);

  async function requestScan() {
    await fetch("/api/scan", { method: "POST" });
    setScanRequested(true);
  }

  useEffect(() => {
    if (!scanRequested) return;
    const id = setInterval(async () => {
      const s = await (await fetch("/api/scan")).json();
      setScanResult(s.result);
      if (!s.requested) setScanRequested(false);
    }, 2000);
    return () => clearInterval(id);
  }, [scanRequested]);

  useEffect(() => {
    fetch("/api/config")
      .then((r) => r.json())
      .then((c: Loaded) => {
        setQobuzEmail(c.qobuzEmail);
        setQobuzUserId(c.qobuzUserId);
        setSlskdUrl(c.slskdUrl);
        setLastfmUsername(c.lastfmUsername);
        setPwSet(c.qobuzPasswordSet);
        setTokenSet(c.qobuzTokenSet);
        setKeySet(c.slskdApiKeySet);
        setLastfmKeySet(c.lastfmApiKeySet);
      });
  }, []);

  useEffect(() => {
    fetch("/api/discover/config")
      .then((r) => r.json())
      .then((c: Record<string, string>) => setDiscoverCfg(c));
  }, []);

  useEffect(() => {
    fetch("/api/monitor/config")
      .then((r) => r.json())
      .then((c: Record<string, string>) => setMonitorCfg(c));
  }, []);

  useEffect(() => {
    fetch("/api/qobuz/pacing")
      .then((r) => r.json())
      .then((c: Record<string, string>) => setPacingCfg(c));
  }, []);

  async function saveCredentials(e: React.FormEvent) {
    e.preventDefault();
    await fetch("/api/config", {
      method: "PUT",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        qobuzUserId,
        qobuzToken,
        slskdUrl,
        slskdApiKey,
        lastfmApiKey,
        lastfmUsername,
      }),
    });
    setQobuzToken("");
    setSlskdApiKey("");
    setLastfmApiKey("");
    setSaved(true);
    setTimeout(() => setSaved(false), 1500);
    setTokenSet(tokenSet || qobuzToken !== "");
    setKeySet(keySet || slskdApiKey !== "");
    setLastfmKeySet(lastfmKeySet || lastfmApiKey !== "");
  }

  function setCfg(key: string, value: string) {
    setDiscoverCfg((c) => ({ ...c, [key]: value }));
  }

  async function saveDiscovery(e: React.FormEvent) {
    e.preventDefault();
    await fetch("/api/discover/config", {
      method: "PATCH",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(discoverCfg),
    });
    setDiscoverSaved(true);
    setTimeout(() => setDiscoverSaved(false), 1500);
  }

  function setMon(key: string, value: string) {
    setMonitorCfg((c) => ({ ...c, [key]: value }));
  }

  async function saveMonitor(e: React.FormEvent) {
    e.preventDefault();
    await fetch("/api/monitor/config", {
      method: "PATCH",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(monitorCfg),
    });
    setMonitorSaved(true);
    setTimeout(() => setMonitorSaved(false), 1500);
  }

  function setPacing(key: string, value: string) {
    setPacingCfg((c) => ({ ...c, [key]: value }));
  }

  async function savePacing(e: React.FormEvent) {
    e.preventDefault();
    await fetch("/api/qobuz/pacing", {
      method: "PATCH",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(pacingCfg),
    });
    setPacingSaved(true);
    setTimeout(() => setPacingSaved(false), 1500);
  }

  const secretHint = (set: boolean) => (set ? <span className="set"> · set</span> : null);

  async function clearField(field: "qobuzToken" | "slskdApiKey" | "lastfmApiKey") {
    await fetch(`/api/config?field=${field}`, { method: "DELETE" });
    if (field === "qobuzToken") { setTokenSet(false); setQobuzToken(""); }
    if (field === "slskdApiKey") { setKeySet(false); setSlskdApiKey(""); }
    if (field === "lastfmApiKey") { setLastfmKeySet(false); setLastfmApiKey(""); }
  }

  // Purge the legacy email/password login (kept working for backward-compat, but Qobuz
  // often rejects it — the token path is preferred).
  async function clearLegacyQobuz() {
    await Promise.all([
      fetch("/api/config?field=qobuzPassword", { method: "DELETE" }),
      fetch("/api/config?field=qobuzEmail", { method: "DELETE" }),
    ]);
    setPwSet(false);
    setQobuzEmail("");
  }

  return (
    <div>
      <PageHead title="Settings" eyebrow="Credentials · library · monitor · discovery · last.fm" />

      <div className="tabs">
        {TABS.map((t) => (
          <button key={t} className={`tab${tab === t ? " active" : ""}`} onClick={() => setTab(t)}>
            {t}
          </button>
        ))}
      </div>

      {tab === "Qobuz" ? (
        <>
        <form className="settings-section" onSubmit={saveCredentials}>
          <div className="subhead">
            Auth token
            <HelpButton title="Qobuz authentication">
              Lyra signs in to Qobuz with a <b>user ID + auth token</b> — the download engine
              treats these as more reliable than email/password, which Qobuz often rejects. Find
              them in your logged-in Qobuz web session (bundle/app secret + user auth token).
            </HelpButton>
          </div>
          <div className="field-grid">
            <label className="field">
              <span>Qobuz user ID</span>
              <input aria-label="qobuz user id" value={qobuzUserId} onChange={(e) => setQobuzUserId(e.target.value)} />
            </label>
            <label className="field">
              <span>
                Qobuz auth token{secretHint(tokenSet)}
                {tokenSet ? (
                  <button type="button" className="btn ghost sm"
                    aria-label="clear qobuz auth token" onClick={() => clearField("qobuzToken")}>
                    Clear
                  </button>
                ) : null}
              </span>
              <input aria-label="qobuz auth token" type="password" placeholder={tokenSet ? "•••• stored" : ""} value={qobuzToken} onChange={(e) => setQobuzToken(e.target.value)} />
            </label>
          </div>
          {pwSet || qobuzEmail ? (
            <p className="help">
              A legacy Qobuz email/password login is still stored (Qobuz often rejects it —
              the token above takes precedence).{" "}
              <button type="button" className="btn ghost sm" aria-label="clear legacy qobuz login"
                onClick={clearLegacyQobuz}>
                Clear legacy login
              </button>
            </p>
          ) : null}
          <div className="save-row">
            <button type="submit" className="btn accent">Save</button>
            {saved ? <span className="saved-note">Saved</span> : null}
          </div>
        </form>
        <form className="settings-section" onSubmit={savePacing}>
          <div className="subhead">
            Pacing
            <HelpButton title="Qobuz pacing">
              Throttles Qobuz to a human-looking volume and cadence so the account isn&apos;t flagged
              for bulk downloading. Within active hours it downloads up to the daily cap, spaced out
              with randomized gaps; when throttled, albums fall through to Soulseek/YouTube and
              upgrade to Qobuz hi-res later. The daily cap ramps up over the first two weeks from the
              new-account start date.
            </HelpButton>
          </div>
          <label className="field-inline">
            <input type="checkbox" aria-label="pace qobuz downloads"
              checked={pacingCfg["qobuz.pacing.enabled"] !== "false"}
              onChange={(e) => setPacing("qobuz.pacing.enabled", e.target.checked ? "true" : "false")} />
            <span>Pace Qobuz downloads</span>
          </label>
          <div className="field-grid">
            <label className="field">
              <span>Active hours — start (0–24)</span>
              <input type="number" min={0} max={24} aria-label="qobuz active start hour"
                value={pacingCfg["qobuz.pacing.activeStartHour"] ?? ""}
                onChange={(e) => setPacing("qobuz.pacing.activeStartHour", e.target.value)} />
            </label>
            <label className="field">
              <span>Active hours — end (0–24)</span>
              <input type="number" min={0} max={24} aria-label="qobuz active end hour"
                value={pacingCfg["qobuz.pacing.activeEndHour"] ?? ""}
                onChange={(e) => setPacing("qobuz.pacing.activeEndHour", e.target.value)} />
            </label>
            <label className="field">
              <span>Daily cap (steady)</span>
              <input type="number" min={0} aria-label="qobuz daily cap"
                value={pacingCfg["qobuz.pacing.dailyCap"] ?? ""}
                onChange={(e) => setPacing("qobuz.pacing.dailyCap", e.target.value)} />
            </label>
            <label className="field">
              <span>New-account start (warm-up)</span>
              <input type="date" aria-label="qobuz warmup start date"
                value={pacingCfg["qobuz.pacing.warmupStartDate"] ?? ""}
                onChange={(e) => setPacing("qobuz.pacing.warmupStartDate", e.target.value)} />
            </label>
          </div>
          <div className="save-row">
            <button type="submit" className="btn accent">Save</button>
            {pacingSaved ? <span className="saved-note">Saved</span> : null}
          </div>
        </form>
        </>
      ) : null}

      {tab === "Soulseek" ? (
        <form className="settings-section" onSubmit={saveCredentials}>
          <div className="subhead">Soulseek (slskd)</div>
          <div className="field-grid">
            <label className="field">
              <span>slskd URL</span>
              <input aria-label="slskd url" value={slskdUrl} onChange={(e) => setSlskdUrl(e.target.value)} />
            </label>
            <label className="field">
              <span>
                slskd API key{secretHint(keySet)}
                {keySet ? (
                  <button type="button" className="btn ghost sm"
                    aria-label="clear slskd api key" onClick={() => clearField("slskdApiKey")}>
                    Clear
                  </button>
                ) : null}
              </span>
              <input aria-label="slskd api key" type="password" placeholder={keySet ? "•••• stored" : ""} value={slskdApiKey} onChange={(e) => setSlskdApiKey(e.target.value)} />
            </label>
          </div>
          <div className="save-row">
            <button type="submit" className="btn accent">Save</button>
            {saved ? <span className="saved-note">Saved</span> : null}
          </div>
        </form>
      ) : null}

      {tab === "Last.fm" ? (
        <form className="settings-section" onSubmit={saveCredentials}>
          <div className="subhead">Last.fm</div>
          <div className="field-grid">
            <label className="field">
              <span>Last.fm username</span>
              <input aria-label="lastfm username" value={lastfmUsername} onChange={(e) => setLastfmUsername(e.target.value)} />
            </label>
            <label className="field">
              <span>
                Last.fm API key{secretHint(lastfmKeySet)}
                {lastfmKeySet ? (
                  <button type="button" className="btn ghost sm"
                    aria-label="clear lastfm api key" onClick={() => clearField("lastfmApiKey")}>
                    Clear
                  </button>
                ) : null}
              </span>
              <input aria-label="lastfm api key" type="password" placeholder={lastfmKeySet ? "•••• stored" : ""} value={lastfmApiKey} onChange={(e) => setLastfmApiKey(e.target.value)} />
            </label>
          </div>
          <div className="save-row">
            <button type="submit" className="btn accent">Save</button>
            {saved ? <span className="saved-note">Saved</span> : null}
          </div>
        </form>
      ) : null}

      {tab === "Library" ? (
        <section className="settings-section">
          <div className="info-panel">
            <div>
              <span className="k">Music path</span> &nbsp; <code>/music</code>
            </div>
            <div className="muted-note" style={{ fontFamily: "inherit" }}>
              Mounted read/write into the worker. To point Lyra at a different folder, set <code>MUSIC_DIR</code> in{" "}
              <code>docker-compose.yml</code> and rebuild — it can’t be remounted from here.
            </div>
          </div>
          <div className="info-panel">
            <div>
              <span className="k">Staging path</span> &nbsp; <code>/staging</code>
            </div>
            <div className="muted-note" style={{ fontFamily: "inherit" }}>
              Where each download is assembled + verified before being promoted into the library.
              Set by <code>STAGING_DIR</code> in <code>docker-compose.yml</code> (defaults to{" "}
              <code>MUSIC_DIR/.staging</code>). When your library is on a network share (SMB/NFS),
              point <code>STAGING_DIR</code> at a <b>fast local disk</b> so temporary download I/O
              doesn’t hammer the share — the finished album is copied onto the library volume and
              swapped in atomically. Rebuild to change.
            </div>
          </div>
          <p className="muted-note">Scan the library to record what’s already on disk as owned + monitored.</p>
          <div className="save-row">
            <button type="button" className="btn" onClick={requestScan} disabled={scanRequested}>
              {scanRequested ? "Scanning…" : "Scan library"}
            </button>
            {scanResult ? (
              <span className="saved-note" aria-label="scan result" style={{ color: "var(--graphite)" }}>
                Last scan: {scanResult}
              </span>
            ) : null}
          </div>
        </section>
      ) : null}

      {tab === "Monitor" ? (
        <form className="settings-section" onSubmit={saveMonitor}>
          <div className="save-row" style={{ margin: "6px 0 4px" }}>
            <label className="toggle">
              <input
                type="checkbox"
                aria-label="automatic monitoring"
                checked={monitorCfg["monitor.enabled"] === "true"}
                onChange={(e) => setMon("monitor.enabled", e.target.checked ? "true" : "false")}
              />
              Automatic monitoring (grab &amp; upgrade)
            </label>
            <HelpButton title="Monitoring">
              When on, the worker periodically checks your followed artists: it <b>grabs new
              releases</b> as they appear and <b>upgrades</b> owned albums that fall below the
              quality cutoff.
              <br />
              <br />
              <b>Quality cutoff</b> — the minimum acceptable quality class: <code>1</code> = lossy,{" "}
              <code>2</code> = CD-lossless (FLAC 16/44.1), <code>3</code> = hi-res. An owned album is
              kept as-is when it’s ≥ the cutoff; otherwise a better copy is chased. Range 1–3.
              <br />
              <br />
              <b>Poll interval</b> — hours between new-release checks. <b>Retry interval</b> — hours
              before re-attempting a release that couldn’t be found yet. <b>Upgrade window</b> — how
              many days after first acquiring an album to keep looking for a better copy.
            </HelpButton>
          </div>
          <div className="field-grid">
            {MONITOR_FIELDS.map(([key, label]) => (
              <label key={key} className="field">
                <span>{label}</span>
                <input type="number" step="any" value={monitorCfg[key] ?? ""} onChange={(e) => setMon(key, e.target.value)} />
              </label>
            ))}
          </div>
          <div className="save-row">
            <button type="submit" className="btn">Save monitor settings</button>
            {monitorSaved ? <span className="saved-note">Saved</span> : null}
          </div>
        </form>
      ) : null}

      {tab === "Discovery" ? (
        <form className="settings-section" onSubmit={saveDiscovery}>
          <div className="save-row" style={{ margin: "6px 0 4px" }}>
            <label className="toggle">
              <input
                type="checkbox"
                checked={discoverCfg["discover.enabled"] === "true"}
                onChange={(e) => setCfg("discover.enabled", e.target.checked ? "true" : "false")}
              />
              Scheduled discovery sweep
            </label>
            <HelpButton title="Discovery">
              When on, the worker periodically sweeps your followed artists for similar artists +
              their new releases (via ListenBrainz and, if configured, Last.fm) and lists them on{" "}
              <b>Discover</b>. Nothing is downloaded automatically — you Follow/Want from there.
              <br />
              <br />
              <b>Interval</b> — hours between sweeps. <b>Seeds per chunk</b> — how many followed
              artists are processed per worker cycle (keeps job-claiming responsive on big rosters).
              <b> Similar per seed</b> — candidates pulled per artist. <b>Albums per artist</b> —
              releases surfaced per suggested artist. <b>Min score</b> — drops weak suggestions.{" "}
              <b>Albums only</b> — suggest full-length albums only, hiding singles and EPs.{" "}
              <b>Seed from library</b> — also find artists similar to ones you own albums by, not
              just the artists you follow.{" "}
              <b>ListenBrainz base URL</b> — leave blank for the default endpoint.
            </HelpButton>
          </div>
          <div className="field-grid">
            {DISCOVERY_FIELDS.map(([key, label]) => (
              <label key={key} className="field">
                <span>{label}</span>
                <input type="number" step="any" value={discoverCfg[key] ?? ""} onChange={(e) => setCfg(key, e.target.value)} />
              </label>
            ))}
          </div>
          <label className="toggle" style={{ margin: "10px 0 0" }}>
            <input
              type="checkbox"
              checked={discoverCfg["discover.albumsOnly"] !== "false"}
              onChange={(e) => setCfg("discover.albumsOnly", e.target.checked ? "true" : "false")}
            />
            Albums only (hide singles &amp; EPs)
          </label>
          <label className="toggle" style={{ margin: "6px 0 0" }}>
            <input
              type="checkbox"
              checked={discoverCfg["discover.seedFromLibrary"] !== "false"}
              onChange={(e) => setCfg("discover.seedFromLibrary", e.target.checked ? "true" : "false")}
            />
            Seed from library (similar to albums you own)
          </label>
          <label className="field">
            <span>ListenBrainz base URL (blank = default)</span>
            <input value={discoverCfg["discover.listenBrainzUrl"] ?? ""}
              onChange={(e) => setCfg("discover.listenBrainzUrl", e.target.value)} />
          </label>
          <div className="save-row">
            <button type="submit" className="btn">Save discovery settings</button>
            {discoverSaved ? <span className="saved-note">Saved</span> : null}
          </div>
        </form>
      ) : null}
    </div>
  );
}
