"use client";

import { useState } from "react";

import { api, type ImportPreview, type ImportResult, type ImportRowReport } from "@/lib/api/browser";

import Modal from "./Modal";
import { Button, ErrorNote } from "./ui";

/**
 * CSV import with a mandatory preview step (task 3.8, spec guest-management).
 *
 * Two calls, not one: `preview` parses and validates without writing, `commit` writes. The
 * host is uploading a spreadsheet a relative maintained, and the realistic failure is not
 * a malformed file — it is a *plausible* file with the phone column shifted by one. A
 * preview they must look at first is the only thing that catches that.
 *
 * This is a modal over the guest list, not a screen of its own (design D2). The destination
 * event arrives as a prop from the list's own filter and there is no control for it here
 * (design D1) — a second picker could only disagree with the list the admin is standing on,
 * and the disagreement would be invisible until the rows landed on the wrong event. The
 * sheet's old `events` column is ignored for the same reason: neither a file nor a form can
 * contradict the destination the admin can see.
 */
export default function ImportView({
  eventId,
  eventName,
  onClose,
  onImported,
}: {
  /** The list being imported into. The caller only mounts this once an event is chosen. */
  eventId: string;
  eventName?: string;
  onClose: () => void;
  /** Fired after a successful commit so the table underneath reflects the new rows. */
  onImported: () => void;
}) {
  const [file, setFile] = useState<File | null>(null);
  const [preview, setPreview] = useState<ImportPreview | null>(null);
  const [result, setResult] = useState<ImportResult | null>(null);
  const [error, setError] = useState<unknown>(null);
  const [busy, setBusy] = useState(false);

  function reset() {
    setPreview(null);
    setResult(null);
    setError(null);
  }

  async function runPreview(chosen: File) {
    setBusy(true);
    reset();
    try {
      setPreview(await api.previewImport(eventId, chosen));
    } catch (err) {
      setError(err);
    } finally {
      setBusy(false);
    }
  }

  async function commit() {
    if (!file) return;
    setBusy(true);
    setError(null);
    try {
      setResult(await api.commitImport(eventId, file));
      setPreview(null);
      // The list behind this modal is now stale by exactly the rows just written. Refresh it
      // while the report is still on screen, so closing reveals a correct table rather than
      // one the admin has to reload by hand (design D3).
      onImported();
    } catch (err) {
      setError(err);
    } finally {
      setBusy(false);
    }
  }

  return (
    <Modal
      title={eventName ? `Import guests into ${eventName}` : "Import guests"}
      onClose={onClose}
      // The validation report is a three-column table plus a row of stats. At the default
      // width it would scroll sideways, which defeats a report whose entire purpose is being
      // read before anything is written.
      size="wide"
      // Pinned below the scroll container, so the decision is reachable without scrolling
      // past a 200-row error list to find it.
      footer={
        preview ? (
          <div className="flex justify-end gap-2">
            <Button variant="secondary" onClick={reset} disabled={busy}>
              Discard
            </Button>
            <Button onClick={() => void commit()} disabled={busy || preview.valid_rows === 0}>
              {busy ? "Importing…" : `Import ${preview.valid_rows} guest(s)`}
            </Button>
          </div>
        ) : (
          <div className="flex justify-end">
            <Button variant="secondary" onClick={onClose} disabled={busy}>
              {result ? "Done" : "Cancel"}
            </Button>
          </div>
        )
      }
    >
      <div className="space-y-4">
        <div>
          <label htmlFor="csv" className="mb-1 block text-xs font-medium text-stone-600">
            CSV file
          </label>
          <input
            id="csv"
            type="file"
            accept=".csv,text/csv"
            onChange={(e) => {
              const chosen = e.target.files?.[0] ?? null;
              setFile(chosen);
              if (chosen) void runPreview(chosen);
            }}
            className="block w-full text-sm file:mr-3 file:min-h-11 file:rounded-md file:border file:border-stone-300 file:bg-white file:px-4 file:text-sm file:font-medium hover:file:bg-stone-50"
          />
          <p className="mt-2 text-xs text-stone-500">
            Columns: <code>full_name</code>, <code>phone</code>, <code>email</code>,{" "}
            <code>side</code>, <code>group_tag</code>, <code>max_guests</code>. Only{" "}
            <code>full_name</code> is required. Every row joins{" "}
            <span className="font-medium text-stone-700">{eventName ?? "this event"}</span> — an{" "}
            <code>events</code> column in the sheet is ignored.
          </p>
        </div>

        <ErrorNote error={error} />

        {preview && (
          <section>
            <h3 className="mb-3 text-sm font-semibold text-stone-900">Validation report</h3>
            <dl className="grid grid-cols-2 gap-4 sm:grid-cols-4">
              <Stat label="Rows" value={preview.total_rows} />
              <Stat label="Valid" value={preview.valid_rows} tone="good" />
              <Stat label="Invalid" value={preview.invalid_rows} tone={preview.invalid_rows ? "bad" : undefined} />
              <Stat label="Will merge" value={preview.duplicates} />
            </dl>

            {preview.duplicates > 0 && (
              <p className="mt-3 rounded-lg bg-sky-50 px-3 py-2 text-sm text-sky-900">
                {preview.duplicates} row{preview.duplicates === 1 ? "" : "s"} match a guest
                already on this event&apos;s list by phone or email and will be merged rather
                than duplicated. Matches on other events&apos; lists do not count — those are
                separate guest records.
              </p>
            )}

            {preview.errors.length > 0 && (
              <div className="mt-4">
                <h4 className="mb-2 text-xs font-semibold uppercase tracking-wide text-stone-500">
                  Rows with problems
                </h4>
                {/* Per-row, not "the file is invalid": a single bad phone number must not
                    cost the host all 800 rows. */}
                <RowTable rows={preview.errors} showErrors />
                <p className="mt-2 text-xs text-stone-500">
                  These rows are skipped. Everything else still imports.
                </p>
              </div>
            )}

            {preview.preview.length > 0 && (
              <div className="mt-4">
                <h4 className="mb-2 text-xs font-semibold uppercase tracking-wide text-stone-500">
                  First rows
                </h4>
                <RowTable rows={preview.preview} />
              </div>
            )}
          </section>
        )}

        {result && (
          <section>
            <h3 className="mb-3 text-sm font-semibold text-stone-900">Import complete</h3>
            <dl className="grid grid-cols-2 gap-4 sm:grid-cols-4">
              <Stat label="Imported" value={result.imported} tone="good" />
              <Stat label="Merged" value={result.merged} />
              <Stat label="Skipped" value={result.skipped} tone={result.skipped ? "bad" : undefined} />
            </dl>
            {result.errors.length > 0 && (
              <div className="mt-4">
                {/* Kept on screen after the commit rather than replaced by a success line:
                    these are the rows the host still has to fix by hand, and nothing else
                    records which ones they were. */}
                <h4 className="mb-2 text-xs font-semibold uppercase tracking-wide text-stone-500">
                  Skipped rows
                </h4>
                <RowTable rows={result.errors} showErrors />
              </div>
            )}
          </section>
        )}
      </div>
    </Modal>
  );
}

function RowTable({ rows, showErrors }: { rows: ImportRowReport[]; showErrors?: boolean }) {
  return (
    <div className="overflow-x-auto">
      <table className="w-full min-w-[28rem] text-left text-sm">
        <thead>
          <tr className="border-b border-stone-200 text-xs uppercase tracking-wide text-stone-500">
            <th scope="col" className="py-2 pr-3 font-medium">
              Line
            </th>
            <th scope="col" className="py-2 pr-3 font-medium">
              Name
            </th>
            <th scope="col" className="py-2 pr-3 font-medium">
              {showErrors ? "Problem" : "Action"}
            </th>
          </tr>
        </thead>
        <tbody>
          {rows.map((row) => (
            <tr key={row.line_number} className="border-b border-stone-100 last:border-0">
              <td className="py-2 pr-3 font-mono text-xs text-stone-500">{row.line_number}</td>
              <td className="py-2 pr-3">{row.full_name || <em className="text-stone-400">blank</em>}</td>
              <td className="py-2 pr-3">
                {showErrors ? (
                  <span className="text-red-700">{row.errors.join("; ")}</span>
                ) : row.would_merge ? (
                  <span className="text-sky-700">merge with existing guest</span>
                ) : (
                  <span className="text-stone-500">create</span>
                )}
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

function Stat({
  label,
  value,
  tone,
}: {
  label: string;
  value: number;
  tone?: "good" | "bad";
}) {
  const color =
    tone === "good" ? "text-emerald-700" : tone === "bad" ? "text-red-700" : "text-stone-900";
  return (
    <div>
      <dd className={`text-2xl font-semibold ${color}`}>{value}</dd>
      <dt className="text-xs text-stone-500">{label}</dt>
    </div>
  );
}
