"use client";

import * as React from "react";
import { BanIcon, FileTextIcon, PencilIcon, PlusIcon } from "lucide-react";

import { Example } from "@/components/design-system/example";
import { Section } from "@/components/design-system/section";
import { RecordDrawer } from "@/components/medora/drawer";
import { ACCOUNT_TYPES, AccountStatement, accountProblems, BalanceSheet, ChartOfAccounts, OpeningBalances, ProfitAndLoss, TrialBalance, type AccountType, type LedgerAccount, type OpeningLine, type Posting } from "@/components/medora/ledger";
import { downloadText, FileUpload, reviewCsv } from "@/components/medora/file-upload";
import type { RowAction } from "@/components/medora/row-actions";
import { Button } from "@/components/ui/button";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { Spinner } from "@/components/ui/spinner";
import { toast } from "@/components/ui/toast";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { branches, formatDate } from "@/lib/mock-data";

const A = (code: string, name: string, type: AccountType, balance: number, extra: Partial<LedgerAccount> = {}): LedgerAccount => ({ code, name, type, balance, active: true, hasPostings: balance !== 0, ...extra });

/** A balanced mock ledger (debits positive). Retained earnings is the balancing figure. */
const SEED: LedgerAccount[] = [
  A("1000", "Cash and bank", "Asset", 0, { system: true, description: "Branch money accounts and bank accounts." }),
  A("1010", "Cash drawer", "Asset", 18450, { parent: "1000", branch: "Westlands Main", system: true }),
  A("1011", "Cash drawer", "Asset", -1200, { parent: "1000", branch: "Kilimani", system: true, description: "Credit balance: a drop was recorded twice on 18 Oct." }),
  A("1020", "M-Pesa till 247247", "Asset", 64300, { parent: "1000", branch: "Westlands Main", system: true }),
  A("1021", "M-Pesa till 512330", "Asset", 21750, { parent: "1000", branch: "Kilimani", system: true }),
  A("1030", "Card terminal", "Asset", 12800, { parent: "1000", branch: "Westlands Main", system: true }),
  A("1040", "Equity Bank", "Asset", 412600, { parent: "1000" }),
  A("1041", "KCB payroll account", "Asset", 85000, { parent: "1000" }),
  A("1100", "Accounts receivable", "Asset", 96420, { system: true }),
  A("1200", "Inventory", "Asset", 684210, { system: true, description: "Valued from batches." }),
  A("1300", "Staff advances", "Asset", 5000, { system: true }),
  A("1500", "Fixtures and equipment", "Asset", 350000),
  A("2000", "Accounts payable", "Liability", -146270, { system: true }),
  A("2100", "VAT payable", "Liability", -31560, { system: true }),
  A("2200", "Payroll liabilities", "Liability", 0, { system: true }),
  A("2210", "PAYE payable", "Liability", -62850, { parent: "2200", system: true }),
  A("2220", "NSSF payable", "Liability", -9180, { parent: "2200", system: true }),
  A("2230", "SHIF payable", "Liability", -9340, { parent: "2200", system: true }),
  A("2240", "Housing levy payable", "Liability", -5090, { parent: "2200", system: true }),
  A("3000", "Owner's equity", "Equity", -1000000),
  A("3100", "Retained earnings", "Equity", -572090, { system: true }),
  A("4000", "Sales", "Income", -1284600, { system: true }),
  A("4100", "Delivery income", "Income", -18400, { system: true }),
  A("5000", "Cost of goods sold", "Expense", 842300, { system: true }),
  A("5100", "Stock write-offs", "Expense", 10200, { system: true }),
  A("6000", "Rent", "Expense", 180000),
  A("6100", "Bank charges", "Expense", 3250),
  A("6200", "Salaries and wages", "Expense", 339500, { system: true }),
  A("6300", "Electricity", "Expense", 14800),
  A("6400", "Courier", "Expense", 0, { active: false, hasPostings: true, description: "Replaced by Delivery costs in 2025." }),
];

const STATEMENT: Posting[] = [
  { id: "p1", date: "2026-10-02", ref: "DRP-2026-0141", details: "Cash drop from Westlands drawer", debit: 120000, credit: 0 },
  { id: "p2", date: "2026-10-05", ref: "PAY-0101", details: "Payment to Dawa Distributors Ltd", debit: 0, credit: 84300 },
  { id: "p3", date: "2026-10-09", ref: "EXP-2026-0230", details: "October rent, Sarit Centre", debit: 0, credit: 180000 },
  { id: "p4", date: "2026-10-14", ref: "RCP-2026-0077", details: "Aga Khan Hospital, invoice INV-2026-00741", debit: 52150, credit: 0 },
  { id: "p5", date: "2026-10-18", ref: "JV-2026-0411", details: "Bank charges, October statement", debit: 0, credit: 1250 },
];
const STATEMENT_OPENING = 506000;

type Draft = { code: string; name: string; type: AccountType; parent: string; branch: string; description: string };

function AccountDrawer({ open, onClose, accounts, initial, editing, onSave }: { open: boolean; onClose: () => void; accounts: LedgerAccount[]; initial: Draft; editing?: LedgerAccount; onSave: (d: Draft) => void }) {
  const [d, setD] = React.useState(initial);
  const [tried, setTried] = React.useState(false);
  const [saving, setSaving] = React.useState(false);
  const problems = accountProblems({ ...d, parent: d.parent || undefined, branch: d.branch || undefined }, accounts, editing?.code);
  const parents = accounts.filter((a) => a.type === d.type && !a.parent && a.code !== editing?.code);
  const dirty = JSON.stringify(d) !== JSON.stringify(initial);
  const err = (k: "code" | "name" | "parent") => (tried ? problems[k] : undefined);
  const range = ACCOUNT_TYPES.find((t) => t.type === d.type)!.range;
  return (
    <RecordDrawer
      open={open}
      onOpenChange={(o) => !o && onClose()}
      variant="edit"
      dirty={dirty}
      saving={saving}
      title={editing ? `Edit ${editing.code} ${editing.name}` : "New account"}
      footer={
        <>
          <Button variant="outline" onClick={onClose} disabled={saving}>
            Cancel
          </Button>
          <Button
            disabled={saving}
            onClick={() => {
              setTried(true);
              if (Object.keys(problems).length) return;
              setSaving(true);
              setTimeout(() => {
                setSaving(false);
                onSave(d);
              }, 700);
            }}
          >
            {saving ? <Spinner /> : null}
            {saving ? "Saving…" : "Save account"}
          </Button>
        </>
      }
    >
      <div className="space-y-4">
        {editing?.system ? <p className="rounded-md border border-[var(--color-border-panel)] px-3 py-2 text-sm text-muted-foreground">Used by automatic postings: the number and type can&apos;t change. You can rename it and edit the description.</p> : null}
        <div className="grid gap-4 sm:grid-cols-2">
          <Field>
            <FieldLabel htmlFor="acc-type">Type</FieldLabel>
            <NativeSelect id="acc-type" value={d.type} disabled={!!editing} onChange={(e) => setD({ ...d, type: e.target.value as AccountType, parent: "" })}>
              {ACCOUNT_TYPES.map((t) => (
                <NativeSelectOption key={t.type} value={t.type}>
                  {t.type}
                </NativeSelectOption>
              ))}
            </NativeSelect>
          </Field>
          <Field data-invalid={!!err("parent")}>
            <FieldLabel htmlFor="acc-parent">Sub-account of (optional)</FieldLabel>
            <NativeSelect id="acc-parent" value={d.parent} disabled={!!editing?.system} onChange={(e) => setD({ ...d, parent: e.target.value })}>
              <NativeSelectOption value="">None, a main account</NativeSelectOption>
              {parents.map((p) => (
                <NativeSelectOption key={p.code} value={p.code}>
                  {p.code} {p.name}
                </NativeSelectOption>
              ))}
            </NativeSelect>
            {err("parent") ? <FieldError>{err("parent")}</FieldError> : null}
          </Field>
          <Field data-invalid={!!err("code")}>
            <FieldLabel htmlFor="acc-code">Account number</FieldLabel>
            <Input id="acc-code" inputMode="numeric" className="font-mono" value={d.code} disabled={!!editing?.system} onChange={(e) => setD({ ...d, code: e.target.value.replace(/\D/g, "").slice(0, 4) })} aria-invalid={!!err("code")} />
            {err("code") ? (
              <FieldError>{err("code")}</FieldError>
            ) : (
              <FieldDescription>
                {d.parent ? `Under ${d.parent}: ${d.parent.slice(0, 2)}xx` : `${d.type} accounts: ${range[0]}–${range[1]}`}
              </FieldDescription>
            )}
          </Field>
          <Field data-invalid={!!err("name")}>
            <FieldLabel htmlFor="acc-name">Name</FieldLabel>
            <Input id="acc-name" value={d.name} onChange={(e) => setD({ ...d, name: e.target.value })} aria-invalid={!!err("name")} />
            {err("name") ? <FieldError>{err("name")}</FieldError> : null}
          </Field>
          <Field>
            <FieldLabel htmlFor="acc-branch">Branch (money accounts only)</FieldLabel>
            <NativeSelect id="acc-branch" value={d.branch} onChange={(e) => setD({ ...d, branch: e.target.value })}>
              <NativeSelectOption value="">All branches</NativeSelectOption>
              {branches.map((b) => (
                <NativeSelectOption key={b.id} value={b.name}>
                  {b.name}
                </NativeSelectOption>
              ))}
            </NativeSelect>
          </Field>
          <Field>
            <FieldLabel htmlFor="acc-desc">Description (optional)</FieldLabel>
            <Input id="acc-desc" value={d.description} onChange={(e) => setD({ ...d, description: e.target.value })} />
          </Field>
        </div>
      </div>
    </RecordDrawer>
  );
}

type LedgerView = "chart" | "trial" | "pl" | "bs" | "opening";

const YEARS = ["2025", "2024", "2023"];
/** Three full years; each year's net profit is the change in retained earnings on the balance sheet. */
const PL = {
  income: [
    { code: "4000", name: "Sales", amounts: [19200000, 17500000, 14800000] },
    { code: "4100", name: "Delivery income", amounts: [240000, 180000, 90000] },
  ],
  costOfSales: [
    { code: "5000", name: "Cost of goods sold", amounts: [13100000, 12020000, 10300000] },
    { code: "5100", name: "Stock write-offs", amounts: [160000, 140000, 120000] },
  ],
  expenses: [
    { code: "6000", name: "Rent", amounts: [2160000, 2040000, 1920000] },
    { code: "6200", name: "Salaries and wages", amounts: [3649000, 3050000, 2480000] },
    { code: "6300", name: "Electricity", amounts: [175000, 150000, 125000] },
    { code: "6100", name: "Bank charges", amounts: [38000, 30000, 25000] },
  ],
};
const BS = {
  assets: [
    { code: "1000", name: "Cash and bank", amounts: [540000, 420000, 300000] },
    { code: "1100", name: "Accounts receivable", amounts: [88000, 70000, 52000] },
    { code: "1200", name: "Inventory", amounts: [650000, 590000, 510000] },
    { code: "1500", name: "Fixtures and equipment, net", amounts: [300000, 320000, 260000] },
  ],
  liabilities: [
    { code: "2000", name: "Accounts payable", amounts: [140000, 130000, 118000] },
    { code: "2100", name: "VAT payable", amounts: [30000, 26000, 22000] },
    { code: "2200", name: "Payroll liabilities", amounts: [80000, 74000, 62000] },
  ],
  equity: [
    { code: "3000", name: "Owner's equity", amounts: [1000000, 1000000, 1000000] },
    { code: "3100", name: "Retained earnings", amounts: [328000, 170000, -80000] },
  ],
};

function OpeningDemo({ accounts }: { accounts: LedgerAccount[] }) {
  const [lines, setLines] = React.useState<Record<string, OpeningLine>>({
    "1040": { code: "1040", debit: "380,000", credit: "" },
    "1200": { code: "1200", debit: "590,000", credit: "" },
    "2000": { code: "2000", debit: "", credit: "130,000" },
    "3000": { code: "3000", debit: "", credit: "1,000,000" },
  });
  const [posted, setPosted] = React.useState(false);
  const num = (s: string) => Number(s.replace(/,/g, "")) || 0;
  const dr = Object.values(lines).reduce((s, l) => s + num(l.debit), 0);
  const cr = Object.values(lines).reduce((s, l) => s + num(l.credit), 0);
  const balanced = dr > 0 && Math.round((dr - cr) * 100) === 0 && !Object.values(lines).some((l) => num(l.debit) && num(l.credit));
  if (posted) return <p className="rounded-md border border-status-success-border bg-status-success-bg px-3 py-2 text-sm text-status-success-text">Opening journal JV-2026-0001 posted at 1 Jan 2026, KES {dr.toLocaleString("en-US", { minimumFractionDigits: 2 })} each side. Change it later with a correcting journal.</p>;
  return (
    <div className="space-y-4">
      <FileUpload
        label="Import from the old trial balance (CSV)"
        hint="Columns: Code, Debit, Credit. Up to 2 MB."
        kind="csv"
        accept={[".csv"]}
        maxBytes={2 * 1024 * 1024}
        onTemplate={() => downloadText("opening-balances-template.csv", "Code,Debit,Credit\n1040,380000,\n2000,,130000\n")}
        upload={async (file, progress) => {
          progress(50);
          const r = reviewCsv(await file.text(), [
            { key: "code", header: "Code", required: true, validate: (v) => (accounts.some((a) => a.code === v) ? undefined : `No account ${v}.`) },
            { key: "debit", header: "Debit", validate: (v) => (/^[\d,]+(\.\d{1,2})?$/.test(v) ? undefined : "Enter an amount.") },
            { key: "credit", header: "Credit", validate: (v) => (/^[\d,]+(\.\d{1,2})?$/.test(v) ? undefined : "Enter an amount.") },
          ]);
          progress(100);
          setLines((ls) => ({ ...ls, ...Object.fromEntries(r.valid.map((v) => [v.code, { code: v.code, debit: v.debit, credit: v.credit }])) }));
          return `${r.valid.length} accounts filled${r.problems.length ? `, ${r.problems.length} rows skipped` : ""}`;
        }}
      />
      <OpeningBalances accounts={accounts} lines={lines} onChange={(code, l) => setLines((ls) => ({ ...ls, [code]: l }))} />
      <div className="flex justify-end">
        <Button disabled={!balanced} onClick={() => setPosted(true)}>
          Post opening balances
        </Button>
      </div>
    </div>
  );
}

export function LedgerSection() {
  const [accounts, setAccounts] = React.useState(SEED);
  const [view, setView] = React.useState<LedgerView>("chart");
  const [drawer, setDrawer] = React.useState<{ initial: Draft; editing?: LedgerAccount; key: number } | null>(null);
  const [statementFor, setStatementFor] = React.useState<string>("1040");
  const statementRef = React.useRef<HTMLDivElement>(null);

  const blank = (over: Partial<Draft> = {}): Draft => ({ code: "", name: "", type: "Expense", parent: "", branch: "", description: "", ...over });
  const actionsFor = (a: LedgerAccount): RowAction[] => [
    {
      label: "View statement",
      icon: FileTextIcon,
      onSelect: () => {
        setStatementFor(a.code);
        setTimeout(() => statementRef.current?.scrollIntoView({ behavior: "smooth", block: "start" }), 0);
      },
    },
    ...(!a.parent ? [{ label: "Add sub-account", icon: PlusIcon, onSelect: () => setDrawer({ initial: blank({ type: a.type, parent: a.code }), key: Date.now() }) }] : []),
    { label: "Edit", icon: PencilIcon, onSelect: () => setDrawer({ initial: { code: a.code, name: a.name, type: a.type, parent: a.parent ?? "", branch: a.branch ?? "", description: a.description ?? "" }, editing: a, key: Date.now() }) },
    ...(a.system || !a.active
      ? []
      : [
          {
            label: "Deactivate",
            icon: BanIcon,
            destructive: true,
            onSelect: () => {
              if (a.balance !== 0) {
                toast.add({ type: "error", title: `${a.code} still has a balance`, description: "Move the balance with a journal first; an account with a balance can't be deactivated." });
                return;
              }
              setAccounts((xs) => xs.map((x) => (x.code === a.code ? { ...x, active: false } : x)));
              toast.add({ title: `${a.code} ${a.name} deactivated`, description: "Its history stays; it can't be used for new postings." });
            },
          },
        ]),
  ];

  const stAcc = accounts.find((a) => a.code === statementFor)!;

  return (
    <Section
      id="ledger"
      title="Chart of accounts and ledger"
      description="The General Ledger's accounts by type, each branch's cash drawer, M-Pesa till and card terminal as sub-accounts. Balances show as Dr or Cr, and a balance on the unusual side is flagged. System accounts are used by automatic postings: they can be renamed but not deactivated. Accounts with postings are deactivated, never deleted. Every posting balances, so the trial balance always agrees."
    >
      <Example title="Ledger" description="Kilimani's cash drawer is in credit: an unusual balance. Try Add sub-account under Cash and bank with number 1990, and Deactivate on Electricity" bare>
        <div className="flex flex-wrap items-center gap-3 border-b border-border px-4 py-3">
          <ToggleGroup variant="outline" size="sm" spacing={0} aria-label="View" value={[view]} onValueChange={(v: string[]) => v[0] && setView(v[0] as LedgerView)} className="flex-wrap">
            <ToggleGroupItem value="chart">Chart of accounts</ToggleGroupItem>
            <ToggleGroupItem value="trial">Trial balance</ToggleGroupItem>
            <ToggleGroupItem value="pl">Profit &amp; loss</ToggleGroupItem>
            <ToggleGroupItem value="bs">Balance sheet</ToggleGroupItem>
            <ToggleGroupItem value="opening">Opening balances</ToggleGroupItem>
          </ToggleGroup>
          {view === "chart" ? (
            <Button size="sm" className="ml-auto" onClick={() => setDrawer({ initial: blank(), key: Date.now() })}>
              <PlusIcon data-icon="inline-start" />
              New account
            </Button>
          ) : null}
        </div>
        <div className="p-4">
          {view === "chart" ? (
            <ChartOfAccounts accounts={accounts} actionsFor={actionsFor} asAt="20 Oct 2026" />
          ) : view === "trial" ? (
            <TrialBalance accounts={accounts} asAt="20 Oct 2026" />
          ) : view === "pl" ? (
            <ProfitAndLoss periods={YEARS} {...PL} />
          ) : view === "bs" ? (
            <BalanceSheet periods={YEARS.map((y) => `31 Dec ${y}`)} {...BS} />
          ) : (
            <OpeningDemo accounts={accounts} />
          )}
        </div>
      </Example>
      <div ref={statementRef} className="scroll-mt-20">
        <Example title={`Statement: ${stAcc.code} ${stAcc.name}${stAcc.branch ? `, ${stAcc.branch}` : ""}`} description="1–20 Oct 2026. Each line names the document behind it" bare>
          <div className="p-4">
            {stAcc.code === "1040" ? (
              <AccountStatement account={stAcc} opening={STATEMENT_OPENING} postings={STATEMENT} formatDate={(d) => formatDate(d)} from="1 Oct 2026" to="20 Oct 2026" />
            ) : (
              <AccountStatement account={stAcc} opening={stAcc.balance} postings={[]} formatDate={(d) => formatDate(d)} from="1 Oct 2026" to="20 Oct 2026" />
            )}
          </div>
        </Example>
      </div>
      {drawer ? (
        <AccountDrawer
          key={drawer.key}
          open
          onClose={() => setDrawer(null)}
          accounts={accounts}
          initial={drawer.initial}
          editing={drawer.editing}
          onSave={(d) => {
            const fields = { code: d.code, name: d.name.trim(), type: d.type, parent: d.parent || undefined, branch: d.branch || undefined, description: d.description.trim() || undefined };
            if (drawer.editing) setAccounts((xs) => xs.map((x) => (x.code === drawer.editing!.code ? { ...x, ...fields } : x)));
            else setAccounts((xs) => [...xs, { ...fields, balance: 0, active: true }]);
            toast.add({ title: drawer.editing ? `${d.code} saved` : `${d.code} ${d.name} added` });
            setDrawer(null);
          }}
        />
      ) : null}
    </Section>
  );
}
