import { useState, useRef } from "react";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Upload, FileSpreadsheet, AlertCircle, CheckCircle2, ArrowRight } from "lucide-react";
import * as XLSX from "xlsx";

interface ExpectedColumn {
  key: string;
  label: string;
  required?: boolean;
  aliases?: string[];
}

interface ImportSpreadsheetDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  title: string;
  description: string;
  expectedColumns: ExpectedColumn[];
  onImport: (rows: Record<string, string>[]) => Promise<void>;
  templateFileName?: string;
}

const normalizeHeader = (value: unknown) =>
  String(value ?? "")
    .trim()
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "_")
    .replace(/^_+|_+$/g, "");

export default function ImportSpreadsheetDialog({
  open, onOpenChange, title, description, expectedColumns, onImport, templateFileName,
}: ImportSpreadsheetDialogProps) {
  const [rawRows, setRawRows] = useState<unknown[][]>([]);
  const [rawHeaders, setRawHeaders] = useState<string[]>([]);
  const [columnMapping, setColumnMapping] = useState<Record<string, string>>({});
  const [step, setStep] = useState<"upload" | "map" | "preview">("upload");
  const [parsedRows, setParsedRows] = useState<Record<string, string>[]>([]);
  const [importing, setImporting] = useState(false);
  const [error, setError] = useState("");
  const [fileName, setFileName] = useState("");
  const fileInputRef = useRef<HTMLInputElement>(null);

  const reset = () => {
    setRawRows([]);
    setRawHeaders([]);
    setColumnMapping({});
    setStep("upload");
    setParsedRows([]);
    setError("");
    setFileName("");
    if (fileInputRef.current) fileInputRef.current.value = "";
  };

  const handleFile = async (e: React.ChangeEvent<HTMLInputElement>) => {
    const file = e.target.files?.[0];
    if (!file) return;

    setError("");
    setFileName(file.name);

    try {
      const data = await file.arrayBuffer();
      const workbook = XLSX.read(data, { type: "array", cellDates: true });
      const sheet = workbook.Sheets[workbook.SheetNames[0]];
      const json: unknown[][] = XLSX.utils.sheet_to_json(sheet, { header: 1, defval: "", rawNumbers: false });

      if (json.length < 2) {
        setError("File must have a header row and at least one data row.");
        return;
      }

      const headers = json[0].map((h) => String(h ?? "").trim()).filter(Boolean);
      setRawHeaders(headers);
      setRawRows(json.slice(1).filter((row) => (row as unknown[]).some((cell: unknown) => String(cell ?? "").trim())));

      // Auto-suggest mapping with aliases and partial matching
      const autoMap: Record<string, string> = {};
      for (const col of expectedColumns) {
        const candidates = [col.key, col.label, ...(col.aliases || [])];
        const normCandidates = candidates.map(normalizeHeader);
        let matched = false;
        for (const h of headers) {
          const normH = normalizeHeader(h);
          if (normCandidates.some(nc => nc === normH)) {
            autoMap[col.key] = h;
            matched = true;
            break;
          }
        }
        // Partial match fallback: check if header contains key or key contains header
        if (!matched) {
          for (const h of headers) {
            const normH = normalizeHeader(h);
            if (normCandidates.some(nc => (normH.length > 2 && nc.includes(normH)) || (nc.length > 2 && normH.includes(nc)))) {
              autoMap[col.key] = h;
              break;
            }
          }
        }
      }
      setColumnMapping(autoMap);
      setStep("map");
    } catch {
      setError("Could not read file. Please use a valid CSV or Excel (.xlsx/.xls) file.");
    }
  };

  const proceedToPreview = () => {
    const missingRequired = expectedColumns
      .filter((c) => c.required)
      .filter((c) => !columnMapping[c.key]);

    if (missingRequired.length > 0) {
      setError(`Please map required fields: ${missingRequired.map((c) => c.label).join(", ")}`);
      return;
    }
    setError("");

    const mappedHeaders = Object.keys(columnMapping).filter((k) => columnMapping[k]);

    const rows = rawRows.map((row) => {
      const obj: Record<string, string> = {};
      for (const [dbKey, csvHeader] of Object.entries(columnMapping)) {
        if (!csvHeader) continue;
        const colIndex = rawHeaders.indexOf(csvHeader);
        if (colIndex === -1) continue;
        const cell = (row as unknown[])[colIndex];
        if (cell instanceof Date && !isNaN(cell.getTime())) {
          const yyyy = cell.getFullYear();
          const mm = String(cell.getMonth() + 1).padStart(2, "0");
          const dd = String(cell.getDate()).padStart(2, "0");
          obj[dbKey] = `${yyyy}-${mm}-${dd}`;
        } else {
          obj[dbKey] = String(cell ?? "").trim();
        }
      }
      return obj;
    }).filter((row) => Object.values(row).some((v) => v));

    if (rows.length === 0) {
      setError("No data rows found after mapping.");
      return;
    }

    setParsedRows(rows);
    setStep("preview");
  };

  const handleImport = async () => {
    setImporting(true);
    try {
      await onImport(parsedRows);
      reset();
      onOpenChange(false);
    } catch (err: any) {
      setError(err.message || "Import failed");
    } finally {
      setImporting(false);
    }
  };

  const downloadTemplate = () => {
    const ws = XLSX.utils.aoa_to_sheet([expectedColumns.map((column) => column.label)]);
    const wb = XLSX.utils.book_new();
    XLSX.utils.book_append_sheet(wb, ws, "Template");
    XLSX.writeFile(wb, templateFileName || "import_template.xlsx");
  };

  const updateMapping = (dbKey: string, csvHeader: string) => {
    setColumnMapping((prev) => ({
      ...prev,
      [dbKey]: csvHeader === "__none__" ? "" : csvHeader,
    }));
  };

  const mappedDbKeys = Object.keys(columnMapping).filter((k) => columnMapping[k]);
  const previewHeaders = step === "preview" ? mappedDbKeys : [];

  return (
    <Dialog open={open} onOpenChange={(nextOpen) => {
      if (!nextOpen) reset();
      onOpenChange(nextOpen);
    }}>
      <DialogContent className="max-w-4xl w-[95vw] max-h-[85vh] overflow-y-auto">
        <DialogHeader>
          <DialogTitle className="flex items-center gap-2">
            <FileSpreadsheet className="h-5 w-5" /> {title}
          </DialogTitle>
          <p className="text-sm text-muted-foreground">{description}</p>
        </DialogHeader>

        {/* Step 1: Upload */}
        {step === "upload" && (
          <div className="space-y-4 py-4">
            <div className="flex flex-col items-center gap-4 py-8 border-2 border-dashed rounded-lg">
              <Upload className="h-10 w-10 text-muted-foreground" />
              <p className="text-sm text-muted-foreground">Drop a CSV or Excel file, or click to browse</p>
              <input ref={fileInputRef} type="file" accept=".csv,.xlsx,.xls" className="hidden" onChange={handleFile} />
              <Button variant="outline" onClick={() => fileInputRef.current?.click()}>Select File</Button>
            </div>

            <div className="flex items-center justify-between text-sm">
              <span className="text-muted-foreground">Need a template?</span>
              <Button variant="link" size="sm" onClick={downloadTemplate}>Download Template (.xlsx)</Button>
            </div>

            {error && (
              <div className="flex items-center gap-2 text-destructive text-sm">
                <AlertCircle className="h-4 w-4" /> {error}
              </div>
            )}
          </div>
        )}

        {/* Step 2: Map Fields */}
        {step === "map" && (
          <div className="space-y-4">
            <div className="flex items-center justify-between">
              <div className="flex items-center gap-2 text-sm">
                <CheckCircle2 className="h-4 w-4 text-emerald-600" />
                <span className="font-medium">{fileName}</span> — {rawRows.length} rows, {rawHeaders.length} columns
              </div>
              <Button variant="ghost" size="sm" onClick={reset}>Choose Different File</Button>
            </div>

            <div className="bg-muted/50 rounded-lg p-4">
              <p className="text-sm font-medium mb-4">Map your file columns to fields</p>
              <div className="space-y-3">
                {expectedColumns.map((col) => (
                  <div key={col.key} className="flex items-center gap-4">
                    <div className="w-[45%] text-sm flex items-center gap-1.5">
                      <span className={col.required ? "font-medium" : "text-muted-foreground"}>
                        {col.label}
                      </span>
                      {col.required && <span className="text-destructive text-xs">*</span>}
                    </div>
                    <ArrowRight className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
                    <Select
                      value={columnMapping[col.key] || "__none__"}
                      onValueChange={(v) => updateMapping(col.key, v)}
                    >
                      <SelectTrigger className="h-9 text-sm flex-1">
                        <SelectValue placeholder="— skip —" />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="__none__">— skip —</SelectItem>
                        {rawHeaders.map((h) => (
                          <SelectItem key={h} value={h}>{h}</SelectItem>
                        ))}
                      </SelectContent>
                    </Select>
                  </div>
                ))}
              </div>
            </div>

            {/* Sample data preview */}
            {rawRows.length > 0 && (
              <div className="rounded-lg border overflow-x-auto max-h-36">
                <Table>
                  <TableHeader>
                    <TableRow>
                      {rawHeaders.map((h) => <TableHead key={h} className="text-xs font-mono whitespace-nowrap">{h}</TableHead>)}
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {rawRows.slice(0, 3).map((row, i) => (
                      <TableRow key={i}>
                        {rawHeaders.map((h, ci) => (
                          <TableCell key={ci} className="text-xs whitespace-nowrap">
                            {String((row as unknown[])[ci] ?? "").slice(0, 50) || "—"}
                          </TableCell>
                        ))}
                      </TableRow>
                    ))}
                  </TableBody>
                </Table>
              </div>
            )}

            {error && (
              <div className="flex items-center gap-2 text-destructive text-sm">
                <AlertCircle className="h-4 w-4" /> {error}
              </div>
            )}

            <div className="flex justify-end gap-2">
              <Button variant="outline" onClick={() => { reset(); onOpenChange(false); }}>Cancel</Button>
              <Button onClick={proceedToPreview}>
                Continue <ArrowRight className="h-3.5 w-3.5 ml-1" />
              </Button>
            </div>
          </div>
        )}

        {/* Step 3: Preview & Import */}
        {step === "preview" && (
          <div className="space-y-4">
            <div className="flex items-center justify-between">
              <div className="flex items-center gap-2 text-sm">
                <CheckCircle2 className="h-4 w-4 text-emerald-600" />
                <span className="font-medium">{parsedRows.length} rows</span> ready to import
              </div>
              <Button variant="ghost" size="sm" onClick={() => setStep("map")}>Back to Mapping</Button>
            </div>

            <div className="rounded-lg border overflow-x-auto max-h-64">
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead className="w-10 text-xs">#</TableHead>
                    {previewHeaders.map((key) => {
                      const col = expectedColumns.find((c) => c.key === key);
                      return <TableHead key={key} className="text-xs font-mono">{col?.label || key}</TableHead>;
                    })}
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {parsedRows.slice(0, 10).map((row, index) => (
                    <TableRow key={index}>
                      <TableCell className="text-xs text-muted-foreground">{index + 1}</TableCell>
                      {previewHeaders.map((key) => (
                        <TableCell key={key} className="text-xs">{row[key] || "—"}</TableCell>
                      ))}
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
              {parsedRows.length > 10 && (
                <p className="text-xs text-muted-foreground text-center py-2">
                  ...and {parsedRows.length - 10} more rows
                </p>
              )}
            </div>

            {error && (
              <div className="flex items-center gap-2 text-destructive text-sm">
                <AlertCircle className="h-4 w-4" /> {error}
              </div>
            )}

            <div className="flex justify-end gap-2">
              <Button variant="outline" onClick={() => { reset(); onOpenChange(false); }}>Cancel</Button>
              <Button onClick={handleImport} disabled={importing}>
                {importing ? "Importing..." : `Import ${parsedRows.length} rows`}
              </Button>
            </div>
          </div>
        )}
      </DialogContent>
    </Dialog>
  );
}
