import { useEffect, useMemo, useState } from "react";
import * as XLSX from "xlsx";
import { toast } from "sonner";
import { accounting } from "@/lib/accountingClient";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
} from "@/components/ui/dialog";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { AlertCircle, CheckCircle2, AlertTriangle, ArrowRight, Loader2 } from "lucide-react";

const ACCOUNT_TYPES = ["asset", "liability", "equity", "income", "expense"] as const;
type AccountType = (typeof ACCOUNT_TYPES)[number];

// Importable target fields, in the order shown in the mapping step.
const FIELDS: { key: string; label: string; required: boolean; hint: string }[] = [
  { key: "code", label: "Account code", required: false, hint: "e.g. 1100" },
  { key: "name", label: "Account name", required: true, hint: "e.g. Operating Cash" },
  { key: "type", label: "Type", required: true, hint: "Asset, Liability, Equity, Income, or Expense" },
  { key: "description", label: "Description", required: false, hint: "Optional notes" },
  { key: "is_bank", label: "Bank account?", required: false, hint: "Yes / No" },
  { key: "is_reserve", label: "Reserve fund?", required: false, hint: "Yes / No" },
];

// Map the many ways accounting systems label account types onto our 5 enum values.
const TYPE_ALIASES: Record<string, AccountType> = {
  asset: "asset", assets: "asset", bank: "asset", cash: "asset",
  "accounts receivable": "asset", "a/r": "asset", ar: "asset", receivable: "asset",
  "fixed asset": "asset", "fixed assets": "asset", "other asset": "asset",
  "other current asset": "asset", "current asset": "asset",
  liability: "liability", liabilities: "liability",
  "accounts payable": "liability", "a/p": "liability", ap: "liability", payable: "liability",
  "credit card": "liability", "current liability": "liability",
  "other current liability": "liability", "long term liability": "liability",
  "long-term liability": "liability",
  equity: "equity", equities: "equity", "retained earnings": "equity", capital: "equity",
  income: "income", revenue: "income", revenues: "income", sales: "income",
  "other income": "income",
  expense: "expense", expenses: "expense", "cost of goods sold": "expense",
  cogs: "expense", "other expense": "expense",
};

function normalizeType(raw: unknown): AccountType | null {
  const v = String(raw ?? "").trim().toLowerCase();
  if (!v) return null;
  if ((ACCOUNT_TYPES as readonly string[]).includes(v)) return v as AccountType;
  return TYPE_ALIASES[v] ?? null;
}

function parseBool(raw: unknown): boolean {
  const v = String(raw ?? "").trim().toLowerCase();
  return ["yes", "y", "true", "1", "x", "✓"].includes(v);
}

const norm = (s: string) => s.toLowerCase().replace(/[\s_-]/g, "");

type PreviewRow = {
  _id: number;
  code: string;
  name: string;
  type: AccountType | null;
  rawType: string;
  description: string;
  is_bank: boolean;
  is_reserve: boolean;
  errors: string[];
  duplicate: boolean;
};

export default function ChartOfAccountsImportDialog({
  open, onOpenChange, companyId, existingAccounts, onSuccess,
}: {
  open: boolean;
  onOpenChange: (v: boolean) => void;
  companyId: string;
  existingAccounts: { code?: string | null }[];
  onSuccess?: () => void;
}) {
  const [step, setStep] = useState(1);
  const [busy, setBusy] = useState(false);
  const [file, setFile] = useState<File | null>(null);
  const [headers, setHeaders] = useState<string[]>([]);
  const [rows, setRows] = useState<Record<string, any>[]>([]);
  const [mapping, setMapping] = useState<Record<string, string>>({});
  const [preview, setPreview] = useState<PreviewRow[]>([]);
  const [summary, setSummary] = useState<{ total: number; imported: number; skipped: number } | null>(null);

  useEffect(() => {
    if (!open) {
      setStep(1); setBusy(false); setFile(null); setHeaders([]); setRows([]);
      setMapping({}); setPreview([]); setSummary(null);
    }
  }, [open]);

  const existingCodes = useMemo(() => {
    const s = new Set<string>();
    for (const a of existingAccounts) {
      const c = String(a.code ?? "").trim().toLowerCase();
      if (c) s.add(c);
    }
    return s;
  }, [existingAccounts]);

  const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
    const f = e.target.files?.[0];
    if (!f) return;
    const ok = /\.(csv|xlsx|xls)$/i.test(f.name) || f.type === "text/csv";
    if (!ok) {
      toast.error("Please upload a CSV or Excel file.");
      return;
    }
    setFile(f);
  };

  const parseFile = () => {
    if (!file) return;
    setBusy(true);
    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const data = new Uint8Array(e.target!.result as ArrayBuffer);
        const wb = XLSX.read(data, { type: "array" });
        const ws = wb.Sheets[wb.SheetNames[0]];
        const json = XLSX.utils.sheet_to_json<any[]>(ws, { header: 1, blankrows: false });
        if (json.length < 2) {
          toast.error("The file appears to be empty or has only a header row.");
          setBusy(false);
          return;
        }
        const hdrs = (json[0] as any[]).map((h) => String(h ?? "").trim()).filter(Boolean);
        const dataRows = json.slice(1).map((r) => {
          const obj: Record<string, any> = {};
          hdrs.forEach((h, i) => { obj[h] = (r as any[])[i]; });
          return obj;
        });
        setHeaders(hdrs);
        setRows(dataRows);

        // Auto-map by header name.
        const auto: Record<string, string> = {};
        for (const field of FIELDS) {
          const match = hdrs.find((h) => norm(h) === norm(field.key) || norm(h) === norm(field.label));
          if (match) auto[field.key] = match;
        }
        setMapping(auto);
        setStep(2);
      } catch {
        toast.error("Could not read the file.");
      } finally {
        setBusy(false);
      }
    };
    reader.readAsArrayBuffer(file);
  };

  const buildPreview = () => {
    const missingRequired = FIELDS.filter((f) => f.required && !mapping[f.key]);
    if (missingRequired.length) {
      toast.error(`Map required fields: ${missingRequired.map((f) => f.label).join(", ")}`);
      return;
    }
    const seenCodes = new Set<string>();
    const out: PreviewRow[] = rows.map((row, i) => {
      const get = (key: string) => (mapping[key] ? row[mapping[key]] : undefined);
      const code = String(get("code") ?? "").trim();
      const name = String(get("name") ?? "").trim();
      const rawType = String(get("type") ?? "").trim();
      const type = normalizeType(rawType);
      const description = String(get("description") ?? "").trim();
      const is_bank = mapping.is_bank ? parseBool(get("is_bank")) : false;
      const is_reserve = mapping.is_reserve ? parseBool(get("is_reserve")) : false;

      const errors: string[] = [];
      if (!name) errors.push("Name is required");
      if (!type) errors.push(rawType ? `Unrecognized type "${rawType}"` : "Type is required");

      const codeKey = code.toLowerCase();
      let duplicate = false;
      if (codeKey) {
        if (existingCodes.has(codeKey)) duplicate = true;
        else if (seenCodes.has(codeKey)) duplicate = true;
        seenCodes.add(codeKey);
      }

      return { _id: i, code, name, type, rawType, description, is_bank, is_reserve, errors, duplicate };
    });
    setPreview(out);
    setStep(3);
  };

  const importable = useMemo(
    () => preview.filter((r) => r.errors.length === 0 && !r.duplicate),
    [preview]
  );

  const runImport = async () => {
    if (importable.length === 0) {
      toast.error("No valid new accounts to import.");
      return;
    }
    setBusy(true);
    try {
      const payload = importable.map((r) => ({
        company_id: companyId,
        name: r.name,
        code: r.code || null,
        type: r.type,
        description: r.description || null,
        is_bank: r.is_bank,
        is_reserve: r.is_reserve,
      }));

      const CHUNK = 200;
      for (let i = 0; i < payload.length; i += CHUNK) {
        const { error } = await accounting.from("accounts").insert(payload.slice(i, i + CHUNK));
        if (error) throw error;
      }

      setSummary({
        total: preview.length,
        imported: importable.length,
        skipped: preview.length - importable.length,
      });
      setStep(4);
      onSuccess?.();
    } catch (err: any) {
      toast.error(err?.message || "Import failed.");
    } finally {
      setBusy(false);
    }
  };

  const errorCount = preview.filter((r) => r.errors.length > 0).length;
  const dupCount = preview.filter((r) => r.errors.length === 0 && r.duplicate).length;

  return (
    <Dialog open={open} onOpenChange={(v) => { if (!busy) onOpenChange(v); }}>
      <DialogContent className="sm:max-w-3xl max-h-[90vh] flex flex-col">
        <DialogHeader className="shrink-0">
          <DialogTitle>
            {step === 1 && "Import Chart of Accounts"}
            {step === 2 && "Map Columns"}
            {step === 3 && "Preview Accounts"}
            {step === 4 && "Import Complete"}
          </DialogTitle>
          <DialogDescription>
            {step === 1 && "Upload a CSV or Excel file containing your account list."}
            {step === 2 && "Match your file's columns to account fields."}
            {step === 3 && "Review accounts before importing. Existing codes are skipped."}
            {step === 4 && "Review the results of the import."}
          </DialogDescription>
        </DialogHeader>

        <div className="flex-1 overflow-y-auto py-4 min-h-[280px]">
          {step === 1 && (
            <div className="space-y-4">
              <Alert>
                <AlertCircle className="h-4 w-4" />
                <AlertTitle className="font-semibold">Expected columns</AlertTitle>
                <AlertDescription className="mt-1 text-sm leading-relaxed">
                  At minimum include an <strong>Account name</strong> and a <strong>Type</strong>{" "}
                  (Asset, Liability, Equity, Income, or Expense). Optional columns: Account code,
                  Description, Bank account?, Reserve fund?. You'll map them on the next step.
                </AlertDescription>
              </Alert>
              <div className="space-y-2">
                <Label htmlFor="coa-import-file" className="text-sm font-semibold">Select file</Label>
                <Input
                  id="coa-import-file"
                  type="file"
                  accept=".csv,.xlsx,.xls,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel"
                  onChange={handleFile}
                  className="cursor-pointer"
                />
                {file && <p className="text-xs text-muted-foreground">Selected: {file.name}</p>}
              </div>
            </div>
          )}

          {step === 2 && (
            <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
              {FIELDS.map((field) => (
                <div key={field.key} className="space-y-1.5 p-3 border rounded-lg bg-muted/40">
                  <Label className="font-medium text-sm flex items-center">
                    {field.label}
                    {field.required && <span className="text-destructive ml-1">*</span>}
                  </Label>
                  <p className="text-xs text-muted-foreground">{field.hint}</p>
                  <Select
                    value={mapping[field.key] ?? "__none__"}
                    onValueChange={(v) =>
                      setMapping((m) => {
                        const next = { ...m };
                        if (v === "__none__") delete next[field.key];
                        else next[field.key] = v;
                        return next;
                      })
                    }
                  >
                    <SelectTrigger className={`bg-background ${field.required && !mapping[field.key] ? "border-amber-300" : ""}`}>
                      <SelectValue placeholder="Select a column…" />
                    </SelectTrigger>
                    <SelectContent>
                      <SelectItem value="__none__">— Not mapped —</SelectItem>
                      {headers.map((h) => <SelectItem key={h} value={h}>{h}</SelectItem>)}
                    </SelectContent>
                  </Select>
                </div>
              ))}
            </div>
          )}

          {step === 3 && (
            <div className="space-y-4">
              <Alert variant={errorCount > 0 ? "destructive" : "default"}>
                {errorCount > 0
                  ? <AlertCircle className="h-4 w-4" />
                  : <CheckCircle2 className="h-4 w-4 text-emerald-600" />}
                <AlertTitle>{errorCount > 0 ? "Some rows have errors" : "Ready to import"}</AlertTitle>
                <AlertDescription>
                  {importable.length} new account(s) will be imported
                  {dupCount > 0 ? `, ${dupCount} duplicate(s) skipped` : ""}
                  {errorCount > 0 ? `, ${errorCount} row(s) with errors skipped` : ""}.
                </AlertDescription>
              </Alert>
              <div className="rounded-md border overflow-x-auto">
                <Table className="min-w-[640px]">
                  <TableHeader>
                    <TableRow>
                      <TableHead className="w-24">Code</TableHead>
                      <TableHead>Name</TableHead>
                      <TableHead className="w-28">Type</TableHead>
                      <TableHead className="w-20">Bank</TableHead>
                      <TableHead className="w-20">Reserve</TableHead>
                      <TableHead className="w-10"></TableHead>
                    </TableRow>
                  </TableHeader>
                  <TableBody>
                    {preview.map((r) => {
                      const bad = r.errors.length > 0;
                      return (
                        <TableRow key={r._id} className={bad ? "bg-destructive/5" : (r.duplicate ? "bg-amber-50/40" : "")}>
                          <TableCell className="font-mono text-xs text-muted-foreground">{r.code || "—"}</TableCell>
                          <TableCell className="text-sm">{r.name || <span className="text-destructive">Missing</span>}</TableCell>
                          <TableCell className="text-sm capitalize">
                            {r.type ?? <span className="text-destructive">{r.rawType || "Missing"}</span>}
                          </TableCell>
                          <TableCell className="text-xs">{r.is_bank ? "Yes" : "—"}</TableCell>
                          <TableCell className="text-xs">{r.is_reserve ? "Yes" : "—"}</TableCell>
                          <TableCell>
                            {bad ? (
                              <span title={r.errors.join(", ")}><AlertCircle className="h-4 w-4 text-destructive" /></span>
                            ) : r.duplicate ? (
                              <span title="Code already exists — will be skipped"><AlertTriangle className="h-4 w-4 text-amber-500" /></span>
                            ) : (
                              <CheckCircle2 className="h-4 w-4 text-emerald-500" />
                            )}
                          </TableCell>
                        </TableRow>
                      );
                    })}
                  </TableBody>
                </Table>
              </div>
            </div>
          )}

          {step === 4 && summary && (
            <div className="flex flex-col items-center justify-center py-8 space-y-6">
              <div className="w-16 h-16 rounded-full bg-emerald-100 flex items-center justify-center text-emerald-600">
                <CheckCircle2 className="w-8 h-8" />
              </div>
              <div className="text-center">
                <h3 className="text-xl font-bold">Import Complete</h3>
                <p className="text-muted-foreground text-sm">Your chart of accounts has been updated.</p>
              </div>
              <div className="grid grid-cols-3 gap-4 w-full max-w-md">
                <div className="bg-muted border rounded-xl p-4 text-center">
                  <p className="text-xs font-semibold text-muted-foreground uppercase mb-1">Total</p>
                  <p className="text-2xl font-bold">{summary.total}</p>
                </div>
                <div className="bg-emerald-50 border border-emerald-100 rounded-xl p-4 text-center">
                  <p className="text-xs font-semibold text-emerald-600 uppercase mb-1">Imported</p>
                  <p className="text-2xl font-bold text-emerald-700">{summary.imported}</p>
                </div>
                <div className="bg-amber-50 border border-amber-100 rounded-xl p-4 text-center">
                  <p className="text-xs font-semibold text-amber-600 uppercase mb-1">Skipped</p>
                  <p className="text-2xl font-bold text-amber-700">{summary.skipped}</p>
                </div>
              </div>
            </div>
          )}
        </div>

        <DialogFooter className="shrink-0 pt-4 border-t">
          {step < 4 ? (
            <>
              <Button variant="outline" onClick={() => onOpenChange(false)} disabled={busy}>Cancel</Button>
              {step === 1 && (
                <Button onClick={parseFile} disabled={!file || busy}>
                  {busy ? <><Loader2 className="mr-2 h-4 w-4 animate-spin" /> Parsing…</> : "Continue"}
                </Button>
              )}
              {step === 2 && (
                <>
                  <Button variant="outline" onClick={() => setStep(1)} disabled={busy}>Back</Button>
                  <Button onClick={buildPreview} disabled={busy}>Generate Preview</Button>
                </>
              )}
              {step === 3 && (
                <>
                  <Button variant="outline" onClick={() => setStep(2)} disabled={busy}>Back</Button>
                  <Button onClick={runImport} disabled={busy || importable.length === 0}>
                    {busy ? <><Loader2 className="mr-2 h-4 w-4 animate-spin" /> Importing…</> : `Import ${importable.length} account(s)`}
                  </Button>
                </>
              )}
            </>
          ) : (
            <Button onClick={() => onOpenChange(false)}>Done <ArrowRight className="w-4 h-4 ml-2" /></Button>
          )}
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}
