import { useEffect, useMemo, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { Info, Lock, LockOpen } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { AccountTreePicker } from "@/components/AccountTree";
import { cn } from "@/lib/utils";
import { formatDate, formatMoney, todayISO } from "@/lib/format";
import { AccountLedgerDrilldown } from "@/components/AccountLedgerDrilldown";
import { ACCOUNT_CODE_HINT, validateAccountDraft, type RuleAccount } from "@/lib/account-rules";
import {
  createAccount,
  listAccountBalancesAsOf,
  postOpeningBalances,
  updateAccount,
  type AccountBalance,
} from "@/lib/erp";

export type DialogAccountType = {
  id: string;
  code: string;
  name: string;
  category: string;
  normal_balance: string;
};

export type DialogAccount = RuleAccount & {
  account_type_id: string;
  description: string | null;
  account_types: { code?: string; name?: string; category: string } | null;
};

/** Category groupings shown in the "Account type" select, QuickBooks style. */
const CATEGORY_LABELS: Record<string, string> = {
  asset: "Assets",
  liability: "Liabilities",
  equity: "Equity",
  revenue: "Income",
  expense: "Expenses",
};

const STATEMENT_OF: Record<string, string> = {
  asset: "Balance Sheet",
  liability: "Balance Sheet",
  equity: "Balance Sheet",
  revenue: "Profit & Loss",
  expense: "Profit & Loss",
};

/** Next free code inside the block of the chosen type (e.g. 5xxx expenses). */
function suggestCode(
  accounts: DialogAccount[],
  typeId: string,
  byTypeId: (id: string) => string | undefined,
) {
  const category = byTypeId(typeId);
  const siblings = accounts
    .filter((a) =>
      category ? a.account_types?.category === category : a.account_type_id === typeId,
    )
    .filter((a) => /^\d+$/.test(a.code))
    .map((a) => Number(a.code));
  if (siblings.length === 0) return "";
  const base = Math.min(...siblings);
  const block = Math.floor(base / 1000) * 1000;
  let next = Math.max(...siblings) + 10;
  if (next >= block + 1000) next = Math.max(...siblings) + 1;
  const used = new Set(siblings);
  while (used.has(next)) next += 1;
  return String(next);
}

type Draft = {
  name: string;
  code: string;
  category: string;
  account_type_id: string;
  is_sub: boolean;
  parent_id: string;
  opening_balance: string;
  opening_date: string;
  description: string;
  is_active: boolean;
};

function draftFrom(row: DialogAccount | null, types: DialogAccountType[]): Draft {
  const type = row ? types.find((t) => t.id === row.account_type_id) : undefined;
  return {
    name: row?.name ?? "",
    code: row?.code ?? "",
    category: type?.category ?? "",
    account_type_id: row?.account_type_id ?? "",
    is_sub: !!row?.parent_id,
    parent_id: row?.parent_id ?? "",
    opening_balance: "",
    opening_date: todayISO(),
    description: row?.description ?? "",
    is_active: row?.is_active ?? true,
  };
}

/**
 * QuickBooks-style new/edit account window: account name + number, account type
 * (category) and detail type, optional sub-account, opening balance, description,
 * lock (active) control and a live statement preview of where it will land.
 */
export function NewAccountDialog({
  open,
  onOpenChange,
  accounts,
  types,
  editing,
  onSaved,
}: {
  open: boolean;
  onOpenChange: (v: boolean) => void;
  accounts: DialogAccount[];
  types: DialogAccountType[];
  editing: DialogAccount | null;
  onSaved?: () => void;
}) {
  const qc = useQueryClient();
  const [draft, setDraft] = useState<Draft>(() => draftFrom(editing, types));

  useEffect(() => {
    if (open) setDraft(draftFrom(editing, types));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, editing?.id, types.length]);

  const categoryOf = (typeId: string) => types.find((t) => t.id === typeId)?.category;
  const categories = useMemo(() => Array.from(new Set(types.map((t) => t.category))), [types]);
  const detailTypes = useMemo(
    () => types.filter((t) => t.category === draft.category),
    [types, draft.category],
  );

  const parentOptions = useMemo(
    () =>
      accounts.filter(
        (a) =>
          a.id !== editing?.id && (!draft.category || a.account_types?.category === draft.category),
      ),
    [accounts, draft.category, editing?.id],
  );

  /** Blocking rule error (same rules the database triggers enforce). */
  const ruleError = useMemo(
    () =>
      validateAccountDraft({
        code: draft.code,
        name: draft.name || "placeholder",
        accountTypeId: draft.account_type_id,
        category: draft.category || undefined,
        parentId: draft.is_sub ? draft.parent_id || null : null,
        accounts: accounts as RuleAccount[],
        editingId: editing?.id ?? null,
      }),
    [draft, accounts, editing?.id],
  );

  /** Per-field inline messages, only shown once a field has been touched. */
  const [touched, setTouched] = useState<Record<string, boolean>>({});
  const touch = (field: string) => setTouched((t) => ({ ...t, [field]: true }));

  const fieldErrors = useMemo(() => {
    const out: Record<string, string> = {};
    if (!draft.name.trim()) out["name"] = "Account name is required.";
    if (!draft.account_type_id) out["account_type_id"] = "Pick an account type and detail type.";
    if (draft.is_sub && !draft.parent_id) out["parent_id"] = "Choose the parent account.";
    if (draft.category !== "asset" && Number(draft.opening_balance || 0) > 0)
      out["opening_balance"] = "Opening balances are only available for asset accounts.";
    if (draft.opening_balance && !Number.isFinite(Number(draft.opening_balance)))
      out["opening_balance"] = "Enter a number.";
    if (ruleError) {
      const key = /code|number|digit/i.test(ruleError)
        ? "code"
        : /parent|sub-account/i.test(ruleError)
          ? "parent_id"
          : /type|category/i.test(ruleError)
            ? "account_type_id"
            : "code";
      out[key] = ruleError;
    }
    return out;
  }, [draft, ruleError]);

  const blocked = Object.keys(fieldErrors).length > 0;
  const errorFor = (field: string) => (touched[field] ? fieldErrors[field] : undefined);

  const openingAmount = Number(draft.opening_balance || 0);
  const canSeedOpening =
    !editing && draft.category === "asset" && Number.isFinite(openingAmount) && openingAmount > 0;
  const equityAccount = accounts.find((a) => a.code === "3000");

  /** Live ledger balances as of the chosen date — drives the statement preview. */
  const asOf = draft.opening_date || todayISO();
  const { data: ledger = [], isFetching: ledgerLoading } = useQuery({
    queryKey: ["account-balances-as-of", asOf],
    queryFn: () => listAccountBalancesAsOf(asOf),
    enabled: open,
    staleTime: 30_000,
  });
  const balanceOf = (id: string) =>
    (ledger as AccountBalance[]).find((b) => b.account_id === id)?.balance ?? 0;
  const normalBalanceOf = (id: string) => {
    const cat = accounts.find((a) => a.id === id)?.account_types?.category;
    return cat === "liability" || cat === "equity" || cat === "income" ? "credit" : "debit";
  };

  /** Drilldown target for the "As of" preview lines. */
  const [drill, setDrill] = useState<{ id: string; label: string; normal: string } | null>(null);


  const previewSiblings = useMemo(() => {
    const parent = draft.is_sub ? accounts.find((a) => a.id === draft.parent_id) : null;
    const pool = parent
      ? accounts.filter((a) => a.parent_id === parent.id)
      : accounts.filter((a) => a.account_types?.category === draft.category && !a.parent_id);
    return { parent, rows: pool.filter((a) => a.is_active).slice(0, 6), all: pool };
  }, [accounts, draft.is_sub, draft.parent_id, draft.category]);

  /** Section total straight from the ledger, plus the pending opening balance. */
  const sectionTotal =
    previewSiblings.all.reduce((sum, a) => sum + balanceOf(a.id), 0) +
    (canSeedOpening ? openingAmount : 0);

  const save = useMutation({
    mutationFn: async () => {
      const blocking = validateAccountDraft({
        code: draft.code,
        name: draft.name,
        accountTypeId: draft.account_type_id,
        category: draft.category || undefined,
        parentId: draft.is_sub ? draft.parent_id || null : null,
        accounts: accounts as RuleAccount[],
        editingId: editing?.id ?? null,
      });
      if (blocking) throw new Error(blocking);
      if (editing?.is_system) throw new Error("System control accounts are read-only.");
      const payload = {
        code: draft.code.trim(),
        name: draft.name.trim(),
        account_type_id: draft.account_type_id,
        parent_id: draft.is_sub ? draft.parent_id || null : null,
        description: draft.description,
        is_active: draft.is_active,
      };
      const row = editing
        ? ((await updateAccount(editing.id, payload)) as unknown as { id: string })
        : ((await createAccount(payload)) as unknown as { id: string });

      if (canSeedOpening && row?.id) {
        if (!equityAccount)
          throw new Error("Owner Capital (3000) is missing — cannot seed an opening balance.");
        await postOpeningBalances({
          entry_date: draft.opening_date,
          equity_account_id: equityAccount.id,
          memo: `Opening balance — ${payload.code} ${payload.name}`,
          lines: [{ account_id: row.id, amount: openingAmount, description: "Opening balance" }],
        });
      }
      return row;
    },
    onSuccess: () => {
      toast.success(editing ? "Account updated" : "Account created");
      qc.invalidateQueries({ queryKey: ["accounts"] });
      qc.invalidateQueries({ queryKey: ["account-balances"] });
      onOpenChange(false);
      onSaved?.();
    },
    onError: (e: Error) => toast.error(e.message),
  });

  const statement = STATEMENT_OF[draft.category] ?? "Balance Sheet";

  const submit = () => {
    setTouched({
      name: true,
      code: true,
      account_type_id: true,
      parent_id: true,
      opening_balance: true,
    });
    if (blocked) {
      toast.error(Object.values(fieldErrors)[0] ?? "Check the highlighted fields.");
      return;
    }
    save.mutate();
  };

  return (
    <Dialog open={open} onOpenChange={(v) => (save.isPending ? null : onOpenChange(v))}>
      <DialogContent
        aria-describedby="new-account-hint"
        className="max-h-[92vh] gap-0 overflow-y-auto p-0 focus-visible:outline-none sm:max-w-xl"
        onEscapeKeyDown={(e) => {
          if (save.isPending) e.preventDefault();
        }}
      >
        <TooltipProvider>
          <DialogHeader className="border-b border-border px-6 py-4">
            <DialogTitle className="text-center text-base font-semibold">
              {editing ? "Edit account" : "New account"}
            </DialogTitle>
            <p id="new-account-hint" className="text-center text-xs text-muted-foreground">
              Press Escape to close. Required fields are marked with an asterisk.
            </p>
          </DialogHeader>

          <form
            id="new-account-form"
            className="space-y-5 px-6 py-5"
            onSubmit={(e) => {
              e.preventDefault();
              submit();
            }}
          >
            <div className="grid gap-4 sm:grid-cols-2">
              <div className="space-y-1.5">
                <Label htmlFor="acct-name" className="text-xs text-muted-foreground">
                  Account name*
                </Label>
                <Input
                  id="acct-name"
                  autoFocus
                  value={draft.name}
                  aria-invalid={!!errorFor("name")}
                  aria-describedby={errorFor("name") ? "acct-name-error" : undefined}
                  onBlur={() => touch("name")}
                  onChange={(e) => setDraft((d) => ({ ...d, name: e.target.value }))}
                />
                <FieldError id="acct-name-error" message={errorFor("name")} />
              </div>
              <div className="space-y-1.5">
                <Label htmlFor="acct-code" className="text-xs text-muted-foreground">
                  Account number
                </Label>
                <Input
                  id="acct-code"
                  value={draft.code}
                  placeholder="5310"
                  inputMode="numeric"
                  aria-invalid={!!errorFor("code")}
                  aria-describedby={errorFor("code") ? "acct-code-error" : undefined}
                  onBlur={() => touch("code")}
                  onChange={(e) => setDraft((d) => ({ ...d, code: e.target.value }))}
                />
                <FieldError id="acct-code-error" message={errorFor("code")} />
              </div>
            </div>

            <div className="grid gap-4 sm:grid-cols-2">
              <div className="space-y-1.5">
                <span className="flex items-center gap-1.5 text-xs text-muted-foreground">
                  Account type*
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <span className="cursor-help text-muted-foreground">
                        <Info className="size-3.5" />
                      </span>
                    </TooltipTrigger>
                    <TooltipContent>
                      The type decides the statement, normal balance and the code block for this
                      account.
                    </TooltipContent>
                  </Tooltip>
                </span>
                <Select
                  value={draft.category}
                  onValueChange={(v) => {
                    touch("account_type_id");
                    setDraft((d) => ({ ...d, category: v, account_type_id: "", parent_id: "" }));
                  }}
                >
                  <SelectTrigger aria-label="Account type">
                    <SelectValue placeholder="Select account type" />
                  </SelectTrigger>
                  <SelectContent>
                    {categories.map((c) => (
                      <SelectItem key={c} value={c}>
                        {CATEGORY_LABELS[c] ?? c}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
              </div>
              <div className="space-y-1.5">
                <Label className="text-xs text-muted-foreground">Detail type*</Label>
                <Select
                  value={draft.account_type_id}
                  onValueChange={(v) => {
                    touch("account_type_id");
                    setDraft((d) => ({
                      ...d,
                      account_type_id: v,
                      category: categoryOf(v) ?? d.category,
                      code: d.code || suggestCode(accounts, v, categoryOf),
                    }));
                  }}
                  disabled={!draft.category}
                >
                  <SelectTrigger
                    aria-label="Detail type"
                    aria-invalid={!!errorFor("account_type_id")}
                    className={cn(draft.account_type_id && "border-primary ring-1 ring-primary/30")}
                  >
                    <SelectValue placeholder="Select detail type" />
                  </SelectTrigger>
                  <SelectContent>
                    {detailTypes.map((t) => (
                      <SelectItem key={t.id} value={t.id}>
                        {t.name}
                      </SelectItem>
                    ))}
                  </SelectContent>
                </Select>
                <FieldError message={errorFor("account_type_id")} />
                <p className="text-xs text-muted-foreground">{ACCOUNT_CODE_HINT}</p>
              </div>
            </div>

            <div className="space-y-3">
              <label className="flex items-center gap-2.5 text-sm">
                <Checkbox
                  checked={draft.is_sub}
                  onCheckedChange={(v) =>
                    setDraft((d) => ({ ...d, is_sub: !!v, parent_id: v ? d.parent_id : "" }))
                  }
                />
                Make this a subaccount
              </label>
              {draft.is_sub ? (
                <>
                  <AccountTreePicker
                    accounts={parentOptions}
                    value={draft.parent_id || null}
                    onChange={(id) => {
                      touch("parent_id");
                      setDraft((d) => ({ ...d, parent_id: id ?? "" }));
                    }}
                    emptyLabel={
                      draft.category
                        ? "No accounts in this category yet — this one starts at top level."
                        : "Pick an account type first to see its tree."
                    }
                  />
                  <FieldError message={errorFor("parent_id")} />
                </>
              ) : null}
            </div>

            <div className="grid gap-4 sm:grid-cols-2">
              {!editing ? (
                <div className="space-y-1.5">
                  <span className="flex items-center gap-1.5 text-xs text-muted-foreground">
                    Opening balance
                    <Tooltip>
                      <TooltipTrigger asChild>
                        <span className="cursor-help">
                          <Info className="size-3.5" />
                        </span>
                      </TooltipTrigger>
                      <TooltipContent>
                        Asset accounts only. Posts a balanced opening entry funded from Owner
                        Capital.
                      </TooltipContent>
                    </Tooltip>
                  </span>
                  <Input
                    id="acct-opening"
                    inputMode="decimal"
                    value={draft.opening_balance}
                    disabled={draft.category !== "asset"}
                    placeholder="0.00"
                    aria-invalid={!!errorFor("opening_balance")}
                    aria-describedby={
                      errorFor("opening_balance") ? "acct-opening-error" : undefined
                    }
                    onBlur={() => touch("opening_balance")}
                    onChange={(e) => setDraft((d) => ({ ...d, opening_balance: e.target.value }))}
                  />
                  <FieldError id="acct-opening-error" message={errorFor("opening_balance")} />
                  <p className="text-xs text-muted-foreground">
                    {draft.category === "asset"
                      ? "Funded from Owner Capital (3000)."
                      : "Available for asset accounts."}
                  </p>
                </div>
              ) : null}
              <div className="space-y-1.5">
                <Label htmlFor="acct-asof" className="text-xs text-muted-foreground">
                  As of
                </Label>
                <Input
                  id="acct-asof"
                  type="date"
                  value={draft.opening_date}
                  onChange={(e) => setDraft((d) => ({ ...d, opening_date: e.target.value }))}
                />
                <p className="text-xs text-muted-foreground">
                  Drives the preview balances below (and the opening entry date).
                </p>
              </div>
            </div>

            <div className="space-y-1.5">
              <Label htmlFor="acct-desc" className="text-xs text-muted-foreground">
                Description
              </Label>
              <Textarea
                id="acct-desc"
                rows={2}
                value={draft.description}
                onChange={(e) => setDraft((d) => ({ ...d, description: e.target.value }))}
              />
            </div>

            <div className="flex items-center gap-3 border-t border-border pt-4">
              <span className="text-sm font-medium underline decoration-dotted">Lock account</span>
              <div className="inline-flex overflow-hidden rounded-md border border-border">
                <Button
                  type="button"
                  size="icon"
                  variant={draft.is_active ? "ghost" : "secondary"}
                  aria-label="Lock account (inactive)"
                  aria-pressed={!draft.is_active}
                  className="rounded-none"
                  onClick={() => setDraft((d) => ({ ...d, is_active: false }))}
                >
                  <Lock className="size-4" />
                </Button>
                <Button
                  type="button"
                  size="icon"
                  variant={draft.is_active ? "secondary" : "ghost"}
                  aria-label="Unlock account (active)"
                  aria-pressed={draft.is_active}
                  className="rounded-none border-l border-border"
                  onClick={() => setDraft((d) => ({ ...d, is_active: true }))}
                >
                  <LockOpen className="size-4" />
                </Button>
              </div>
              <span className="text-xs text-muted-foreground">
                {draft.is_active
                  ? "Active — selectable on new entries."
                  : "Locked — stays in reports but cannot be selected."}
              </span>
            </div>

            <div className="rounded-lg border border-border">
              <div className="flex items-center justify-between border-b border-border px-4 py-3">
                <div>
                  <p className="text-sm font-medium">{statement}</p>
                  <p className="text-xs text-muted-foreground">
                    Ledger balances as of {formatDate(asOf)}
                    {ledgerLoading ? " · refreshing…" : ""}
                  </p>
                </div>
                <span className="rounded bg-primary px-2 py-1 text-[10px] font-semibold uppercase tracking-wide text-primary-foreground">
                  New account preview
                </span>
              </div>
              <div className="space-y-1 px-4 py-3 text-sm">
                {previewSiblings.parent ? (
                  <PreviewRow
                    label={`${previewSiblings.parent.code} · ${previewSiblings.parent.name}`}
                    amount={balanceOf(previewSiblings.parent.id)}
                    onDrill={() =>
                      setDrill({
                        id: previewSiblings.parent!.id,
                        label: `${previewSiblings.parent!.code} · ${previewSiblings.parent!.name}`,
                        normal: normalBalanceOf(previewSiblings.parent!.id),
                      })
                    }
                  />
                ) : null}
                <p
                  className={cn(
                    "flex justify-between font-medium",
                    previewSiblings.parent && "pl-4",
                  )}
                >
                  <span>
                    {draft.code || "0000"} · {draft.name || "New account"}
                  </span>
                  <span className="num">{formatMoney(canSeedOpening ? openingAmount : 0)}</span>
                </p>
                {previewSiblings.rows.map((a) => (
                  <PreviewRow
                    key={a.id}
                    label={`${a.code} · ${a.name}`}
                    amount={balanceOf(a.id)}
                    indent={!!previewSiblings.parent}
                    onDrill={() =>
                      setDrill({
                        id: a.id,
                        label: `${a.code} · ${a.name}`,
                        normal: normalBalanceOf(a.id),
                      })
                    }
                  />
                ))}
                {previewSiblings.all.length === 0 && !previewSiblings.parent ? (
                  <p className="text-xs text-muted-foreground">
                    Pick an account type to preview where this account lands.
                  </p>
                ) : (
                  <p className="flex justify-between border-t border-border pt-2 text-sm font-semibold">
                    <span>Section total (from General Ledger)</span>
                    <span className="num">{formatMoney(sectionTotal)}</span>
                  </p>
                )}
              </div>
            </div>
          </form>

          <div className="flex items-center justify-end gap-2 border-t border-border bg-muted/40 px-6 py-4">
            <Button
              type="button"
              variant="outline"
              disabled={save.isPending}
              onClick={() => onOpenChange(false)}
            >
              Cancel
            </Button>
            <Button type="submit" form="new-account-form" disabled={save.isPending}>
              {save.isPending ? "Saving…" : "Save"}
            </Button>
          </div>
        </TooltipProvider>
      </DialogContent>

      <AccountLedgerDrilldown
        open={!!drill}
        onOpenChange={(v) => !v && setDrill(null)}
        accountId={drill?.id ?? null}
        label={drill?.label ?? ""}
        asOf={asOf}
        normalBalance={drill?.normal ?? "debit"}
      />
    </Dialog>
  );
}

/**
 * One statement-preview line. Existing accounts drill down into the exact
 * General Ledger lines that make up the shown balance.
 */
function PreviewRow({
  label,
  amount,
  indent = false,
  onDrill,
}: {
  label: string;
  amount: number;
  indent?: boolean;
  onDrill: () => void;
}) {
  return (
    <div className={cn("flex items-center justify-between gap-2", indent && "pl-4")}>
      <button
        type="button"
        onClick={onDrill}
        aria-label={`View General Ledger lines for ${label}`}
        className="truncate rounded text-left text-muted-foreground underline-offset-4 hover:text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
      >
        {label}
      </button>
      <span className="num text-muted-foreground">{formatMoney(amount)}</span>
    </div>
  );
}

/** Inline field-level error text wired to the input via aria-describedby. */
function FieldError({ id, message }: { id?: string | undefined; message?: string | undefined }) {
  if (!message) return null;
  return (
    <p id={id} role="alert" className="text-xs font-medium text-destructive">
      {message}
    </p>
  );
}
