import { useEffect, useMemo, useRef, useState } from "react";
import { apiGet, apiPost, streamNDJSON } from "../../api";
import { PageHead, Empty } from "../../components/ui";
import { IconMusic, IconDisc, IconTrash, IconPlay, IconCalendar, IconCheck, IconChevron, IconWand } from "../../components/icons";
import { useToast } from "../../lib/toast";

interface Override {
  artist: string;
  genre: string;
}

interface Action {
  level: string;
  action: string;
  message: string;
  file?: string;
  group?: string;
  subgroup?: string;
  junk?: string[];
  track?: string | null;
  genre?: string | null;
  path?: string;
}

const MODES = [
  {
    key: "genres",
    label: "Fix genres",
    desc: "Look each album up on MusicBrainz and write one canonical genre to every track",
    icon: <IconMusic />,
  },
  {
    key: "strip_junk",
    label: "Strip junk tags",
    desc: "Remove comment, encoder/tool, URL/purchase and embedded-lyrics frames",
    icon: <IconTrash />,
  },
  {
    key: "normalize_tracks",
    label: "Normalize track numbers",
    desc: "Drop the '/total' suffix and zero-pad track/disc tags (1/12 → 01)",
    icon: <IconDisc />,
  },
] as const;

type ModeKey = (typeof MODES)[number]["key"];

export default function Metadata() {
  const toast = useToast();
  const [root, setRoot] = useState<{ path: string; available: boolean } | null>(null);
  const [modes, setModes] = useState<Record<ModeKey, boolean>>({
    genres: true,
    strip_junk: true,
    normalize_tracks: true,
  });
  const [recentOnly, setRecentOnly] = useState(false);
  const [dryRun, setDryRun] = useState(true);
  const [running, setRunning] = useState(false);
  const [actions, setActions] = useState<Action[]>([]);
  const [showLog, setShowLog] = useState(false);
  const [overrides, setOverrides] = useState<Override[]>([]);
  const [newArtist, setNewArtist] = useState("");
  const [newGenre, setNewGenre] = useState("");

  const runAbort = useRef<AbortController | null>(null);
  const logRef = useRef<HTMLDivElement>(null);

  function loadOverrides() {
    apiGet<{ overrides: Override[] }>("/api/music/metadata/overrides")
      .then((r) => setOverrides(r.overrides || []))
      .catch(() => {});
  }

  useEffect(() => {
    apiGet("/api/config")
      .then((c) => setRoot({ path: c.music.root, available: c.music.available }))
      .catch(() => setRoot(null));
    loadOverrides();
    return () => runAbort.current?.abort();
  }, []);

  async function saveOverride(artist: string, genre: string) {
    if (!artist.trim() || !genre.trim()) return;
    try {
      await apiPost("/api/music/metadata/overrides", { artist: artist.trim(), genre: genre.trim() });
      loadOverrides();
      toast(`Pinned ${artist.trim()} → ${genre.trim()}`, "ok");
    } catch (e: any) {
      toast(e.message || "Could not save override", "err");
    }
  }
  async function removeOverride(artist: string) {
    try {
      await apiPost("/api/music/metadata/overrides/delete", { artist });
      loadOverrides();
    } catch (e: any) {
      toast(e.message || "Could not remove override", "err");
    }
  }
  async function addOverride() {
    await saveOverride(newArtist, newGenre);
    setNewArtist("");
    setNewGenre("");
  }
  useEffect(() => {
    if (showLog) logRef.current?.scrollTo({ top: logRef.current.scrollHeight });
  }, [actions, showLog]);

  const anyMode = Object.values(modes).some(Boolean);

  // ── derive table rows, grouped by album, plus live stats ──────────────────
  const rows = useMemo(() => actions.filter((a) => a.action === "tag" && a.file), [actions]);
  const statusLog = useMemo(() => actions.filter((a) => a.action !== "tag"), [actions]);

  const stats = useMemo(() => {
    let junk = 0;
    let tracks = 0;
    let genres = 0;
    for (const r of rows) {
      junk += r.junk?.length || 0;
      if (r.track) tracks += 1;
      if (r.genre) genres += 1;
    }
    return { files: rows.length, junk, tracks, genres };
  }, [rows]);

  const groups = useMemo(() => {
    const out: { key: string; group: string; subgroup: string; rows: Action[] }[] = [];
    const index = new Map<string, number>();
    for (const r of rows) {
      const key = `${r.subgroup || ""}//${r.group || ""}`;
      let i = index.get(key);
      if (i === undefined) {
        i = out.length;
        index.set(key, i);
        out.push({ key, group: r.group || "Unknown album", subgroup: r.subgroup || "", rows: [] });
      }
      out[i].rows.push(r);
    }
    return out;
  }, [rows]);

  const overriddenKeys = useMemo(
    () => new Set(overrides.map((o) => o.artist.trim().toLowerCase())),
    [overrides]
  );

  const lastStatus = statusLog.length ? statusLog[statusLog.length - 1].message : "";

  async function run() {
    if (!anyMode) return;
    if (!dryRun) {
      const scope = recentOnly ? "recently-changed albums" : "your whole music library";
      if (!window.confirm(`Apply mode is ON. This will permanently rewrite tags in ${scope}. Continue?`)) return;
    }
    setRunning(true);
    setActions([]);
    const ctrl = new AbortController();
    runAbort.current = ctrl;
    try {
      await streamNDJSON("/api/music/metadata/process/stream", {
        method: "POST",
        body: { ...modes, dry_run: dryRun, recent_only: recentOnly },
        signal: ctrl.signal,
        onMessage: (msg: Action) => setActions((prev) => [...prev, msg]),
      });
      toast(dryRun ? "Dry run complete" : "Tags updated", dryRun ? "info" : "ok");
    } catch (e: any) {
      if (e?.name !== "AbortError") toast(e.message || "Run failed", "err");
    } finally {
      setRunning(false);
      runAbort.current = null;
    }
  }

  const logClass = (a: Action) =>
    ({ ok: "log-ok", dry: "log-dry", skip: "log-skip", warn: "log-warn", info: "log-info" }[a.level] || "log-info");

  return (
    <>
      <PageHead title="Metadata Editor" icon={<IconMusic />} />

      {root && !root.available && (
        <div className="panel" style={{ marginBottom: 18 }}>
          <Empty icon={<IconMusic />}>
            Music root not mounted: <code>{root.path}</code>. Set <code>MUSIC_ROOT</code> and mount the share.
          </Empty>
        </div>
      )}

      <div className="workbench" style={{ gridTemplateColumns: "330px 1fr", alignItems: "start" }}>
        {/* ── controls ── */}
        <div className="col">
          <div className="panel">
            <div className="panel-head">
              <h3>What to do</h3>
            </div>
            <div className="panel-body col" style={{ gap: 10 }}>
              {MODES.map((m) => (
                <label
                  key={m.key}
                  className={`chip ${modes[m.key] ? "active" : ""}`}
                  style={{ justifyContent: "flex-start", padding: "11px 13px", cursor: "pointer" }}
                >
                  <input
                    type="checkbox"
                    checked={modes[m.key]}
                    onChange={(e) => setModes((s) => ({ ...s, [m.key]: e.target.checked }))}
                    style={{ marginRight: 4 }}
                  />
                  <span style={{ flexShrink: 0 }}>{m.icon}</span>
                  <span style={{ textAlign: "left" }}>
                    <div style={{ fontWeight: 600, color: "var(--text)" }}>{m.label}</div>
                    <div className="hint">{m.desc}</div>
                  </span>
                </label>
              ))}

              <label className={`chip ${recentOnly ? "active" : ""}`} style={{ justifyContent: "flex-start", padding: "11px 13px", cursor: "pointer" }}>
                <input type="checkbox" checked={recentOnly} onChange={(e) => setRecentOnly(e.target.checked)} style={{ marginRight: 4 }} />
                <span style={{ flexShrink: 0 }}>
                  <IconCalendar />
                </span>
                <span style={{ textAlign: "left" }}>
                  <div style={{ fontWeight: 600, color: "var(--text)" }}>Only recent</div>
                  <div className="hint">Only albums changed in the last 2 hours</div>
                </span>
              </label>
            </div>
          </div>

          <div className="panel">
            <div className="panel-body col">
              <label className={`chip ${dryRun ? "active" : ""}`} style={{ justifyContent: "space-between", cursor: "pointer" }}>
                <span>
                  <div style={{ fontWeight: 700, color: dryRun ? "var(--accent-h)" : "var(--red)" }}>
                    {dryRun ? "Dry run (safe)" : "Apply changes (live)"}
                  </div>
                  <div className="hint">{dryRun ? "Preview only — no tags written" : "Will rewrite tags in your files"}</div>
                </span>
                <input type="checkbox" checked={!dryRun} onChange={(e) => setDryRun(!e.target.checked)} />
              </label>

              {running ? (
                <button className="btn btn-danger btn-block" onClick={() => runAbort.current?.abort()}>
                  <span className="spinner" /> Stop
                </button>
              ) : (
                <button
                  className={`btn ${dryRun ? "btn-primary" : "btn-danger"} btn-block`}
                  onClick={() => run()}
                  disabled={!anyMode || (root ? !root.available : false)}
                >
                  <IconPlay /> {dryRun ? "Preview run" : "Run now"}
                </button>
              )}
              <p className="hint">
                {dryRun
                  ? "Preview shows exactly which tags would change — nothing is written."
                  : "Live mode rewrites tags immediately. Genre lookups query MusicBrainz (~1 album/sec)."}
              </p>
            </div>
          </div>

          <div className="panel">
            <div className="panel-head">
              <h3 className="grow">Genre overrides</h3>
              <span className="sub">{overrides.length}</span>
            </div>
            <div className="panel-body col" style={{ gap: 8 }}>
              <p className="hint">
                Force a genre for an artist — overrides always win over MusicBrainz, so you have full control.
              </p>
              {overrides.length > 0 && (
                <div className="col" style={{ gap: 6 }}>
                  {overrides.map((o) => (
                    <div key={o.artist} className="row" style={{ gap: 8, alignItems: "center" }}>
                      <span
                        className="cell-strong"
                        style={{ flex: "1 1 auto", minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}
                        title={o.artist}
                      >
                        {o.artist}
                      </span>
                      <span className="badge badge-ok">{o.genre}</span>
                      <button className="btn btn-sm" title="Remove override" onClick={() => removeOverride(o.artist)}>
                        <IconTrash />
                      </button>
                    </div>
                  ))}
                </div>
              )}
              <div className="row" style={{ gap: 6 }}>
                <input
                  className="input"
                  placeholder="Artist"
                  value={newArtist}
                  onChange={(e) => setNewArtist(e.target.value)}
                  style={{ flex: 1, minWidth: 0 }}
                />
                <input
                  className="input"
                  placeholder="Genre"
                  value={newGenre}
                  onChange={(e) => setNewGenre(e.target.value)}
                  onKeyDown={(e) => e.key === "Enter" && addOverride()}
                  style={{ flex: 1, minWidth: 0 }}
                />
              </div>
              <button className="btn btn-sm btn-block" onClick={addOverride} disabled={!newArtist.trim() || !newGenre.trim()}>
                <IconCheck /> Add override
              </button>
            </div>
          </div>
        </div>

        {/* ── results ── */}
        <div className="col" style={{ minWidth: 0 }}>
          <div className="stat-row" style={{ gridTemplateColumns: "repeat(4, 1fr)" }}>
            <Tile icon={<IconCheck />} value={stats.files} label="Files changed" />
            <Tile icon={<IconTrash />} value={stats.junk} label="Junk tags removed" />
            <Tile icon={<IconDisc />} value={stats.tracks} label="Tracks renumbered" />
            <Tile icon={<IconMusic />} value={stats.genres} label="Genres set" />
          </div>

          <div className="panel" style={{ display: "flex", flexDirection: "column", minHeight: 0 }}>
            <div className="panel-head">
              <h3 className="grow">{dryRun ? "Planned changes" : "Applied changes"}</h3>
              <span className="sub">
                {running ? (
                  <>
                    <span className="spinner" /> {lastStatus || "working…"}
                  </>
                ) : (
                  `${groups.length} album(s) · ${rows.length} file(s)`
                )}
              </span>
            </div>
            <div style={{ overflow: "auto", maxHeight: "calc(100vh - 430px)", minHeight: 340 }}>
              {rows.length === 0 ? (
                <Empty icon={<IconMusic />}>
                  {running ? "Scanning your library…" : "Pick what to do on the left, then Run. Per-file changes appear here as a table."}
                </Empty>
              ) : (
                <table className="data-table">
                  <thead>
                    <tr>
                      {["Track", "Junk stripped", "Track #", "Genre"].map((h, i) => (
                        <th
                          key={h}
                          style={{
                            position: "sticky",
                            top: 0,
                            zIndex: 1,
                            background: "var(--surface)",
                            width: i === 0 ? "40%" : undefined,
                          }}
                        >
                          {h}
                        </th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {groups.map((g) => (
                      <GroupBlock
                        key={g.key}
                        group={g}
                        overridden={overriddenKeys.has((g.subgroup || "").trim().toLowerCase())}
                        onPin={saveOverride}
                      />
                    ))}
                  </tbody>
                </table>
              )}
            </div>
          </div>

          {statusLog.length > 0 && (
            <div className="panel">
              <button
                className="panel-head"
                onClick={() => setShowLog((s) => !s)}
                style={{ width: "100%", background: "none", border: 0, cursor: "pointer", color: "inherit" }}
              >
                <h3 className="grow" style={{ textAlign: "left" }}>
                  Activity log
                </h3>
                <span className="sub">{statusLog.length} entries</span>
                <IconChevron className={`nav-caret ${showLog ? "open" : ""}`} />
              </button>
              {showLog && (
                <div className="panel-body">
                  <div className="console" ref={logRef} style={{ maxHeight: 240 }}>
                    {statusLog.map((a, i) => (
                      <div className="log-line" key={i}>
                        <span className={`log-tag ${logClass(a)}`}>{a.level === "dry" ? "plan" : a.level}</span>
                        <span>{a.message}</span>
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </div>
          )}
        </div>
      </div>
    </>
  );
}

function Tile({ icon, value, label }: { icon: JSX.Element; value: number; label: string }) {
  return (
    <div className="stat-card">
      <div className="stat-icon">{icon}</div>
      <div className="stat-meta">
        <div className="stat-value">{value.toLocaleString()}</div>
        <div className="stat-label">{label}</div>
      </div>
    </div>
  );
}

function GroupBlock({
  group,
  overridden,
  onPin,
}: {
  group: { group: string; subgroup: string; rows: Action[] };
  overridden: boolean;
  onPin: (artist: string, genre: string) => void;
}) {
  const groupGenre = group.rows.find((r) => r.genre)?.genre || null;
  const canPin = !!(group.subgroup && groupGenre && !overridden);
  return (
    <>
      <tr>
        <td colSpan={4} style={{ background: "var(--surface3)", padding: "9px 14px" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
            <span className="cell-strong">{group.group}</span>
            {group.subgroup && <span className="cell-sub">{group.subgroup}</span>}
            {overridden && <span className="badge badge-accent">override</span>}
            <span className="badge" style={{ marginLeft: "auto" }}>
              {group.rows.length} file{group.rows.length === 1 ? "" : "s"}
            </span>
            {canPin && (
              <button
                className="btn btn-sm"
                title={`Pin ${group.subgroup} → ${groupGenre} as a permanent override`}
                onClick={() => onPin(group.subgroup, groupGenre as string)}
              >
                <IconWand /> Pin genre
              </button>
            )}
          </div>
        </td>
      </tr>
      {group.rows.map((r, i) => (
        <tr key={r.path || `${group.group}-${i}`}>
          <td>
            <div className="cell-strong" style={{ wordBreak: "break-word" }}>
              {r.file}
            </div>
          </td>
          <td>
            {r.junk && r.junk.length > 0 ? (
              <div className="row wrap" style={{ gap: 6, alignItems: "center" }}>
                <span className="badge badge-warn">{r.junk.length}</span>
                <span className="cell-sub" title={r.junk.join(", ")} style={{ wordBreak: "break-word" }}>
                  {r.junk.slice(0, 4).join(", ")}
                  {r.junk.length > 4 ? ` +${r.junk.length - 4} more` : ""}
                </span>
              </div>
            ) : (
              <span className="cell-sub">—</span>
            )}
          </td>
          <td>{r.track ? <span className="badge badge-accent mono">{r.track}</span> : <span className="cell-sub">—</span>}</td>
          <td>{r.genre ? <span className="badge badge-ok">{r.genre}</span> : <span className="cell-sub">—</span>}</td>
        </tr>
      ))}
    </>
  );
}
