"use client";

import { useState } from "react";
import { useMutation } from "@tanstack/react-query";
import * as XLSX from "xlsx";
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogDescription,
  DialogFooter,
} from "@/app/shadcn_components/ui/dialog";
import { Button } from "@/app/shadcn_components/ui/button";
import { parseTagIdentifierRows, IdentifierKey } from "@/lib/tags/bulkAssign";
import { Tag, useInvalidateTagSurfaces, usePortfolioTags } from "./useTags";
import { TagChip } from "./TagChip";

interface Parsed {
  key: IdentifierKey;
  identifiers: string[];
}

interface AssignSummary {
  matchedPropertyCount: number;
  taggedCount: number;
  alreadyTaggedCount: number;
  unmatchedCount: number;
  unmatchedIdentifiers: string[];
}

const KEY_LABEL: Record<IdentifierKey, string> = {
  landlord_property_id: "Property Ref (landlord id)",
  uprn: "UPRN",
};

/** Download a one-column CSV template the user can fill in and re-upload. */
function downloadTemplate(key: IdentifierKey) {
  const example =
    key === "uprn"
      ? ["100012345678", "100012345679"]
      : ["PROP-0001", "PROP-0002"];
  const csv = [key, ...example].join("\n") + "\n";
  const url = URL.createObjectURL(new Blob([csv], { type: "text/csv;charset=utf-8;" }));
  const a = document.createElement("a");
  a.href = url;
  a.download = `tag-assignment-template-${key}.csv`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

/**
 * Bulk tag assignment upload (ADR-0013): reads a small CSV/Excel of identifiers
 * client-side, parses it with the shared pure parser, lets the user pick a
 * target Tag, then POSTs {mode:"identifiers"} and renders the match summary.
 * No FastAPI pipeline — the file matches existing identifiers to Properties.
 */
export function BulkTagUploadModal({
  portfolioId,
  open,
  onOpenChange,
}: {
  portfolioId: string;
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const { data: tags = [] } = usePortfolioTags(portfolioId);
  const invalidate = useInvalidateTagSurfaces(portfolioId);

  const [fileName, setFileName] = useState<string | null>(null);
  const [parsed, setParsed] = useState<Parsed | null>(null);
  const [parseError, setParseError] = useState<string | null>(null);
  const [targetTag, setTargetTag] = useState<Tag | null>(null);
  const [summary, setSummary] = useState<AssignSummary | null>(null);

  function reset() {
    setFileName(null);
    setParsed(null);
    setParseError(null);
    setTargetTag(null);
    setSummary(null);
    assign.reset();
  }

  async function onFile(file: File) {
    setParseError(null);
    setParsed(null);
    setSummary(null);
    setFileName(file.name);
    try {
      const buf = await file.arrayBuffer();
      const wb = XLSX.read(buf, { type: "array" });
      const sheet = wb.Sheets[wb.SheetNames[0]];
      const rows = XLSX.utils.sheet_to_json(sheet, { header: 1 }) as (
        | string
        | number
        | null
      )[][];
      const result = parseTagIdentifierRows(rows);
      if (!result.ok) {
        setParseError(result.error);
        return;
      }
      setParsed({ key: result.key, identifiers: result.identifiers });
    } catch {
      setParseError("Couldn't read that file — is it a valid CSV or Excel?");
    }
  }

  const assign = useMutation<AssignSummary, Error, void>({
    mutationFn: async () => {
      if (!parsed || !targetTag) throw new Error("Pick a file and a tag first");
      const res = await fetch(
        `/api/portfolio/${portfolioId}/tags/${targetTag.id}/assignments`,
        {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            mode: "identifiers",
            key: parsed.key,
            identifiers: parsed.identifiers,
          }),
        },
      );
      const body = await res.json();
      if (!res.ok) throw new Error(body.error ?? "Couldn't assign the tag");
      return body;
    },
    onSuccess: (data) => {
      setSummary(data);
      invalidate();
    },
  });

  return (
    <Dialog
      open={open}
      onOpenChange={(o) => {
        if (!o) reset();
        onOpenChange(o);
      }}
    >
      <DialogContent className="max-w-lg">
        <DialogHeader>
          <DialogTitle>Assign a tag from a file</DialogTitle>
          <DialogDescription>
            Upload a CSV or Excel file with a{" "}
            <span className="font-semibold">landlord_property_id</span> or{" "}
            <span className="font-semibold">uprn</span> column. We&apos;ll match
            those to properties in this portfolio and apply the tag you pick.
          </DialogDescription>
        </DialogHeader>

        {summary ? (
          <div className="space-y-3">
            <div className="rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-sm text-emerald-900">
              <p className="font-semibold">
                Tagged {summary.taggedCount.toLocaleString()}{" "}
                propert{summary.taggedCount === 1 ? "y" : "ies"}
                {targetTag ? (
                  <>
                    {" "}
                    with{" "}
                    <TagChip name={targetTag.name} colour={targetTag.colour} size="xs" />
                  </>
                ) : null}
                .
              </p>
              <ul className="mt-1.5 space-y-0.5 text-[13px] text-emerald-800">
                <li>{summary.matchedPropertyCount.toLocaleString()} matched the file</li>
                {summary.alreadyTaggedCount > 0 && (
                  <li>
                    {summary.alreadyTaggedCount.toLocaleString()} already had the tag
                  </li>
                )}
                {summary.unmatchedCount > 0 && (
                  <li className="text-amber-700">
                    {summary.unmatchedCount.toLocaleString()} identifier
                    {summary.unmatchedCount === 1 ? "" : "s"} matched no property
                  </li>
                )}
              </ul>
            </div>
            {summary.unmatchedIdentifiers.length > 0 && (
              <details className="text-xs text-slate-500">
                <summary className="cursor-pointer font-semibold">
                  Show unmatched identifiers
                </summary>
                <p className="mt-1 max-h-32 overflow-y-auto break-words rounded bg-slate-50 p-2 font-mono text-[11px]">
                  {summary.unmatchedIdentifiers.join(", ")}
                  {summary.unmatchedCount > summary.unmatchedIdentifiers.length &&
                    " …"}
                </p>
              </details>
            )}
          </div>
        ) : (
          <div className="space-y-4">
            <label className="flex cursor-pointer flex-col items-center gap-1 rounded-xl border-2 border-dashed border-slate-200 bg-slate-50 px-4 py-6 text-center transition hover:border-slate-300">
              <span className="text-sm font-semibold text-slate-600">
                {fileName ?? "Choose a CSV or Excel file"}
              </span>
              <span className="text-xs text-slate-400">
                First row must be a header
              </span>
              <input
                type="file"
                accept=".csv,.xlsx,.xls"
                className="hidden"
                onChange={(e) => {
                  const f = e.target.files?.[0];
                  if (f) onFile(f);
                }}
              />
            </label>

            <p className="text-xs text-slate-400">
              Need a starting point? Download a template for{" "}
              <button
                type="button"
                onClick={() => downloadTemplate("landlord_property_id")}
                className="font-semibold text-brandmidblue hover:underline"
              >
                Property Ref
              </button>{" "}
              or{" "}
              <button
                type="button"
                onClick={() => downloadTemplate("uprn")}
                className="font-semibold text-brandmidblue hover:underline"
              >
                UPRN
              </button>
              .
            </p>

            {parseError && (
              <p className="text-sm text-red-600">{parseError}</p>
            )}

            {parsed && (
              <div className="space-y-3">
                <p className="text-sm text-slate-600">
                  Found{" "}
                  <span className="font-semibold text-slate-800">
                    {parsed.identifiers.length.toLocaleString()}
                  </span>{" "}
                  identifiers, matching on{" "}
                  <span className="font-semibold text-slate-800">
                    {KEY_LABEL[parsed.key]}
                  </span>
                  .
                </p>
                <div>
                  <p className="mb-1.5 text-xs font-semibold text-slate-500">
                    Apply which tag?
                  </p>
                  {tags.length === 0 ? (
                    <p className="text-xs text-slate-400">
                      No tags yet —{" "}
                      <a
                        href={`/portfolio/${portfolioId}/settings/tags`}
                        className="font-semibold text-brandmidblue hover:underline"
                      >
                        create one first
                      </a>
                      .
                    </p>
                  ) : (
                    <div className="flex flex-wrap gap-1.5">
                      {tags.map((t) => (
                        <button
                          key={t.id}
                          type="button"
                          onClick={() => setTargetTag(t)}
                          className={`rounded-full transition ${
                            targetTag?.id === t.id
                              ? "ring-2 ring-brandmidblue ring-offset-1"
                              : "opacity-70 hover:opacity-100"
                          }`}
                        >
                          <TagChip name={t.name} colour={t.colour} />
                        </button>
                      ))}
                    </div>
                  )}
                </div>
              </div>
            )}

            {assign.isError && (
              <p className="text-sm text-red-600">{assign.error.message}</p>
            )}
          </div>
        )}

        <DialogFooter>
          {summary ? (
            <>
              <Button variant="ghost" onClick={reset}>
                Assign another
              </Button>
              <Button onClick={() => onOpenChange(false)}>Done</Button>
            </>
          ) : (
            <>
              <Button variant="ghost" onClick={() => onOpenChange(false)}>
                Cancel
              </Button>
              <Button
                disabled={!parsed || !targetTag || assign.isLoading}
                onClick={() => assign.mutate()}
              >
                {assign.isLoading ? "Assigning…" : "Assign tag"}
              </Button>
            </>
          )}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
