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

interface Album {
  path: string;
  folder_name: string;
  artist: string;
  album: string;
  year: string | null;
  track_count: number;
  has_cover: boolean;
  needs_folder_rename: boolean;
  extra_file_count: number;
}
interface Action {
  level: string;
  action: string;
  message: string;
}

const MODES = [
  { key: "folder_cleanup", label: "Folder rename", desc: "Normalize album folders to 'YEAR - Album'", icon: <IconFolder /> },
  { key: "rename", label: "Rename tracks", desc: "Rename audio files to 'NN - Title'", icon: <IconDisc /> },
  { key: "covers", label: "Fetch covers", desc: "Download missing cover.jpg (Cover Art Archive)", icon: <IconImage /> },
  { key: "lyrics", label: "Fetch lyrics", desc: "Download .lrc / .txt sidecars (LRCLIB)", icon: <IconWand /> },
  { key: "file_cleanup", label: "File cleanup", desc: "Remove non-audio / non-art files", icon: <IconTrash /> },
] as const;

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

export default function CoverManager() {
  const toast = useToast();
  const [root, setRoot] = useState<{ path: string; available: boolean } | null>(null);
  const [modes, setModes] = useState<Record<ModeKey, boolean>>({
    folder_cleanup: true,
    rename: true,
    covers: true,
    lyrics: false,
    file_cleanup: false,
  });
  const [recentOnly, setRecentOnly] = useState(false);
  const [dryRun, setDryRun] = useState(true);
  const [running, setRunning] = useState(false);
  const [actions, setActions] = useState<Action[]>([]);

  const [albums, setAlbums] = useState<Album[]>([]);
  const [scanning, setScanning] = useState(false);
  const [selected, setSelected] = useState<Set<string>>(new Set());

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

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

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

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

  async function previewScan() {
    setScanning(true);
    setAlbums([]);
    setSelected(new Set());
    const ctrl = new AbortController();
    scanAbort.current = ctrl;
    try {
      await streamNDJSON("/api/music/scan/stream", {
        signal: ctrl.signal,
        onMessage: (msg) => {
          if (msg.type === "album") setAlbums((prev) => [...prev, msg.album]);
          else if (msg.type === "error") toast(msg.message, "err");
        },
      });
    } catch (e: any) {
      if (e?.name !== "AbortError") toast(e.message || "Scan failed", "err");
    } finally {
      setScanning(false);
      scanAbort.current = null;
    }
  }

  function toggleSelect(path: string) {
    setSelected((s) => {
      const n = new Set(s);
      n.has(path) ? n.delete(path) : n.add(path);
      return n;
    });
  }

  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="Library Cleanup" icon={<IconWand />} />

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

      <div className="workbench" style={{ gridTemplateColumns: "360px 1fr" }}>
        {/* 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 files touched" : "Will modify files on disk"}</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>
              )}
              {selected.size > 0 && !running && (
                <button className="btn btn-block" onClick={() => run([...selected])} disabled={!anyMode}>
                  <IconCheck /> Run on {selected.size} selected
                </button>
              )}
              <p className="hint">
                {dryRun
                  ? "Preview shows exactly what would change — nothing is modified."
                  : "Live mode renames, deletes and downloads immediately."}
              </p>
            </div>
          </div>
        </div>

        {/* output */}
        <div className="col">
          <div className="panel">
            <div className="panel-head">
              <h3 className="grow">Activity</h3>
              <span className="sub">
                {actions.length} entries{running ? " · running…" : ""}
              </span>
            </div>
            <div className="panel-body">
              <div className="console" ref={logRef} style={{ minHeight: 200 }}>
                {actions.length === 0 ? (
                  <span className="dim">Pick what to do on the left, then Run. Output streams here as it happens.</span>
                ) : (
                  actions.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 className="panel">
            <div className="panel-head">
              <h3 className="grow">Library preview</h3>
              <span className="sub">{albums.length} albums</span>
              {scanning ? (
                <button className="btn btn-sm btn-danger" onClick={() => scanAbort.current?.abort()}>
                  <span className="spinner" /> Stop
                </button>
              ) : (
                <button className="btn btn-sm" onClick={previewScan} disabled={root ? !root.available : false}>
                  <IconRefresh /> Scan
                </button>
              )}
              {albums.length > 0 && (
                <button
                  className="btn btn-sm"
                  onClick={() => setSelected(selected.size === albums.length ? new Set() : new Set(albums.map((a) => a.path)))}
                >
                  {selected.size === albums.length ? "Clear" : "Select all"}
                </button>
              )}
            </div>
            {albums.length === 0 ? (
              <Empty icon={<IconDisc />}>{scanning ? "Scanning…" : "Optional: scan to preview albums and run actions on specific ones."}</Empty>
            ) : (
              <div style={{ maxHeight: 460, overflow: "auto" }}>
                <table className="data-table">
                  <thead>
                    <tr>
                      <th style={{ width: 30 }}></th>
                      <th>Album</th>
                      <th>Year</th>
                      <th>Tracks</th>
                      <th>Status</th>
                      <th style={{ textAlign: "right" }}>Action</th>
                    </tr>
                  </thead>
                  <tbody>
                    {albums.map((a) => (
                      <tr key={a.path}>
                        <td>
                          <input type="checkbox" checked={selected.has(a.path)} onChange={() => toggleSelect(a.path)} />
                        </td>
                        <td>
                          <div className="cell-strong">{a.album || a.folder_name}</div>
                          <div className="cell-sub">{a.artist}</div>
                        </td>
                        <td className="mono">{a.year || "—"}</td>
                        <td className="mono">{a.track_count}</td>
                        <td>
                          <div className="row wrap" style={{ gap: 6 }}>
                            {a.has_cover ? <span className="badge badge-ok">cover</span> : <span className="badge badge-warn">no cover</span>}
                            {a.needs_folder_rename && <span className="badge badge-accent">rename</span>}
                            {a.extra_file_count > 0 && <span className="badge">{a.extra_file_count} extra</span>}
                          </div>
                        </td>
                        <td style={{ textAlign: "right" }}>
                          <button className="btn btn-sm" disabled={running || !anyMode} onClick={() => run([a.path])} title="Run selected modes on this album">
                            <IconWand /> Fix
                          </button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            )}
          </div>
        </div>
      </div>
    </>
  );
}
