"use client";

import { useState, useRef, useEffect } from "react";
import { CATEGORIES, formatCategory } from "@/lib/categories";
import { useImportCSV, useStatementCoverage } from "@/lib/hooks";
import {
  parseCSVRows, detectHasHeaders, getColumnLabels, getDataRows, applyMapping,
  saveBankPreset, loadBankPresets, splitByCoverage, inFileDuplicates,
  type DateFormat, type ColumnMapping, type ParsedTransaction, type BankPreset,
} from "@/lib/csv-parser";

const DATE_FORMATS: DateFormat[] = ["DD/MM/YYYY", "YYYY-MM-DD", "MM/DD/YYYY", "M/D/YYYY"];
const TX_TYPES = ["debit", "credit", "payment", "refund", "fee", "interest", "transfer"];

type Step = "upload" | "map" | "review" | "done";



function ColSelect({
  label, value, onChange, options, required,
}: {
  label: string; value: string; onChange: (v: string) => void;
  options: string[]; required?: boolean;
}) {
  return (
    <div>
      <label className="block text-xs text-zinc-500 mb-1">{label}</label>
      <select
        value={value}
        onChange={(e) => onChange(e.target.value)}
        className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
      >
        {!required && <option value="">— none —</option>}
        {options.map((o) => <option key={o} value={o}>{o}</option>)}
      </select>
    </div>
  );
}

export function CsvImportModal({ onClose }: { onClose: () => void }) {
  const importCSV = useImportCSV();
  const fileRef = useRef<HTMLInputElement>(null);

  const [step, setStep] = useState<Step>("upload");
  const [rawRows, setRawRows] = useState<string[][]>([]);
  const [hasHeaders, setHasHeaders] = useState(false);
  const [columnLabels, setColumnLabels] = useState<string[]>([]);
  const [dataRows, setDataRows] = useState<string[][]>([]);
  const [bankName, setBankName] = useState("");
  const [dateFormat, setDateFormat] = useState<DateFormat>("DD/MM/YYYY");
  const [mapping, setMapping] = useState<ColumnMapping>({
    dateCol: "", descriptionCol: "", amountMode: "single", amountCol: "",
  });
  const [savePreset, setSavePreset] = useState(false);
  const [editedRows, setEditedRows] = useState<ParsedTransaction[]>([]);
  const [error, setError] = useState("");
  const [presets, setPresets] = useState<BankPreset[]>([]);
  const [insertedCount, setInsertedCount] = useState(0);
  const [coveredRows, setCoveredRows] = useState<ParsedTransaction[]>([]);
  const [showCovered, setShowCovered] = useState(false);
  const coverage = useStatementCoverage();

  useEffect(() => { setPresets(loadBankPresets()); }, []);

  function handleFile(file: File) {
    setError("");
    const reader = new FileReader();
    reader.onload = (e) => {
      const text = e.target?.result as string;
      const rows = parseCSVRows(text);
      if (rows.length === 0) { setError("No data found in file"); return; }
      setRawRows(rows);
      const headers = detectHasHeaders(rows, dateFormat);
      setHasHeaders(headers);
      const labels = getColumnLabels(rows, headers);
      setColumnLabels(labels);
      setDataRows(getDataRows(rows, headers));
      // auto-set first columns as defaults
      setMapping((m) => ({
        ...m,
        dateCol: labels[0] ?? "",
        descriptionCol: labels[1] ?? "",
        amountCol: labels[2] ?? "",
      }));
      setStep("map");
    };
    reader.readAsText(file);
  }

  function applyPreset(preset: BankPreset) {
    setBankName(preset.bankName);
    setDateFormat(preset.dateFormat);
    setMapping(preset.mapping);
  }

  function refreshColumns() {
    if (!rawRows.length) return;
    const headers = detectHasHeaders(rawRows, dateFormat);
    setHasHeaders(headers);
    const labels = getColumnLabels(rawRows, headers);
    setColumnLabels(labels);
    setDataRows(getDataRows(rawRows, headers));
  }

  function handleNext() {
    setError("");
    if (!bankName.trim()) { setError("Bank name is required"); return; }
    if (!mapping.dateCol) { setError("Date column is required"); return; }
    if (!mapping.descriptionCol) { setError("Description column is required"); return; }
    if (mapping.amountMode === "single" && !mapping.amountCol) { setError("Amount column is required"); return; }
    if (mapping.amountMode === "debit_credit" && !mapping.debitCol && !mapping.creditCol) {
      setError("At least one of debit/credit columns is required"); return;
    }
    const parsed = applyMapping(dataRows, columnLabels, mapping, dateFormat);
    if (parsed.length === 0) { setError("No valid transactions could be parsed — check your column mapping and date format"); return; }
    if (savePreset) {
      saveBankPreset({ bankName: bankName.trim(), mapping, dateFormat });
    }
    const { keep, covered } = splitByCoverage(parsed, coverage.data ?? []);
    setEditedRows(keep);
    setCoveredRows(covered);
    setStep("review");
  }

  async function handleImport() {
    setError("");
    const valid = editedRows.filter((r) => r.date && r.amount > 0 && r.description);
    if (!valid.length) { setError("No valid rows to import"); return; }
    try {
      const result = await importCSV.mutateAsync({
        bank_name: bankName,
        // Provenance, so a second import of the same file inserts nothing —
        // paired with each row's source_ref where the file carries an id.
        source: bankName.trim().toLowerCase().replace(/\s+/g, "-"),
        transactions: valid,
      });
      setInsertedCount(result.inserted);
      setStep("done");
    } catch (e) {
      setError(e instanceof Error ? e.message : "Import failed");
    }
  }

  function updateRow(i: number, patch: Partial<ParsedTransaction>) {
    setEditedRows((rows) => rows.map((r, idx) => idx === i ? { ...r, ...patch } : r));
  }

  function deleteRow(i: number) {
    setEditedRows((rows) => rows.filter((_, idx) => idx !== i));
  }

  const modalWidth = step === "review" ? "max-w-5xl" : step === "map" ? "max-w-xl" : "max-w-md";

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4" onClick={onClose}>
      <div
        className={`bg-zinc-900 border border-zinc-700 rounded-xl shadow-2xl w-full ${modalWidth} flex flex-col max-h-[90vh]`}
        onClick={(e) => e.stopPropagation()}
      >
        {/* Header */}
        <div className="flex items-center justify-between px-6 py-4 border-b border-zinc-800 flex-shrink-0">
          <div>
            <h3 className="font-semibold text-sm text-zinc-200">Import CSV</h3>
            <div className="flex gap-2 mt-1">
              {(["upload", "map", "review", "done"] as Step[]).map((s, i) => (
                <span
                  key={s}
                  className={`text-xs ${step === s ? "text-indigo-400 font-medium" : "text-zinc-600"}`}
                >
                  {i + 1}. {s.charAt(0).toUpperCase() + s.slice(1)}
                </span>
              ))}
            </div>
          </div>
          <button onClick={onClose} className="text-zinc-500 hover:text-zinc-300 text-xl leading-none">×</button>
        </div>

        {/* Body */}
        <div className="overflow-y-auto flex-1 px-6 py-5">

          {/* Step 1: Upload */}
          {step === "upload" && (
            <div className="space-y-4">
              {presets.length > 0 && (
                <div>
                  <label className="block text-xs text-zinc-500 mb-1">Load saved preset</label>
                  <select
                    className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                    defaultValue=""
                    onChange={(e) => {
                      const p = presets.find((x) => x.bankName === e.target.value);
                      if (p) applyPreset(p);
                    }}
                  >
                    <option value="">— select preset —</option>
                    {presets.map((p) => <option key={p.bankName} value={p.bankName}>{p.bankName}</option>)}
                  </select>
                </div>
              )}
              <div>
                <input
                  ref={fileRef}
                  type="file"
                  accept=".csv,text/csv"
                  className="hidden"
                  onChange={(e) => { const f = e.target.files?.[0]; if (f) handleFile(f); }}
                />
                <button
                  onClick={() => fileRef.current?.click()}
                  className="w-full border-2 border-dashed border-zinc-700 hover:border-indigo-500 rounded-xl py-12 text-center transition-colors"
                  onDragOver={(e) => e.preventDefault()}
                  onDrop={(e) => { e.preventDefault(); const f = e.dataTransfer.files?.[0]; if (f) handleFile(f); }}
                >
                  <p className="text-zinc-400 text-sm">Drop a CSV file here, or click to browse</p>
                  <p className="text-zinc-600 text-xs mt-1">Westpac, ANZ, CBA, NAB and others</p>
                </button>
              </div>
              {error && <p className="text-red-400 text-xs">{error}</p>}
            </div>
          )}

          {/* Step 2: Map */}
          {step === "map" && (
            <div className="space-y-4">
              {/* Raw preview */}
              <div>
                <p className="text-xs text-zinc-500 mb-2">First 3 rows from file:</p>
                <div className="overflow-x-auto rounded border border-zinc-800">
                  <table className="text-xs text-zinc-400 w-full">
                    <thead>
                      <tr className="border-b border-zinc-800">
                        {columnLabels.map((h) => (
                          <th key={h} className="px-2 py-1.5 text-left font-medium text-zinc-300 whitespace-nowrap">{h}</th>
                        ))}
                      </tr>
                    </thead>
                    <tbody>
                      {dataRows.slice(0, 3).map((row, i) => (
                        <tr key={i} className="border-b border-zinc-800/50">
                          {columnLabels.map((_, ci) => (
                            <td key={ci} className="px-2 py-1 truncate max-w-[160px]">{row[ci] ?? ""}</td>
                          ))}
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
              </div>

              <div className="grid grid-cols-2 gap-3">
                <div>
                  <label className="block text-xs text-zinc-500 mb-1">Bank Name</label>
                  <input
                    value={bankName}
                    onChange={(e) => setBankName(e.target.value)}
                    placeholder="e.g. Westpac"
                    className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                  />
                </div>
                <div>
                  <label className="block text-xs text-zinc-500 mb-1">Date Format</label>
                  <select
                    value={dateFormat}
                    onChange={(e) => { setDateFormat(e.target.value as DateFormat); refreshColumns(); }}
                    className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1.5 text-sm"
                  >
                    {DATE_FORMATS.map((f) => <option key={f} value={f}>{f}</option>)}
                  </select>
                </div>
              </div>

              <div className="grid grid-cols-2 gap-3">
                <ColSelect label="Date Column *" value={mapping.dateCol} onChange={(v) => setMapping((m) => ({ ...m, dateCol: v }))} options={columnLabels} required />
                <ColSelect label="Description Column *" value={mapping.descriptionCol} onChange={(v) => setMapping((m) => ({ ...m, descriptionCol: v }))} options={columnLabels} required />
              </div>

              <div>
                <label className="block text-xs text-zinc-500 mb-2">Amount</label>
                <div className="flex gap-4 mb-2">
                  <label className="flex items-center gap-2 text-sm cursor-pointer">
                    <input type="radio" name="amtmode" value="single" checked={mapping.amountMode === "single"} onChange={() => setMapping((m) => ({ ...m, amountMode: "single" }))} className="accent-indigo-500" />
                    Single signed column
                  </label>
                  <label className="flex items-center gap-2 text-sm cursor-pointer">
                    <input type="radio" name="amtmode" value="debit_credit" checked={mapping.amountMode === "debit_credit"} onChange={() => setMapping((m) => ({ ...m, amountMode: "debit_credit" }))} className="accent-indigo-500" />
                    Separate debit / credit columns
                  </label>
                </div>
                {mapping.amountMode === "single" ? (
                  <ColSelect label="Amount Column *" value={mapping.amountCol ?? ""} onChange={(v) => setMapping((m) => ({ ...m, amountCol: v }))} options={columnLabels} required />
                ) : (
                  <div className="grid grid-cols-2 gap-3">
                    <ColSelect label="Debit Column" value={mapping.debitCol ?? ""} onChange={(v) => setMapping((m) => ({ ...m, debitCol: v }))} options={columnLabels} />
                    <ColSelect label="Credit Column" value={mapping.creditCol ?? ""} onChange={(v) => setMapping((m) => ({ ...m, creditCol: v }))} options={columnLabels} />
                  </div>
                )}
              </div>

              <div className="grid grid-cols-2 gap-3">
                <ColSelect label="Merchant Column (optional)" value={mapping.merchantCol ?? ""} onChange={(v) => setMapping((m) => ({ ...m, merchantCol: v || undefined }))} options={columnLabels} />
                <ColSelect label="Category Column (optional)" value={mapping.categoryCol ?? ""} onChange={(v) => setMapping((m) => ({ ...m, categoryCol: v || undefined }))} options={columnLabels} />
                <ColSelect label="Account Column (optional)" value={mapping.accountCol ?? ""} onChange={(v) => setMapping((m) => ({ ...m, accountCol: v || undefined }))} options={columnLabels} />
                <ColSelect label="Currency Column (optional)" value={mapping.currencyCol ?? ""} onChange={(v) => setMapping((m) => ({ ...m, currencyCol: v || undefined }))} options={columnLabels} />
                <ColSelect label="Row ID Column (optional)" value={mapping.sourceRefCol ?? ""} onChange={(v) => setMapping((m) => ({ ...m, sourceRefCol: v || undefined }))} options={columnLabels} />
              </div>
              <p className="text-[11px] text-zinc-500 leading-relaxed">
                Map <b>Account</b> when the file covers more than one account: rows already
                covered by that account&apos;s statements are then left out. Map <b>Row ID</b> to
                make re-importing the same file do nothing. Map <b>Currency</b> for a
                multi-currency file, or foreign rows are stored as if they were AUD.
              </p>

              <label className="flex items-center gap-2 text-sm cursor-pointer text-zinc-400">
                <input type="checkbox" checked={savePreset} onChange={(e) => setSavePreset(e.target.checked)} className="accent-indigo-500" />
                Save as preset for {bankName || "this bank"}
              </label>

              {error && <p className="text-red-400 text-xs">{error}</p>}
            </div>
          )}

          {/* Step 3: Review */}
          {step === "review" && (
            <div>
              <p className="text-xs text-zinc-500 mb-2">
                {editedRows.length} transactions to import. Edit or remove rows before importing.
              </p>

              {/*
                Reported, never silent. A row that disappears can mean several
                things and one of them is worth following up, so the count is
                always shown and the rows themselves are one click away.
              */}
              {coveredRows.length > 0 && (
                <div className="mb-3 rounded border border-zinc-800 bg-zinc-800/30 px-3 py-2">
                  <button
                    type="button"
                    onClick={() => setShowCovered((v) => !v)}
                    className="text-xs text-zinc-300 hover:text-white text-left w-full"
                  >
                    {coveredRows.length} row{coveredRows.length === 1 ? "" : "s"} left out — already covered by
                    a statement for that account.{" "}
                    <span className="text-indigo-400">{showCovered ? "hide" : "show"}</span>
                  </button>
                  {showCovered && (
                    <>
                      <ul className="mt-2 max-h-40 overflow-y-auto text-[11px] text-zinc-500 font-mono space-y-0.5">
                        {coveredRows.slice(0, 200).map((r, i) => (
                          <li key={i}>{r.date}  {r.amount.toFixed(2).padStart(10)}  {r.description.slice(0, 44)}</li>
                        ))}
                      </ul>
                      {coveredRows.length > 200 && (
                        <p className="mt-1 text-[11px] text-zinc-600">…and {coveredRows.length - 200} more.</p>
                      )}
                      <p className="mt-2 text-[11px] text-zinc-500">
                        Statements cover:{" "}
                        {(coverage.data ?? []).map((c) => `${c.last4} to ${c.coveredTo}`).join(" · ")}
                      </p>
                    </>
                  )}
                </div>
              )}

              {inFileDuplicates(editedRows) > 0 && (
                <p className="mb-3 rounded border border-amber-900/50 bg-amber-950/30 px-3 py-2 text-xs text-amber-300">
                  {inFileDuplicates(editedRows)} row{inFileDuplicates(editedRows) === 1 ? " duplicates another row" : "s duplicate other rows"} in
                  this same file (same date, amount and description). A re-consent can make an
                  aggregator re-export history under new ids — check before importing.
                </p>
              )}
              <div className="overflow-x-auto rounded border border-zinc-800">
                <table className="w-full text-xs">
                  <thead className="border-b border-zinc-800">
                    <tr>
                      {["Date", "Description", "Amount", "Type", "Merchant", "Category", ""].map((h) => (
                        <th key={h} className="px-2 py-2 text-left text-zinc-400 font-medium whitespace-nowrap">{h}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {editedRows.map((row, i) => (
                      <tr key={i} className="border-b border-zinc-800/50 hover:bg-zinc-800/30">
                        <td className="px-2 py-1">
                          <input type="date" value={row.date} onChange={(e) => updateRow(i, { date: e.target.value })}
                            className="bg-transparent border-b border-zinc-700 text-zinc-300 text-xs w-28 focus:outline-none focus:border-indigo-500" />
                        </td>
                        <td className="px-2 py-1">
                          <input value={row.description} onChange={(e) => updateRow(i, { description: e.target.value })}
                            className="bg-transparent border-b border-zinc-700 text-zinc-300 text-xs w-48 focus:outline-none focus:border-indigo-500" />
                        </td>
                        <td className="px-2 py-1">
                          <input type="number" step="0.01" value={row.amount} onChange={(e) => updateRow(i, { amount: parseFloat(e.target.value) || 0 })}
                            className="bg-transparent border-b border-zinc-700 text-zinc-300 text-xs w-20 focus:outline-none focus:border-indigo-500" />
                        </td>
                        <td className="px-2 py-1">
                          <select value={row.transaction_type} onChange={(e) => updateRow(i, { transaction_type: e.target.value })}
                            className="bg-zinc-800 border border-zinc-700 rounded px-1 py-0.5 text-xs">
                            {TX_TYPES.map((t) => <option key={t} value={t}>{t}</option>)}
                          </select>
                        </td>
                        <td className="px-2 py-1">
                          <input value={row.merchant_name ?? ""} onChange={(e) => updateRow(i, { merchant_name: e.target.value || undefined })}
                            className="bg-transparent border-b border-zinc-700 text-zinc-300 text-xs w-28 focus:outline-none focus:border-indigo-500" />
                        </td>
                        <td className="px-2 py-1">
                          <select value={row.category ?? ""} onChange={(e) => updateRow(i, { category: e.target.value || undefined })}
                            className="bg-zinc-800 border border-zinc-700 rounded px-1 py-0.5 text-xs">
                            <option value="">—</option>
                            {CATEGORIES.map((c) => <option key={c} value={c}>{formatCategory(c)}</option>)}
                          </select>
                        </td>
                        <td className="px-2 py-1">
                          <button onClick={() => deleteRow(i)} className="text-zinc-600 hover:text-red-400 text-base leading-none">×</button>
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
              {error && <p className="text-red-400 text-xs mt-2">{error}</p>}
            </div>
          )}

          {/* Step 4: Done */}
          {step === "done" && (
            <div className="text-center py-6 space-y-3">
              <div className="text-4xl">✓</div>
              <p className="text-zinc-200 font-medium">Imported {insertedCount} transactions</p>
              <p className="text-zinc-500 text-sm">Tagged with <span className="text-indigo-400">csv-import</span></p>
              <div className="flex gap-2 justify-center pt-2">
                <a href="/transactions" className="px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded-lg text-sm">
                  View Transactions
                </a>
                <a href="/reconcile" className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg text-sm">
                  Reconcile
                </a>
              </div>
            </div>
          )}
        </div>

        {/* Footer */}
        {step !== "done" && (
          <div className="flex gap-2 px-6 py-4 border-t border-zinc-800 flex-shrink-0">
            {step === "map" && (
              <button onClick={() => setStep("upload")} className="px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded-lg text-sm">
                Back
              </button>
            )}
            {step === "review" && (
              <button onClick={() => setStep("map")} className="px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded-lg text-sm">
                Back
              </button>
            )}
            <div className="flex-1" />
            <button onClick={onClose} className="px-4 py-2 bg-zinc-800 hover:bg-zinc-700 text-zinc-300 rounded-lg text-sm">
              Cancel
            </button>
            {step === "map" && (
              <button onClick={handleNext} className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg text-sm font-medium">
                Next →
              </button>
            )}
            {step === "review" && (
              <button
                onClick={handleImport}
                disabled={importCSV.isPending || editedRows.length === 0}
                className="px-4 py-2 bg-indigo-600 hover:bg-indigo-500 text-white rounded-lg text-sm font-medium disabled:opacity-50"
              >
                {importCSV.isPending ? "Importing..." : `Import ${editedRows.length} transactions`}
              </button>
            )}
          </div>
        )}
      </div>
    </div>
  );
}
