import { useEffect, useMemo, useState } from "react";
import ImportSpreadsheetDialog from "@/components/ImportSpreadsheetDialog";
import { supabase } from "@/integrations/supabase/client";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Badge } from "@/components/ui/badge";
import { useToast } from "@/hooks/use-toast";
import { Download, Upload, BookOpen } from "lucide-react";

const PAGE_SIZE = 1000;

const normalize = (value: string | null | undefined) =>
  String(value ?? "").trim().replace(/\s+/g, " ").toLowerCase();

const nullable = (value: string | null | undefined) => {
  const trimmed = String(value ?? "").trim();
  return trimmed ? trimmed : null;
};

const parseBool = (value: string | null | undefined): boolean | null => {
  const v = normalize(value);
  if (!v) return null;
  if (["true", "yes", "y", "1", "active"].includes(v)) return true;
  if (["false", "no", "n", "0", "inactive"].includes(v)) return false;
  return null;
};

interface Account {
  id: string;
  account_number: string;
  account_name: string;
  account_type: string;
  accounting_system: string;
  description: string | null;
  is_active: boolean;
  association_ids: string[] | null;
}

interface Association {
  id: string;
  name: string;
}

export default function BulkChartOfAccountsUpdatesTab() {
  const { toast } = useToast();
  const [accounts, setAccounts] = useState<Account[]>([]);
  const [associations, setAssociations] = useState<Association[]>([]);
  const [loading, setLoading] = useState(false);
  const [importOpen, setImportOpen] = useState(false);
  const [lastResult, setLastResult] = useState<{ updated: number; skipped: number } | null>(null);

  useEffect(() => {
    fetchAll();
  }, []);

  const fetchAll = async () => {
    setLoading(true);
    const all: Account[] = [];
    for (let from = 0; ; from += PAGE_SIZE) {
      const { data, error } = await supabase
        .from("chart_of_accounts")
        .select("id, account_number, account_name, account_type, accounting_system, description, is_active, association_ids")
        .order("account_number")
        .range(from, from + PAGE_SIZE - 1);
      if (error) {
        toast({ title: "Error loading accounts", description: error.message, variant: "destructive" });
        break;
      }
      if (!data?.length) break;
      all.push(...(data as Account[]));
      if (data.length < PAGE_SIZE) break;
    }
    setAccounts(all);

    const { data: assocs, error: assocErr } = await supabase
      .from("associations")
      .select("id, name")
      .order("name");
    if (assocErr) {
      toast({ title: "Error loading associations", description: assocErr.message, variant: "destructive" });
    } else {
      setAssociations((assocs as Association[]) ?? []);
    }
    setLoading(false);
  };

  const assocByName = useMemo(() => {
    const m = new Map<string, string>();
    associations.forEach((a) => m.set(normalize(a.name), a.id));
    return m;
  }, [associations]);

  const assocById = useMemo(() => {
    const m = new Map<string, string>();
    associations.forEach((a) => m.set(a.id, a.name));
    return m;
  }, [associations]);

  const lookups = useMemo(() => {
    const byId = new Map<string, Account>();
    const byNumber = new Map<string, Account>(); // key: number|system
    accounts.forEach((a) => {
      byId.set(a.id, a);
      byNumber.set(`${normalize(a.account_number)}|${normalize(a.accounting_system)}`, a);
    });
    return { byId, byNumber };
  }, [accounts]);

  const parseAssociationIds = (raw: string | undefined | null): string[] | null => {
    const v = nullable(raw);
    if (v === null) return null;
    if (v.toLowerCase() === "global" || v.toLowerCase() === "all" || v === "*") return [];
    const parts = v.split(/[;,|]/).map((p) => p.trim()).filter(Boolean);
    const ids: string[] = [];
    for (const part of parts) {
      // Could be a UUID or association name
      if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(part)) {
        ids.push(part);
      } else {
        const id = assocByName.get(normalize(part));
        if (id) ids.push(id);
      }
    }
    return ids;
  };

  const handleImportUpdates = async (rows: Record<string, string>[]) => {
    const updates: Array<{ id: string; payload: Record<string, unknown> }> = [];
    let skipped = 0;

    rows.forEach((row) => {
      const id = nullable(row.account_id);
      const number = nullable(row.account_number);
      const system = nullable(row.accounting_system) ?? "buildium";

      let matched: Account | undefined;
      if (id) matched = lookups.byId.get(id);
      if (!matched && number) {
        matched = lookups.byNumber.get(`${normalize(number)}|${normalize(system)}`);
      }
      if (!matched) {
        skipped += 1;
        return;
      }

      const payload: Record<string, unknown> = { updated_at: new Date().toISOString() };

      const newNumber = nullable(row.new_account_number);
      if (newNumber) payload.account_number = newNumber;

      const newName = nullable(row.new_account_name) ?? nullable(row.account_name);
      if (newName && newName !== matched.account_name) payload.account_name = newName;

      const newType = nullable(row.account_type);
      if (newType) payload.account_type = newType;

      const desc = nullable(row.description);
      if (desc !== null) payload.description = desc;

      const active = parseBool(row.is_active);
      if (active !== null) payload.is_active = active;

      const assocIds = parseAssociationIds(row.association_ids);
      if (assocIds !== null) payload.association_ids = assocIds;

      if (Object.keys(payload).length > 1) updates.push({ id: matched.id, payload });
      else skipped += 1;
    });

    for (let i = 0; i < updates.length; i += 25) {
      const results = await Promise.all(
        updates.slice(i, i + 25).map(({ id, payload }) =>
          supabase.from("chart_of_accounts").update(payload).eq("id", id)
        )
      );
      const failed = results.find((r) => r.error);
      if (failed?.error) throw failed.error;
    }

    setLastResult({ updated: updates.length, skipped });
    toast({ title: "Bulk update complete", description: `${updates.length} accounts updated, ${skipped} rows skipped.` });
    await fetchAll();
  };

  const handleExportTemplate = async () => {
    const csv = [
      "Account ID,Account Number,Accounting System,New Account Number,Account Name,New Account Name,Account Type,Description,Is Active,Association IDs",
      ...accounts.map((a) => {
        const assocLabel = (a.association_ids ?? [])
          .map((id) => assocById.get(id) ?? id)
          .join("; ");
        return [
          a.id,
          a.account_number || "",
          a.accounting_system || "",
          "",
          a.account_name || "",
          "",
          a.account_type || "",
          a.description || "",
          a.is_active ? "true" : "false",
          assocLabel,
        ].map((v) => `"${String(v).replace(/"/g, '""')}"`).join(",");
      }),
    ].join("\n");
    const { saveFile } = await import("@/lib/saveFile");
    await saveFile(new Blob([csv], { type: "text/csv" }), {
      suggestedName: "bulk_chart_of_accounts_updates.csv",
      mimeType: "text/csv",
      description: "CSV file",
    });
  };

  return (
    <div className="space-y-6">
      <p className="text-sm text-muted-foreground">
        Update Chart of Accounts entries — name, number, type, description, active status, and association tags — from a spreadsheet.
        Rows are matched by Account ID first, then by Account Number + Accounting System. Use "global" or leave Association IDs blank to clear tags;
        list association names or UUIDs separated by semicolons to tag specific associations.
      </p>

      <Card>
        <CardHeader><CardTitle>Update Spreadsheet</CardTitle></CardHeader>
        <CardContent className="space-y-4">
          <div className="flex flex-wrap gap-2">
            <Button variant="outline" className="gap-2" onClick={handleExportTemplate} disabled={!accounts.length}>
              <Download className="h-4 w-4" /> Export Update Template
            </Button>
            <Button className="gap-2" onClick={() => setImportOpen(true)}>
              <Upload className="h-4 w-4" /> Import Updates
            </Button>
          </div>
          {lastResult && <Badge variant="secondary">Last import: {lastResult.updated} updated, {lastResult.skipped} skipped</Badge>}
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="flex items-center gap-2">
            <BookOpen className="h-5 w-5" /> Current Chart of Accounts ({accounts.length})
          </CardTitle>
        </CardHeader>
        <CardContent>
          <div className="rounded-md border border-border overflow-x-auto">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>Number</TableHead>
                  <TableHead>Name</TableHead>
                  <TableHead>Type</TableHead>
                  <TableHead>System</TableHead>
                  <TableHead>Active</TableHead>
                  <TableHead>Associations</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {loading ? (
                  <TableRow><TableCell colSpan={6} className="text-center text-muted-foreground py-8">Loading accounts...</TableCell></TableRow>
                ) : accounts.slice(0, 100).map((a) => (
                  <TableRow key={a.id}>
                    <TableCell className="font-mono text-xs">{a.account_number}</TableCell>
                    <TableCell className="font-medium">{a.account_name}</TableCell>
                    <TableCell>{a.account_type}</TableCell>
                    <TableCell className="capitalize">{a.accounting_system}</TableCell>
                    <TableCell>{a.is_active ? "Yes" : "No"}</TableCell>
                    <TableCell className="text-xs text-muted-foreground">
                      {(a.association_ids?.length ?? 0) === 0
                        ? "Global"
                        : a.association_ids!.map((id) => assocById.get(id) ?? id.slice(0, 8)).join(", ")}
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
            {accounts.length > 100 && <p className="text-xs text-muted-foreground p-2">Showing first 100 of {accounts.length} accounts.</p>}
          </div>
        </CardContent>
      </Card>

      <ImportSpreadsheetDialog
        open={importOpen}
        onOpenChange={setImportOpen}
        title="Import Chart of Accounts Updates"
        description="Rows are matched by Account ID first, then by Account Number + Accounting System. Blank update fields are ignored."
        templateFileName="bulk_chart_of_accounts_template.xlsx"
        expectedColumns={[
          { key: "account_id", label: "Account ID", aliases: ["ID"] },
          { key: "account_number", label: "Account Number", required: true, aliases: ["Number"] },
          { key: "accounting_system", label: "Accounting System", aliases: ["System"] },
          { key: "new_account_number", label: "New Account Number" },
          { key: "account_name", label: "Account Name", aliases: ["Name"] },
          { key: "new_account_name", label: "New Account Name" },
          { key: "account_type", label: "Account Type", aliases: ["Type"] },
          { key: "description", label: "Description" },
          { key: "is_active", label: "Is Active", aliases: ["Active"] },
          { key: "association_ids", label: "Association IDs", aliases: ["Associations", "Association Names"] },
        ]}
        onImport={handleImportUpdates}
      />
    </div>
  );
}
