"use client";

import { useCallback, useEffect, useState } from "react";

type Check = { id: string; label: string; ok: boolean; detail: string; required: boolean };
type Status = {
  installed: boolean;
  lock: Record<string, unknown> | null;
  allRequired: boolean;
  checks: Check[];
  viewer: { signedIn: boolean; role: string | null };
  env: { database: string | null; siteUrl: string | null; storageDir: string; authSecretSet: boolean };
  node: string;
};

const STEPS = ["Requirements", "Database & site", "Tables", "Admin", "Demo data", "Finish"];
const LS_KEY = "playtube-setup";

function loadSaved() {
  try {
    return JSON.parse(localStorage.getItem(LS_KEY) ?? "{}") as Record<string, string>;
  } catch {
    return {};
  }
}

export default function SetupPage() {
  const [status, setStatus] = useState<Status | null>(null);
  const [loading, setLoading] = useState(true);
  const [step, setStep] = useState(0);
  const [busy, setBusy] = useState("");
  const [notice, setNotice] = useState<{ kind: "ok" | "err" | "info"; text: string } | null>(null);
  const [log, setLog] = useState("");

  const [databaseUrl, setDatabaseUrl] = useState("");
  const [siteUrl, setSiteUrl] = useState("");
  const [storageDir, setStorageDir] = useState("");
  const [authSecret, setAuthSecret] = useState("");
  const [probe, setProbe] = useState<Record<string, unknown> | null>(null);

  const [adminEmail, setAdminEmail] = useState("");
  const [adminPassword, setAdminPassword] = useState("");
  const [adminName, setAdminName] = useState("");
  const [adminHandle, setAdminHandle] = useState("");
  const [migrated, setMigrated] = useState(false);
  const [adminDone, setAdminDone] = useState(false);
  const [seeded, setSeeded] = useState(false);

  const refresh = useCallback(async () => {
    setLoading(true);
    try {
      const res = await fetch("/api/setup/status", { cache: "no-store" });
      const data = (await res.json()) as Status;
      setStatus(data);
    } catch {
      setNotice({ kind: "err", text: "Could not reach the setup API. Is the Node.js app running?" });
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    refresh();
    const saved = loadSaved();
    if (saved.databaseUrl) setDatabaseUrl(saved.databaseUrl);
    if (saved.siteUrl) setSiteUrl(saved.siteUrl);
    if (saved.storageDir) setStorageDir(saved.storageDir);
    if (saved.authSecret) setAuthSecret(saved.authSecret);
    if (saved.adminEmail) setAdminEmail(saved.adminEmail);
    if (saved.adminName) setAdminName(saved.adminName);
    if (saved.adminHandle) setAdminHandle(saved.adminHandle);
  }, [refresh]);

  useEffect(() => {
    try {
      localStorage.setItem(
        LS_KEY,
        JSON.stringify({ databaseUrl, siteUrl, storageDir, authSecret, adminEmail, adminName, adminHandle }),
      );
    } catch {
      /* private mode */
    }
  }, [databaseUrl, siteUrl, storageDir, authSecret, adminEmail, adminName, adminHandle]);

  async function post(path: string, body: unknown) {
    const res = await fetch(path, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(body),
    });
    const data = await res.json().catch(() => ({}));
    if (!res.ok) throw new Error((data as { error?: string }).error ?? `Request failed (${res.status})`);
    return data;
  }

  function generateSecret() {
    const bytes = new Uint8Array(48);
    crypto.getRandomValues(bytes);
    let s = "";
    bytes.forEach((b) => (s += String.fromCharCode(b)));
    setAuthSecret(btoa(s));
  }

  const checks = status?.checks ?? [];
  const checkById = (id: string) => checks.find((c) => c.id === id);

  return (
    <div className="mx-auto max-w-3xl space-y-6 px-4 py-10">
      <header>
        <p className="text-xs font-semibold uppercase tracking-widest text-brand">Server installer</p>
        <h1 className="mt-1 text-3xl font-bold">Install PlayTube</h1>
        <p className="mt-2 text-sm text-muted">
          This wizard configures PlayTube on your own server or cPanel hosting: requirements, database,
          tables, admin account and demo content. Nothing here is needed to just <em>use</em> PlayTube —
          for the mobile/desktop app see <a className="underline" href="/install">/install</a>. Prefer PHP?
          Upload <code>cpanel/installer.php</code> to your hosting and open it in a browser for the same
          installer as a PHP GUI (see docs/CPANEL.md, Method C).
        </p>
      </header>

      {loading && !status ? (
        <div className="animate-pulse space-y-3">
          <div className="h-10 rounded-xl bg-chip" />
          <div className="h-64 rounded-xl bg-chip" />
        </div>
      ) : status?.installed ? (
        <section className="space-y-4 rounded-xl border border-line p-6">
          <h2 className="text-xl font-semibold">✅ PlayTube is already installed</h2>
          {status.lock ? (
            <pre className="overflow-x-auto rounded-lg bg-chip p-3 text-xs">
              {JSON.stringify(status.lock, null, 2)}
            </pre>
          ) : null}
          <div className="flex flex-wrap gap-2">
            <a href="/" className="rounded-lg bg-brand px-4 py-2 text-sm font-semibold text-white hover:bg-brand-hover">
              Open PlayTube
            </a>
            <a href="/admin" className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover">
              Admin panel
            </a>
            {status.viewer.role === "admin" ? (
              <button
                disabled={busy === "unlock"}
                onClick={async () => {
                  setBusy("unlock");
                  setNotice(null);
                  try {
                    await post("/api/setup/unlock", {});
                    try { localStorage.removeItem(LS_KEY); } catch {}
                    await refresh();
                    setNotice({ kind: "info", text: "Installer unlocked — you can re-run setup now." });
                  } catch (err) {
                    setNotice({ kind: "err", text: err instanceof Error ? err.message : "Unlock failed." });
                  } finally {
                    setBusy("");
                  }
                }}
                className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover disabled:opacity-50"
              >
                {busy === "unlock" ? "Unlocking…" : "Unlock & re-run setup"}
              </button>
            ) : (
              <p className="w-full text-xs text-muted">
                Signed in as admin? You can unlock the installer and re-run it. Otherwise it stays locked for safety.
              </p>
            )}
          </div>
        </section>
      ) : (
        <>
          <ol className="flex flex-wrap gap-2">
            {STEPS.map((label, i) => (
              <button
                key={label}
                onClick={() => setStep(i)}
                className={`rounded-full border px-3 py-1.5 text-xs font-semibold ${
                  i === step
                    ? "border-brand bg-brand text-white"
                    : (i === 2 && migrated) || (i === 3 && adminDone) || (i === 4 && seeded)
                      ? "border-green-600 text-green-600"
                      : "border-line text-muted hover:text-fg"
                }`}
              >
                {i + 1}. {label}
              </button>
            ))}
          </ol>

          {notice ? (
            <div
              className={`rounded-xl border p-4 text-sm ${
                notice.kind === "ok"
                  ? "border-green-600/40 bg-green-600/10"
                  : notice.kind === "err"
                    ? "border-red-600/40 bg-red-600/10"
                    : "border-line bg-chip"
              }`}
            >
              {notice.text}
            </div>
          ) : null}

          {/* STEP 1 — requirements */}
          {step === 0 && (
            <section className="space-y-3 rounded-xl border border-line p-5">
              <h2 className="text-lg font-semibold">1 · Server requirements</h2>
              <ul className="space-y-2 text-sm">
                {checks.map((c) => (
                  <li key={c.id} className="flex items-start gap-3 rounded-lg bg-chip p-3">
                    <span className="mt-0.5 text-base">{c.ok ? "✅" : c.required ? "❌" : "⚠️"}</span>
                    <span>
                      <strong>{c.label}</strong>
                      {!c.required && <span className="ml-2 text-xs text-muted">(optional)</span>}
                      <br />
                      <span className="text-muted">{c.detail}</span>
                    </span>
                  </li>
                ))}
              </ul>
              <div className="flex gap-2">
                <button
                  onClick={refresh}
                  className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover"
                >
                  Re-check
                </button>
                <button
                  onClick={() => setStep(1)}
                  className="rounded-lg bg-brand px-4 py-2 text-sm font-semibold text-white hover:bg-brand-hover"
                >
                  Continue →
                </button>
              </div>
            </section>
          )}

          {/* STEP 2 — database & site */}
          {step === 1 && (
            <section className="space-y-4 rounded-xl border border-line p-5">
              <h2 className="text-lg font-semibold">2 · Database & site settings</h2>
              <p className="text-sm text-muted">
                Active config: <code className="rounded bg-chip px-1">{status?.env.database ?? "no DATABASE_URL"}</code>
                {" · "}SITE_URL: <code className="rounded bg-chip px-1">{status?.env.siteUrl ?? "—"}</code>
              </p>
              <label className="block text-sm">
                <span className="mb-1 block font-semibold">DATABASE_URL (PostgreSQL)</span>
                <input
                  value={databaseUrl}
                  onChange={(e) => setDatabaseUrl(e.target.value)}
                  placeholder="postgresql://user:password@host:5432/playtube"
                  spellCheck={false}
                  className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-sm outline-none focus:border-brand"
                />
                <span className="mt-1 block text-xs text-muted">
                  cPanel: create the database + user under “PostgreSQL Databases”, then paste the connection string.
                </span>
              </label>
              <div className="grid gap-4 sm:grid-cols-2">
                <label className="block text-sm">
                  <span className="mb-1 block font-semibold">SITE_URL</span>
                  <input
                    value={siteUrl}
                    onChange={(e) => setSiteUrl(e.target.value)}
                    placeholder="https://tube.example.com"
                    spellCheck={false}
                    className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-sm outline-none focus:border-brand"
                  />
                </label>
                <label className="block text-sm">
                  <span className="mb-1 block font-semibold">STORAGE_DIR (optional)</span>
                  <input
                    value={storageDir}
                    onChange={(e) => setStorageDir(e.target.value)}
                    placeholder="./storage"
                    spellCheck={false}
                    className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-sm outline-none focus:border-brand"
                  />
                </label>
              </div>
              <label className="block text-sm">
                <span className="mb-1 block font-semibold">AUTH_SECRET {status?.env.authSecretSet ? "(already set ✓)" : ""}</span>
                <div className="flex gap-2">
                  <input
                    value={authSecret}
                    onChange={(e) => setAuthSecret(e.target.value)}
                    placeholder="Leave empty to keep the current secret"
                    spellCheck={false}
                    className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-sm outline-none focus:border-brand"
                  />
                  <button
                    onClick={generateSecret}
                    className="shrink-0 rounded-lg border border-line px-3 py-2 text-sm font-semibold hover:bg-surface-hover"
                  >
                    Generate
                  </button>
                </div>
              </label>
              {probe ? (
                <pre className="overflow-x-auto rounded-lg bg-chip p-3 text-xs">
                  {JSON.stringify(probe, null, 2)}
                </pre>
              ) : null}
              <div className="flex flex-wrap gap-2">
                <button
                  disabled={busy === "test" || !databaseUrl.trim()}
                  onClick={async () => {
                    setBusy("test");
                    setNotice(null);
                    try {
                      const data = await post("/api/setup/test-db", { databaseUrl: databaseUrl.trim() });
                      setProbe(data);
                      setNotice(
                        data.ok
                          ? { kind: "ok", text: "Connection works." }
                          : { kind: "err", text: `Connection failed: ${data.error ?? "unknown error"}` },
                      );
                    } catch (err) {
                      setNotice({ kind: "err", text: err instanceof Error ? err.message : "Test failed." });
                    } finally {
                      setBusy("");
                    }
                  }}
                  className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover disabled:opacity-50"
                >
                  {busy === "test" ? "Testing…" : "Test connection"}
                </button>
                <button
                  disabled={busy === "save"}
                  onClick={async () => {
                    setBusy("save");
                    setNotice(null);
                    try {
                      const data = await post("/api/setup/save-env", {
                        databaseUrl: databaseUrl.trim() || undefined,
                        siteUrl: siteUrl.trim() || undefined,
                        storageDir: storageDir.trim() || undefined,
                        authSecret: authSecret.trim() || undefined,
                      });
                      setNotice({
                        kind: "info",
                        text: `Saved ${(data.saved as string[]).join(", ")} to .env. ${data.note as string}`,
                      });
                      await refresh();
                    } catch (err) {
                      setNotice({ kind: "err", text: err instanceof Error ? err.message : "Save failed." });
                    } finally {
                      setBusy("");
                    }
                  }}
                  className="rounded-lg bg-brand px-4 py-2 text-sm font-semibold text-white hover:bg-brand-hover disabled:opacity-50"
                >
                  {busy === "save" ? "Saving…" : "Save to .env"}
                </button>
                <button
                  onClick={() => setStep(2)}
                  className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover"
                >
                  Continue →
                </button>
              </div>
              <p className="text-xs text-muted">
                After saving, restart the Node.js app so the new values load, then press “Re-check” on step 1.
                cPanel: Setup Node.js App → Restart (or <code>touch tmp/restart.txt</code>).
              </p>
            </section>
          )}

          {/* STEP 3 — tables */}
          {step === 2 && (
            <section className="space-y-4 rounded-xl border border-line p-5">
              <h2 className="text-lg font-semibold">3 · Create database tables</h2>
              <p className="text-sm text-muted">
                Runs <code className="rounded bg-chip px-1">drizzle-kit push --force</code> on the server.
                Tables now: <strong>{checkById("tables")?.detail ?? "unknown"}</strong>
              </p>
              <div className="flex gap-2">
                <button
                  disabled={busy === "migrate"}
                  onClick={async () => {
                    setBusy("migrate");
                    setNotice(null);
                    setLog("");
                    try {
                      const data = await post("/api/setup/migrate", {
                        databaseUrl: databaseUrl.trim() || undefined,
                      });
                      setLog(String(data.log ?? ""));
                      if (data.ok) {
                        setMigrated(true);
                        setNotice({ kind: "ok", text: "Tables created. Continuing…" });
                        await refresh();
                      } else {
                        setNotice({ kind: "err", text: "Migration reported an error — see the log below." });
                      }
                    } catch (err) {
                      setNotice({ kind: "err", text: err instanceof Error ? err.message : "Migration failed." });
                    } finally {
                      setBusy("");
                    }
                  }}
                  className="rounded-lg bg-brand px-4 py-2 text-sm font-semibold text-white hover:bg-brand-hover disabled:opacity-50"
                >
                  {busy === "migrate" ? "Migrating… (up to 3 min)" : "Create tables"}
                </button>
                <button
                  onClick={() => setStep(3)}
                  className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover"
                >
                  Continue →
                </button>
              </div>
              {log ? <pre className="max-h-72 overflow-auto rounded-lg bg-chip p-3 text-xs">{log}</pre> : null}
            </section>
          )}

          {/* STEP 4 — admin */}
          {step === 3 && (
            <section className="space-y-4 rounded-xl border border-line p-5">
              <h2 className="text-lg font-semibold">4 · Admin account</h2>
              <p className="text-sm text-muted">
                Admins now: <strong>{checkById("admin")?.detail ?? "unknown"}</strong>. If the email or handle
                already exists it will be promoted to admin (and its password reset).
              </p>
              <div className="grid gap-4 sm:grid-cols-2">
                <label className="block text-sm">
                  <span className="mb-1 block font-semibold">Email</span>
                  <input value={adminEmail} onChange={(e) => setAdminEmail(e.target.value)} placeholder="admin@example.com" className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-sm outline-none focus:border-brand" />
                </label>
                <label className="block text-sm">
                  <span className="mb-1 block font-semibold">Password (min 8 chars)</span>
                  <input value={adminPassword} onChange={(e) => setAdminPassword(e.target.value)} type="password" placeholder="••••••••" className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-sm outline-none focus:border-brand" />
                </label>
                <label className="block text-sm">
                  <span className="mb-1 block font-semibold">Display name</span>
                  <input value={adminName} onChange={(e) => setAdminName(e.target.value)} placeholder="Site Admin" className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-sm outline-none focus:border-brand" />
                </label>
                <label className="block text-sm">
                  <span className="mb-1 block font-semibold">Handle</span>
                  <input value={adminHandle} onChange={(e) => setAdminHandle(e.target.value)} placeholder="admin" className="w-full rounded-lg border border-line bg-bg px-3 py-2 text-sm outline-none focus:border-brand" />
                </label>
              </div>
              <div className="flex gap-2">
                <button
                  disabled={busy === "admin"}
                  onClick={async () => {
                    setBusy("admin");
                    setNotice(null);
                    try {
                      const data = await post("/api/setup/admin", {
                        email: adminEmail,
                        password: adminPassword,
                        displayName: adminName,
                        handle: adminHandle,
                        databaseUrl: databaseUrl.trim() || undefined,
                      });
                      setAdminDone(true);
                      setAdminPassword("");
                      setNotice({
                        kind: "ok",
                        text: data.promoted ? "Existing account promoted to admin." : "Admin account created.",
                      });
                      await refresh();
                    } catch (err) {
                      setNotice({ kind: "err", text: err instanceof Error ? err.message : "Failed." });
                    } finally {
                      setBusy("");
                    }
                  }}
                  className="rounded-lg bg-brand px-4 py-2 text-sm font-semibold text-white hover:bg-brand-hover disabled:opacity-50"
                >
                  {busy === "admin" ? "Saving…" : "Create admin"}
                </button>
                <button
                  onClick={() => setStep(4)}
                  className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover"
                >
                  Continue →
                </button>
              </div>
            </section>
          )}

          {/* STEP 5 — seed */}
          {step === 4 && (
            <section className="space-y-4 rounded-xl border border-line p-5">
              <h2 className="text-lg font-semibold">5 · Demo content (optional)</h2>
              <p className="text-sm text-muted">
                Seeds demo channels, videos, shorts, categories and ads so the homepage isn’t empty.
                Requires the tables from step 3. You can skip this on a production site.
              </p>
              <div className="flex gap-2">
                <button
                  disabled={busy === "seed"}
                  onClick={async () => {
                    setBusy("seed");
                    setNotice(null);
                    try {
                      const data = await post("/api/setup/seed", {});
                      setSeeded(true);
                      setNotice({ kind: "ok", text: String(data.note ?? "Seeded.") });
                      await refresh();
                    } catch (err) {
                      setNotice({ kind: "err", text: err instanceof Error ? err.message : "Seeding failed." });
                    } finally {
                      setBusy("");
                    }
                  }}
                  className="rounded-lg bg-brand px-4 py-2 text-sm font-semibold text-white hover:bg-brand-hover disabled:opacity-50"
                >
                  {busy === "seed" ? "Seeding…" : "Seed demo content"}
                </button>
                <button
                  onClick={() => setStep(5)}
                  className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover"
                >
                  {seeded ? "Continue →" : "Skip →"}
                </button>
              </div>
            </section>
          )}

          {/* STEP 6 — finish */}
          {step === 5 && (
            <section className="space-y-4 rounded-xl border border-line p-5">
              <h2 className="text-lg font-semibold">6 · Finish</h2>
              <p className="text-sm text-muted">
                Writes <code className="rounded bg-chip px-1">install.lock</code>, which locks the installer so
                only signed-in admins can re-run it. Required checks:{" "}
                <strong>{status?.allRequired ? "all green ✅" : "not all green yet ❌"}</strong>
              </p>
              <ul className="space-y-2 text-sm">
                {checks.filter((c) => c.required).map((c) => (
                  <li key={c.id} className="flex items-start gap-2">
                    <span>{c.ok ? "✅" : "❌"}</span>
                    <span><strong>{c.label}</strong> — <span className="text-muted">{c.detail}</span></span>
                  </li>
                ))}
              </ul>
              <div className="flex gap-2">
                <button
                  disabled={busy === "finish"}
                  onClick={async () => {
                    setBusy("finish");
                    setNotice(null);
                    try {
                      await post("/api/setup/finish", {});
                      try { localStorage.removeItem(LS_KEY); } catch {}
                      await refresh();
                      setNotice({ kind: "ok", text: "Installed! 🎉" });
                    } catch (err) {
                      setNotice({ kind: "err", text: err instanceof Error ? err.message : "Finish failed." });
                    } finally {
                      setBusy("");
                    }
                  }}
                  className="rounded-lg bg-brand px-4 py-2 text-sm font-semibold text-white hover:bg-brand-hover disabled:opacity-50"
                >
                  {busy === "finish" ? "Finishing…" : "Finish installation"}
                </button>
                <button
                  onClick={refresh}
                  className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover"
                >
                  Re-check
                </button>
              </div>
            </section>
          )}

          <div className="flex justify-between">
            <button
              disabled={step === 0}
              onClick={() => setStep((s) => Math.max(0, s - 1))}
              className="rounded-lg border border-line px-4 py-2 text-sm font-semibold hover:bg-surface-hover disabled:opacity-50"
            >
              ← Back
            </button>
            <span className="text-xs text-muted">Full cPanel walkthrough: docs/CPANEL.md in the repo</span>
          </div>
        </>
      )}
    </div>
  );
}
