"use client";

import { useEffect, useMemo, useState } from "react";
import { PageHead } from "../_ui/page-head";
import { SectionHeader } from "../_ui/section-header";
import { CoverArt } from "../_ui/cover-art";
import { AlbumModal } from "../_ui/album-modal";
import { toast } from "../_ui/toast";
import { ImportAlbum } from "./import-album";

type Album = {
  id: string;
  artist: string;
  album: string;
  format: string;
  qualityClass: number;
  importedAt: string;
  rgMbid: string | null;
  year: string | null;
  primaryType: string | null;
  artistMbid: string | null;
  monitoredReleaseId: string | null;
  trackNames: string[];
};

type SortKey = "added" | "release" | "album" | "artist";
const SORTS: { key: SortKey; label: string; cmp: (a: Album, b: Album) => number }[] = [
  { key: "added", label: "Recently added", cmp: (a, b) => b.importedAt.localeCompare(a.importedAt) },
  // Release date, newest first; albums with an unknown year sort to the bottom.
  { key: "release", label: "Release date", cmp: (a, b) => (b.year ?? "0000").localeCompare(a.year ?? "0000") || a.album.localeCompare(b.album) },
  { key: "album", label: "Album A–Z", cmp: (a, b) => a.album.localeCompare(b.album) },
  { key: "artist", label: "Artist A–Z", cmp: (a, b) => a.artist.localeCompare(b.artist) || (a.year ?? "").localeCompare(b.year ?? "") },
];

type Unmatched = { id: string; artist: string; album: string; path: string; reason: string };
type DupItem = { id: string; artist: string; album: string; format: string; qualityClass: number; year: string | null };
type IntegrityIssue = { id: string; artist: string; album: string; issues: string[] };

export function LibraryClient() {
  const [albums, setAlbums] = useState<Album[]>([]);
  const [unmatched, setUnmatched] = useState<Unmatched[]>([]);
  const [duplicates, setDuplicates] = useState<DupItem[][]>([]);
  const [integrity, setIntegrity] = useState<{ checked: number; issues: IntegrityIssue[] } | null>(null);
  const [checking, setChecking] = useState(false);
  const [selectMode, setSelectMode] = useState(false);
  const [selected, setSelected] = useState<Set<string>>(new Set());
  const [bulkConfirmDelete, setBulkConfirmDelete] = useState(false);

  function toggleSelect(id: string) {
    setSelected((s) => {
      const n = new Set(s);
      if (n.has(id)) n.delete(id); else n.add(id);
      return n;
    });
  }
  function exitSelect() {
    setSelectMode(false);
    setSelected(new Set());
    setBulkConfirmDelete(false);
  }

  async function bulk(action: "delete" | "ignore") {
    const ids = [...selected];
    if (ids.length === 0) return;
    const res = await fetch("/api/library/bulk", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ action, ids }),
    }).catch(() => null);
    if (res?.ok) {
      const body = await res.json();
      toast(
        `${action === "delete" ? "Deleted" : "Ignoring"} ${body.ok} album${body.ok === 1 ? "" : "s"}` +
          (body.failed?.length ? ` · ${body.failed.length} failed` : ""),
      );
      exitSelect();
      refresh();
    } else {
      toast(`Bulk ${action} failed`);
    }
  }
  const [q, setQ] = useState("");
  const [sort, setSort] = useState<SortKey>("added");
  const [open, setOpen] = useState<Album | null>(null);
  const [confirmDelete, setConfirmDelete] = useState(false);
  const [editing, setEditing] = useState(false);
  const [edit, setEdit] = useState({ artist: "", album: "", year: "" });
  const [saving, setSaving] = useState(false);

  function show(a: Album | null) {
    setOpen(a);
    setConfirmDelete(false);
    setEditing(false);
  }

  function startEdit() {
    if (!open) return;
    setEdit({ artist: open.artist, album: open.album, year: open.year ?? "" });
    setEditing(true);
  }

  async function saveEdit() {
    if (!open) return;
    if (!edit.artist.trim() || !edit.album.trim()) {
      toast("Artist and album are required");
      return;
    }
    setSaving(true);
    try {
      const res = await fetch(`/api/library/${open.id}`, {
        method: "PATCH",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ artist: edit.artist.trim(), album: edit.album.trim(), year: edit.year.trim() }),
      }).catch(() => null);
      if (res?.ok) {
        toast(`Updated ${edit.album.trim()}`);
        setEditing(false);
        show(null);
        refresh();
      } else {
        toast((res && (await res.json().catch(() => null))?.error) || "Couldn't update album");
      }
    } finally {
      setSaving(false);
    }
  }

  async function remove() {
    if (!open) return;
    const target = open;
    const res = await fetch(`/api/library/${target.id}`, { method: "DELETE" }).catch(() => null);
    if (res?.ok) {
      setAlbums((list) => list.filter((a) => a.id !== target.id));
      toast(`Deleted ${target.album}`);
      show(null);
    } else {
      toast(`Couldn't delete ${target.album}`);
    }
  }

  function refresh() {
    fetch("/api/library")
      .then((r) => r.json())
      .then((d) => setAlbums(d.albums ?? []));
    fetch("/api/library/unmatched")
      .then((r) => r.json())
      .then((d) => setUnmatched(d.unmatched ?? []));
    fetch("/api/library/duplicates")
      .then((r) => r.json())
      .then((d) => setDuplicates(d.duplicates ?? []));
  }
  useEffect(() => {
    refresh();
  }, []);

  async function upgrade() {
    if (!open) return;
    const res = await fetch(`/api/library/${open.id}/upgrade`, { method: "POST" }).catch(() => null);
    if (res && (res.status === 202 || res.ok)) {
      const body = await res.json().catch(() => null);
      toast(body?.enqueued === false ? `Already searching for ${open.album}` : `Re-acquiring ${open.album} — keeps it only if better`);
      show(null);
    } else {
      toast((res && (await res.json().catch(() => null))?.error) || "Couldn't start the upgrade");
    }
  }

  async function checkIntegrity() {
    setChecking(true);
    try {
      const res = await fetch("/api/library/integrity").catch(() => null);
      if (res?.ok) {
        setIntegrity(await res.json());
      } else {
        toast("Couldn't run the integrity check");
      }
    } finally {
      setChecking(false);
    }
  }

  async function dismissUnmatched(u: Unmatched) {
    const res = await fetch(`/api/library/unmatched?id=${u.id}`, { method: "DELETE" }).catch(() => null);
    if (res?.ok) {
      setUnmatched((list) => list.filter((x) => x.id !== u.id));
    } else {
      toast("Couldn't dismiss");
    }
  }

  const shown = useMemo(() => {
    const needle = q.trim().toLowerCase();
    const filtered = needle
      ? albums.filter((a) => `${a.artist} ${a.album}`.toLowerCase().includes(needle))
      : albums;
    const cmp = (SORTS.find((s) => s.key === sort) ?? SORTS[0]).cmp;
    return [...filtered].sort(cmp);
  }, [albums, q, sort]);

  return (
    <div>
      <PageHead title="Library" eyebrow="The pressings · everything on disk" />

      <form className="request-form" onSubmit={(e) => e.preventDefault()}>
        <ImportAlbum onImported={refresh} />
        <label className="field">
          <span>Filter</span>
          <input aria-label="filter library" placeholder="Artist or album…" value={q} onChange={(e) => setQ(e.target.value)} />
        </label>
        <label className="field">
          <span>Sort</span>
          <select aria-label="sort library" value={sort} onChange={(e) => setSort(e.target.value as SortKey)}>
            {SORTS.map((s) => (
              <option key={s.key} value={s.key}>{s.label}</option>
            ))}
          </select>
        </label>
      </form>

      {albums.length === 0 ? (
        <p className="empty">Nothing pressed yet. Scan your library in Settings, or queue a release from The Floor.</p>
      ) : (
        <SectionHeader
          title="Pressings"
          note={
            <span className="sel-tools">
              <span>{q ? `${shown.length} of ${albums.length}` : `${albums.length} albums`}</span>
              <button className="btn sm ghost" onClick={() => (selectMode ? exitSelect() : setSelectMode(true))}>
                {selectMode ? "Cancel" : "Select"}
              </button>
            </span>
          }
        />
      )}

      {selectMode ? (
        <div className="bulk-bar">
          <span>{selected.size} selected</span>
          {bulkConfirmDelete ? (
            <>
              <span className="rmeta">Delete {selected.size} album{selected.size === 1 ? "" : "s"} and their files?</span>
              <button className="btn sm accent" onClick={() => bulk("delete")} disabled={selected.size === 0}>Delete</button>
              <button className="btn sm ghost" onClick={() => setBulkConfirmDelete(false)}>Cancel</button>
            </>
          ) : (
            <>
              <button className="btn sm ghost" onClick={() => setBulkConfirmDelete(true)} disabled={selected.size === 0}>Delete selected</button>
              <button className="btn sm ghost" onClick={() => bulk("ignore")} disabled={selected.size === 0}>Ignore selected</button>
            </>
          )}
        </div>
      ) : null}

      {albums.length > 0 ? (
        <div className="album-grid">
          {shown.map((a) => (
            <button
              key={a.id}
              className={`album-card${selectMode && selected.has(a.id) ? " selected" : ""}`}
              onClick={() => (selectMode ? toggleSelect(a.id) : show(a))}
              aria-label={selectMode ? `Select ${a.album}` : `Open ${a.album}`}
              aria-pressed={selectMode ? selected.has(a.id) : undefined}
            >
              {selectMode ? <span className="sel-check" aria-hidden>{selected.has(a.id) ? "✓" : ""}</span> : null}
              <CoverArt rgMbid={a.rgMbid} alt={a.album} size={250} />
              <div className="ac-title">{a.album}</div>
              <div className="ac-artist">{a.artist}</div>
              <div className="ac-meta">
                {a.format}
                {a.qualityClass >= 3 ? " · hi-res" : ""}
                {a.year ? ` · ${a.year}` : ""}
              </div>
            </button>
          ))}
        </div>
      ) : null}

      {albums.length > 0 ? (
        <>
          <SectionHeader
            title="Integrity"
            note={
              <button className="btn sm ghost" onClick={checkIntegrity} disabled={checking}>
                {checking ? "Checking…" : "Check now"}
              </button>
            }
          />
          {integrity ? (
            integrity.issues.length === 0 ? (
              <p className="muted-note">Checked {integrity.checked} albums — no problems found.</p>
            ) : (
              <ul className="list">
                {integrity.issues.map((it) => {
                  const album = albums.find((a) => a.id === it.id);
                  return (
                    <li key={it.id} className="list-row">
                      <div className="main">
                        {album ? (
                          <button className="linkish rtitle" onClick={() => show(album)}>
                            {it.artist} — {it.album}
                          </button>
                        ) : (
                          <div className="rtitle">{it.artist} — {it.album}</div>
                        )}
                        <div className="rmeta">
                          <span className="score">{it.issues.join(" · ")}</span>
                        </div>
                      </div>
                    </li>
                  );
                })}
              </ul>
            )
          ) : (
            <p className="muted-note">
              Check every album folder on disk for missing folders, empty/truncated files, and
              track-count drift.
            </p>
          )}
        </>
      ) : null}

      {duplicates.length > 0 ? (
        <>
          <SectionHeader title="Possible duplicates" note={`${duplicates.length}`} />
          <p className="muted-note">
            Albums that look like the same release held more than once (same MusicBrainz release,
            or the same title ignoring edition wording). Open one to edit or delete it.
          </p>
          {duplicates.map((group, gi) => (
            <ul className="list" key={gi} style={{ marginBottom: 10 }}>
              {group.map((d) => {
                const album = albums.find((a) => a.id === d.id);
                return (
                  <li key={d.id} className="list-row">
                    <div className="main">
                      {album ? (
                        <button className="linkish rtitle" onClick={() => show(album)}>
                          {d.artist} — {d.album}
                        </button>
                      ) : (
                        <div className="rtitle">{d.artist} — {d.album}</div>
                      )}
                      <div className="rmeta">
                        <span className="score">
                          {d.format}
                          {d.qualityClass >= 3 ? " · hi-res" : ""}
                          {d.year ? ` · ${d.year}` : ""}
                        </span>
                      </div>
                    </div>
                  </li>
                );
              })}
            </ul>
          ))}
        </>
      ) : null}

      {unmatched.length > 0 ? (
        <>
          <SectionHeader title="Couldn’t match" note={`${unmatched.length}`} />
          <p className="muted-note">
            Album folders on disk the scan couldn’t resolve to a MusicBrainz release. Fix the
            folder name (Artist / Album (Year)) or re-add them manually, then re-scan. Dismiss to
            hide an entry.
          </p>
          <ul className="list">
            {unmatched.map((u) => (
              <li key={u.id} className="list-row">
                <div className="main">
                  <div className="rtitle">
                    {u.artist ? `${u.artist} — ` : ""}
                    {u.album}
                  </div>
                  <div className="rmeta">
                    <span className="dim">{u.path}</span> <span className="dot">·</span>{" "}
                    <span className="score">{u.reason}</span>
                  </div>
                </div>
                <div className="actions">
                  <button className="btn sm ghost" onClick={() => dismissUnmatched(u)}>
                    Dismiss
                  </button>
                </div>
              </li>
            ))}
          </ul>
        </>
      ) : null}

      {open ? (
        <AlbumModal
          open
          onClose={() => show(null)}
          album={{ rgMbid: open.rgMbid, album: open.album, artist: open.artist, year: open.year, type: open.primaryType, artistMbid: open.artistMbid, trackNames: open.trackNames }}
          status={<span className="chip done">In library · {open.format}</span>}
          actions={
            editing ? (
              <div className="edit-meta">
                <label className="field">
                  <span>Artist</span>
                  <input aria-label="edit artist" value={edit.artist} onChange={(e) => setEdit((s) => ({ ...s, artist: e.target.value }))} />
                </label>
                <label className="field">
                  <span>Album</span>
                  <input aria-label="edit album" value={edit.album} onChange={(e) => setEdit((s) => ({ ...s, album: e.target.value }))} />
                </label>
                <label className="field">
                  <span>Year</span>
                  <input aria-label="edit year" placeholder="YYYY" value={edit.year} onChange={(e) => setEdit((s) => ({ ...s, year: e.target.value }))} />
                </label>
                <div className="save-row">
                  <button className="btn sm accent" onClick={saveEdit} disabled={saving}>
                    {saving ? "Saving…" : "Save"}
                  </button>
                  <button className="btn sm ghost" onClick={() => setEditing(false)} disabled={saving}>
                    Cancel
                  </button>
                </div>
                <p className="muted-note">Renames the folder on disk and re-resolves cover art. Embedded file tags are left as-is.</p>
              </div>
            ) : confirmDelete ? (
              <>
                <span className="rmeta">Delete this album and its files from disk?</span>
                <button className="btn sm accent" onClick={remove}>
                  Delete
                </button>
                <button className="btn sm ghost" onClick={() => setConfirmDelete(false)}>
                  Cancel
                </button>
              </>
            ) : (
              <>
                {open.monitoredReleaseId ? (
                  <button className="btn sm ghost" onClick={upgrade}>
                    Replace / upgrade
                  </button>
                ) : null}
                <button className="btn sm ghost" onClick={startEdit}>
                  Edit metadata
                </button>
                <button className="btn sm ghost" onClick={() => setConfirmDelete(true)}>
                  Delete album
                </button>
              </>
            )
          }
        />
      ) : null}
    </div>
  );
}
