"use client";

import { useEffect, useMemo, useState } from "react";
import { PageHead } from "../_ui/page-head";
import { SectionHeader } from "../_ui/section-header";
import { Modal } from "../_ui/modal";
import { toast } from "../_ui/toast";

type Artist = {
  id: string;
  mbid: string;
  name: string;
  createdAt: string;
  autoMonitorFuture: boolean;
  releaseCount: number;
  monitoredCount: number;
  haveCount: number;
};
type Hit = { mbid: string; name: string; disambiguation: string };

type SortKey = "followed" | "name" | "have" | "monitored" | "releases";
const SORTS: { key: SortKey; label: string; cmp: (a: Artist, b: Artist) => number }[] = [
  { key: "followed", label: "Recently followed", cmp: (a, b) => b.createdAt.localeCompare(a.createdAt) },
  { key: "name", label: "Name A–Z", cmp: (a, b) => a.name.localeCompare(b.name) },
  { key: "have", label: "Most in library", cmp: (a, b) => b.haveCount - a.haveCount || a.name.localeCompare(b.name) },
  { key: "monitored", label: "Most monitored", cmp: (a, b) => b.monitoredCount - a.monitoredCount || a.name.localeCompare(b.name) },
  { key: "releases", label: "Most releases", cmp: (a, b) => b.releaseCount - a.releaseCount || a.name.localeCompare(b.name) },
];

export function ArtistsClient() {
  const [artists, setArtists] = useState<Artist[]>([]);
  const [ownedNotFollowed, setOwnedNotFollowed] = useState<string[]>([]);
  const [justFollowed, setJustFollowed] = useState<Set<string>>(new Set());
  const [filter, setFilter] = useState("");
  const [sort, setSort] = useState<SortKey>("followed");
  const [showAdd, setShowAdd] = useState(false);
  const [query, setQuery] = useState("");
  const [hits, setHits] = useState<Hit[]>([]);
  const [searching, setSearching] = useState(false);
  const [searched, setSearched] = useState(false);

  async function refresh() {
    const res = await fetch("/api/artists");
    const data = await res.json();
    setArtists(data.artists);
    setOwnedNotFollowed(data.ownedNotFollowed ?? []);
  }
  useEffect(() => {
    refresh();
  }, []);

  async function search(e: React.FormEvent) {
    e.preventDefault();
    if (!query.trim()) return;
    setSearching(true);
    try {
      const res = await fetch(`/api/mb/artists?q=${encodeURIComponent(query.trim())}`);
      setHits(res.ok ? (await res.json()).artists : []);
      setSearched(true);
    } finally {
      setSearching(false);
    }
  }

  function openAdd() {
    setShowAdd(true);
  }
  function closeAdd() {
    setShowAdd(false);
    setQuery("");
    setHits([]);
    setSearched(false);
  }

  async function follow(hit: Hit) {
    await fetch("/api/artists", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ mbid: hit.mbid, name: hit.name }),
    });
    toast(`Following ${hit.name}`);
    refresh(); // keep the modal open so several can be added; the hit flips to "Following"
  }

  // Follow an owned-but-not-followed artist: resolve their name → MBID (the library only
  // stores names), then follow with the canonical MB name. Mirrors the Last.fm/discover flow.
  async function followOwned(name: string) {
    const res = await fetch(`/api/mb/artists?q=${encodeURIComponent(name)}`);
    const hit = res.ok ? (await res.json()).artists?.[0] : null;
    if (!hit?.mbid) {
      toast(`No MusicBrainz match for ${name}`);
      return;
    }
    const r = await fetch("/api/artists", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ mbid: hit.mbid, name: hit.name }),
    });
    if (r.ok || r.status === 409) {
      setJustFollowed((s) => new Set(s).add(name)); // hide it immediately, even if names differ
      toast(`Following ${hit.name}`);
      refresh();
    } else {
      toast(`Couldn't follow ${name}`);
    }
  }

  async function toggleAuto(a: Artist) {
    const res = await fetch(`/api/artists/${a.id}`, {
      method: "PATCH",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ autoMonitorFuture: !a.autoMonitorFuture }),
    }).catch(() => null);
    if (!res?.ok) toast(`Couldn't update ${a.name}`);
    refresh();
  }

  async function unfollow(a: Artist) {
    const res = await fetch(`/api/artists/${a.id}`, { method: "DELETE" }).catch(() => null);
    if (!res?.ok) toast(`Couldn't unfollow ${a.name}`);
    refresh();
  }

  const followedMbids = new Set(artists.map((a) => a.mbid));
  const shown = useMemo(() => {
    const n = filter.trim().toLowerCase();
    const filtered = n ? artists.filter((a) => a.name.toLowerCase().includes(n)) : artists;
    const cmp = (SORTS.find((s) => s.key === sort) ?? SORTS[0]).cmp;
    return [...filtered].sort(cmp);
  }, [artists, filter, sort]);
  const ownedShown = useMemo(
    () => ownedNotFollowed.filter((name) => !justFollowed.has(name)),
    [ownedNotFollowed, justFollowed],
  );

  return (
    <div>
      <PageHead title="Artists" eyebrow="The roster · follow & watch" />

      <div className="request-form">
        <button type="button" className="btn accent" onClick={openAdd}>
          Add artist
        </button>
        <label className="field">
          <span>Filter</span>
          <input
            aria-label="filter artists"
            placeholder="Filter watched…"
            value={filter}
            onChange={(e) => setFilter(e.target.value)}
          />
        </label>
        <label className="field">
          <span>Sort</span>
          <select aria-label="sort artists" 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>
      </div>

      <SectionHeader
        title="Watching"
        note={filter ? `${shown.length} of ${artists.length}` : `${artists.length} artists`}
      />
      {artists.length === 0 ? (
        <p className="empty">Not following anyone yet. Use “Add artist” to search MusicBrainz and follow someone.</p>
      ) : shown.length === 0 ? (
        <p className="empty">No watched artists match “{filter}”.</p>
      ) : (
        <ul className="list">
          {shown.map((a) => (
            <li key={a.id} className="list-row">
              <div className="main">
                <div className="rtitle">
                  <a href={`/artists/${a.id}`}>{a.name}</a>
                </div>
                <div className="rmeta">
                  <span className="score">
                    {a.haveCount} have <span className="dot">·</span> {a.monitoredCount} monitored{" "}
                    <span className="dot">·</span> {a.releaseCount} releases
                  </span>
                </div>
              </div>
              <div className="actions">
                <label className="toggle">
                  <input
                    type="checkbox"
                    aria-label={`auto-monitor ${a.name}`}
                    checked={a.autoMonitorFuture}
                    onChange={() => toggleAuto(a)}
                  />
                  auto-monitor
                </label>
                <button className="btn sm ghost" onClick={() => unfollow(a)}>
                  Unfollow
                </button>
              </div>
            </li>
          ))}
        </ul>
      )}

      {ownedShown.length > 0 ? (
        <>
          <SectionHeader
            title="In library, not followed"
            note={`${ownedShown.length}`}
          />
          <p className="muted-note">
            Artists you already own music by but aren’t watching. Follow to monitor them for new
            releases and upgrades.
          </p>
          <ul className="list">
            {ownedShown.map((name) => (
              <li key={name} className="list-row">
                <div className="main">
                  <div className="rtitle">{name}</div>
                </div>
                <div className="actions">
                  <button className="btn sm" onClick={() => followOwned(name)}>
                    Follow
                  </button>
                </div>
              </li>
            ))}
          </ul>
        </>
      ) : null}

      {showAdd ? (
        <Modal open onClose={closeAdd} title="Add artist">
          <form className="request-form" style={{ margin: "0 0 6px" }} onSubmit={search}>
            <label className="field">
              <span>Find an artist</span>
              {/* eslint-disable-next-line jsx-a11y/no-autofocus */}
              <input
                aria-label="artist search"
                placeholder="Search MusicBrainz…"
                value={query}
                onChange={(e) => setQuery(e.target.value)}
                autoFocus
              />
            </label>
            <button type="submit" className="btn accent">
              Search
            </button>
            {searching ? <span className="rmeta">searching…</span> : null}
          </form>

          {hits.length > 0 ? (
            <ul className="list">
              {hits.map((h) => (
                <li key={h.mbid} className="list-row">
                  <div className="main">
                    <div className="rtitle">
                      <a href={`/discover/artist/${h.mbid}?name=${encodeURIComponent(h.name)}`}>{h.name}</a>
                      {h.disambiguation ? <span className="dim"> — {h.disambiguation}</span> : null}
                    </div>
                  </div>
                  <div className="actions">
                    {followedMbids.has(h.mbid) ? (
                      <span className="following">Following</span>
                    ) : (
                      <button className="btn sm" onClick={() => follow(h)}>
                        Follow
                      </button>
                    )}
                  </div>
                </li>
              ))}
            </ul>
          ) : searched && !searching ? (
            <p className="muted-note">No matches on MusicBrainz.</p>
          ) : null}
        </Modal>
      ) : null}
    </div>
  );
}
