"use client";

import { useRef, useState } from "react";
import { Modal } from "../_ui/modal";
import { toast } from "../_ui/toast";

const AUDIO_RE = /\.(flac|mp3|m4a|opus|ogg|aac|wav)$/i;

// A dropped item may be a whole folder; the plain dataTransfer.files doesn't recurse, so walk
// the entry tree with webkitGetAsEntry to collect files inside a dropped album folder.
function readEntries(reader: {
  readEntries: (cb: (e: FileSystemEntry[]) => void, err: (e: unknown) => void) => void;
}): Promise<FileSystemEntry[]> {
  return new Promise((res, rej) => reader.readEntries(res, rej));
}

async function walkEntry(entry: FileSystemEntry, out: File[]): Promise<void> {
  if (entry.isFile) {
    const file = await new Promise<File>((res, rej) => (entry as FileSystemFileEntry).file(res, rej));
    out.push(file);
  } else if (entry.isDirectory) {
    const reader = (entry as FileSystemDirectoryEntry).createReader();
    for (let batch = await readEntries(reader); batch.length; batch = await readEntries(reader)) {
      for (const e of batch) await walkEntry(e, out);
    }
  }
}

/** Parse "Artist - Album (Year)" / "Album (Year)" from a dropped folder name. */
function parseFolder(name: string): { artist?: string; album: string; year?: string } {
  let s = name.trim();
  let year: string | undefined;
  const ym = s.match(/[([]?(\d{4})[)\]]?\s*$/);
  if (ym) {
    year = ym[1];
    s = s.slice(0, ym.index).trim().replace(/[-–—([]\s*$/, "").trim();
  }
  const dash = s.split(/\s+[-–—]\s+/);
  if (dash.length >= 2) return { artist: dash[0].trim(), album: dash.slice(1).join(" - ").trim(), year };
  return { album: s, year };
}

/** Manually add an album (ripped CD / local files): drag-drop the folder (or audio files) +
 *  fill in artist/album/year, POST to /api/library/import. */
export function ImportAlbum({ onImported }: { onImported: () => void }) {
  const [open, setOpen] = useState(false);
  const [files, setFiles] = useState<File[]>([]);
  const [artist, setArtist] = useState("");
  const [album, setAlbum] = useState("");
  const [year, setYear] = useState("");
  const [busy, setBusy] = useState(false);
  const [drag, setDrag] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);
  const folderRef = useRef<HTMLInputElement>(null);

  const audioCount = files.filter((f) => AUDIO_RE.test(f.name)).length;
  const ready = !!artist.trim() && !!album.trim() && audioCount > 0;

  function reset() {
    setFiles([]);
    setArtist("");
    setAlbum("");
    setYear("");
  }
  function close() {
    setOpen(false);
    reset();
  }
  function addFiles(list: File[]) {
    if (list.length) setFiles((prev) => [...prev, ...list]);
  }

  function prefillFolder(folder: string | null) {
    if (!folder) return;
    const p = parseFolder(folder);
    setAlbum((a) => a || p.album);
    if (p.artist) setArtist((a) => a || p.artist!);
    if (p.year) setYear((y) => y || p.year!);
  }

  async function onDrop(e: React.DragEvent) {
    e.preventDefault();
    setDrag(false);
    // Capture the entries SYNCHRONOUSLY — the DataTransfer is emptied once this handler yields.
    const dt = e.dataTransfer;
    const entries = Array.from(dt.items || [])
      .map((it) => it.webkitGetAsEntry?.())
      .filter((x): x is FileSystemEntry => !!x);
    const plain = Array.from(dt.files || []);
    try {
      if (entries.length) {
        const out: File[] = [];
        for (const en of entries) await walkEntry(en, out);
        const dirs = entries.filter((en) => en.isDirectory);
        addFiles(out);
        prefillFolder(dirs.length === 1 && entries.length === 1 ? dirs[0].name : null);
        if (out.length === 0) toast("That folder had no files Lyra could read");
      } else if (plain.length) {
        addFiles(plain);
      } else {
        toast("Couldn't read the drop — try the “choose a folder” link");
      }
    } catch (err) {
      console.error("import drop failed:", err);
      toast("Couldn't read the dropped folder — try the “choose a folder” link");
    }
  }

  // Reliable fallback: a folder picker (webkitdirectory). Files carry webkitRelativePath, whose
  // first segment is the folder name — use it to prefill.
  function onPickFolder(e: React.ChangeEvent<HTMLInputElement>) {
    const picked = Array.from(e.target.files ?? []);
    addFiles(picked);
    const rel = picked[0]?.webkitRelativePath ?? "";
    prefillFolder(rel.includes("/") ? rel.split("/")[0] : null);
    e.target.value = "";
  }

  async function submit(e: React.FormEvent) {
    e.preventDefault();
    if (!ready) return;
    setBusy(true);
    try {
      const fd = new FormData();
      fd.set("artist", artist.trim());
      fd.set("album", album.trim());
      if (year.trim()) fd.set("year", year.trim());
      for (const f of files) fd.append("files", f);
      const res = await fetch("/api/library/import", { method: "POST", body: fd }).catch(() => null);
      if (res?.ok) {
        const d = (await res.json()) as { tracks: number };
        toast(`Imported ${album.trim()} · ${d.tracks} tracks`);
        onImported();
        close();
      } else {
        const d = res ? ((await res.json().catch(() => ({}))) as { error?: string }) : {};
        toast(d.error || "Import failed");
      }
    } finally {
      setBusy(false);
    }
  }

  return (
    <>
      <button type="button" className="btn accent" onClick={() => setOpen(true)}>
        Add album
      </button>
      {open ? (
        <Modal open onClose={close} title="Add an album">
          <form onSubmit={submit} className="import-form">
            <div
              className={`dropzone${drag ? " over" : ""}`}
              onDragEnter={(e) => {
                e.preventDefault();
                setDrag(true);
              }}
              onDragOver={(e) => {
                e.preventDefault();
                e.dataTransfer.dropEffect = "copy";
                setDrag(true);
              }}
              onDragLeave={() => setDrag(false)}
              onDrop={onDrop}
              onClick={() => inputRef.current?.click()}
            >
              {audioCount > 0
                ? `${audioCount} audio file${audioCount === 1 ? "" : "s"} selected${
                    files.length > audioCount ? " (+ cover)" : ""
                  }`
                : "Drop an album folder (or files) here"}
              <input
                ref={inputRef}
                type="file"
                multiple
                accept="audio/*,.flac,.m4a,.opus,image/*"
                hidden
                onChange={(e) => addFiles(Array.from(e.target.files ?? []))}
              />
              {/* @ts-expect-error webkitdirectory is a non-standard attribute for folder selection */}
              <input ref={folderRef} type="file" webkitdirectory="" hidden onChange={onPickFolder} />
            </div>
            <p className="import-picks">
              <button type="button" className="linkish" onClick={() => folderRef.current?.click()}>
                Choose a folder
              </button>
              {" · "}
              <button type="button" className="linkish" onClick={() => inputRef.current?.click()}>
                Choose files
              </button>
            </p>
            <div className="field-grid">
              <label className="field">
                <span>Artist</span>
                <input aria-label="import artist" value={artist} onChange={(e) => setArtist(e.target.value)} />
              </label>
              <label className="field">
                <span>Album</span>
                <input aria-label="import album" value={album} onChange={(e) => setAlbum(e.target.value)} />
              </label>
              <label className="field">
                <span>Year (optional)</span>
                <input aria-label="import year" value={year} onChange={(e) => setYear(e.target.value)} placeholder="2020" />
              </label>
            </div>
            <p className="help">
              Files are written into the library as-is (add a cover.jpg by including an image). Run a{" "}
              <b>Scan</b> in Settings afterwards to fetch cover art and match MusicBrainz.
            </p>
            <div className="save-row">
              <button type="submit" className="btn accent" disabled={busy || !ready}>
                {busy ? "Importing…" : "Import"}
              </button>
              {files.length > 0 ? (
                <button type="button" className="btn ghost sm" onClick={reset}>
                  Clear files
                </button>
              ) : null}
            </div>
          </form>
        </Modal>
      ) : null}
    </>
  );
}
