"use client";

/**
 * Payroll runs (HR-05 – HR-08, HR-19; plan L): prepare a monthly, piece-worker or casual run for everyone, a
 * department or one employee; review the payslips (summary, the design's `PayslipGroups`, and each payslip with the
 * lines behind it); approve (never the preparer), reverse with a reason, record net pay as paid, and print payslips on
 * the design's A4 `Payslip`. SMS and email wait for plan M (Q2).
 */

import * as React from "react";
import { ArrowLeftIcon, CheckIcon, PrinterIcon, RefreshCwIcon, Undo2Icon, WalletIcon } from "lucide-react";

import { PanelError } from "@/components/medora/errors";
import { PayslipGroups } from "@/components/medora/hr-more";
import { Payslip } from "@/components/medora/payroll";
import { printElement } from "@/components/medora/print";
import { StatusTag } from "@/components/medora/record-lists";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Field, 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 { Table, TableBody, TableCell, TableFooter, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { toast } from "@/components/ui/toast";
import { PayrollApi, type PayrollOptions, type PayrollRunDetail, type PayrollRunRow, type PayslipRow } from "@/lib/api";

import { Section, apiError, human, kes, longMonth, useAction } from "./shared";
import { Loading } from "@/components/medora/loading";

const KINDS = ["Monthly", "Piece workers", "Casuals"] as const;
const PAY_TYPE: Record<(typeof KINDS)[number], string> = { Monthly: "Monthly", "Piece workers": "Piece rate", Casuals: "Casual (hourly)" };

const period = (r: PayrollRunRow) => (r.kind === "Monthly" ? longMonth(r.month) : `${human(r.from)} – ${human(r.to)}`);

/** `prepareRef`: R2 (card 037), the page header's "Prepare a run" opens the form through it. */
export function RunsView({ today, userId, may, prepareRef }: { today: string; userId: number | null; may: (a: string) => boolean; prepareRef?: React.RefObject<(() => void) | null> }) {
  const [runs, setRuns] = React.useState<PayrollRunRow[] | null>(null);
  const [failed, setFailed] = React.useState(false); // R12 (card 037): a failed load says so, with Try again
  const [opts, setOpts] = React.useState<PayrollOptions | null>(null);
  const [open, setOpen] = React.useState<number | null>(null);
  const [prep, setPrep] = React.useState<null | { kind: (typeof KINDS)[number]; month: string; from: string; to: string; department: string; employeeId: string }>(null);
  React.useEffect(() => {
    if (!prepareRef) return;
    prepareRef.current = () => setPrep({ kind: "Monthly", month: today.slice(0, 7), from: `${today.slice(0, 7)}-01`, to: today, department: "", employeeId: "" });
    return () => {
      prepareRef.current = null;
    };
  });

  const { busy, run } = useAction();

  const load = React.useCallback(() => {
    Promise.all([PayrollApi.runs(), PayrollApi.options()])
      .then(([r, o]) => {
        setRuns(r);
        setOpts(o);
        setFailed(false);
      })
      .catch((e) => (setRuns([]), setFailed(true), toast.add({ type: "error", title: "Couldn't load payroll", description: apiError(e, "Try again.") })));
  }, []);
  React.useEffect(() => load(), [load]);

  if (failed) return <PanelError framed what="payroll runs" onRetry={load} />;
  if (!runs || !opts)
    return (
      <Loading what="payroll runs" columns={6} rows={5} numeric={[3, 4]} />
    );
  if (open != null) return <RunDetail id={open} userId={userId} may={may} accounts={opts.accounts} today={today} onBack={() => (setOpen(null), load())} />;

  const departments = [...new Set(opts.employees.map((e) => e.department).filter(Boolean) as string[])].sort();
  const eligible = prep ? opts.employees.filter((e) => e.active && e.payType === PAY_TYPE[prep.kind] && (!prep.department || e.department === prep.department)) : [];

  const prepare = async () => {
    if (!prep) return;
    const body = prep.kind === "Monthly" ? { kind: prep.kind, month: prep.month } : { kind: prep.kind, from: prep.from, to: prep.to };
    const r = await run(() => PayrollApi.createRun({ ...body, department: prep.department || null, employeeIds: prep.employeeId ? [Number(prep.employeeId)] : [] }), (x) => `${x.number} prepared: ${x.slips} payslips`, "Payroll not prepared");
    if (r) {
      if (r.skipped?.length) toast.add({ type: "warning", title: `${r.skipped.length} left out`, description: r.skipped.join(" ") });
      setPrep(null);
      setOpen(r.id);
    }
  };

  return (
    <div className="space-y-6">
      <Section
        title="Payroll runs"
        description="Prepared as a draft, approved by someone else, then posted to the ledger."
      >
        {runs.length ? (
          <div className="overflow-x-auto rounded-md border border-[var(--color-border-panel)] bg-card">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>Run</TableHead>
                  <TableHead>Period</TableHead>
                  <TableHead className="text-right">Payslips</TableHead>
                  <TableHead className="text-right">Gross</TableHead>
                  <TableHead className="text-right">PAYE</TableHead>
                  <TableHead className="text-right">Net</TableHead>
                  <TableHead>Status</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {runs.map((r) => (
                  <TableRow key={r.id} className="cursor-pointer" onClick={() => setOpen(r.id)}>
                    <TableCell>
                      <button type="button" className="font-medium text-primary hover:underline" onClick={() => setOpen(r.id)}>
                        {r.number}
                      </button>
                      <span className="block text-xs text-muted-foreground">
                        {r.kind}
                        {r.department ? `, ${r.department}` : ""}
                      </span>
                    </TableCell>
                    <TableCell>{period(r)}</TableCell>
                    <TableCell className="text-right tabular-nums">{r.slips}</TableCell>
                    <TableCell className="text-right tabular-nums">{kes(r.gross)}</TableCell>
                    <TableCell className="text-right tabular-nums">{kes(r.paye)}</TableCell>
                    <TableCell className="text-right tabular-nums">{kes(r.net)}</TableCell>
                    <TableCell>
                      <StatusTag status={r.status} />
                      {r.paidAt ? <span className="block text-xs text-muted-foreground">Paid {human(r.paidDate)}</span> : null}
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          </div>
        ) : (
          <p className="text-sm text-muted-foreground">No payroll runs yet.</p>
        )}
      </Section>

      <Dialog open={!!prep} onOpenChange={(o) => (!o ? setPrep(null) : null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Prepare a payroll run</DialogTitle>
            <DialogDescription>Nothing is paid, posted or marked as used until the draft is approved.</DialogDescription>
          </DialogHeader>
          {prep ? (
            <div className="grid gap-3">
              <Field>
                <FieldLabel htmlFor="pr-kind">Who</FieldLabel>
                <NativeSelect id="pr-kind" value={prep.kind} onChange={(e) => setPrep({ ...prep, kind: e.target.value as (typeof KINDS)[number], employeeId: "" })}>
                  {KINDS.map((k) => (
                    <NativeSelectOption key={k} value={k}>
                      {k === "Monthly" ? "Monthly staff" : k === "Casuals" ? "Casuals, from their hours" : "Piece workers, from their pieces"}
                    </NativeSelectOption>
                  ))}
                </NativeSelect>
              </Field>
              {prep.kind === "Monthly" ? (
                <Field>
                  <FieldLabel htmlFor="pr-month">Month</FieldLabel>
                  <Input id="pr-month" type="month" max={today.slice(0, 7)} value={prep.month} onChange={(e) => setPrep({ ...prep, month: e.target.value })} />
                </Field>
              ) : (
                <div className="grid grid-cols-2 gap-2">
                  <Field>
                    <FieldLabel htmlFor="pr-from">From</FieldLabel>
                    <Input id="pr-from" type="date" max={today} value={prep.from} onChange={(e) => setPrep({ ...prep, from: e.target.value })} />
                  </Field>
                  <Field>
                    <FieldLabel htmlFor="pr-to">To</FieldLabel>
                    <Input id="pr-to" type="date" max={today} value={prep.to} onChange={(e) => setPrep({ ...prep, to: e.target.value })} />
                  </Field>
                </div>
              )}
              <Field>
                <FieldLabel htmlFor="pr-dept">Department</FieldLabel>
                <NativeSelect id="pr-dept" value={prep.department} onChange={(e) => setPrep({ ...prep, department: e.target.value, employeeId: "" })}>
                  <NativeSelectOption value="">Every department</NativeSelectOption>
                  {departments.map((d) => (
                    <NativeSelectOption key={d} value={d}>
                      {d}
                    </NativeSelectOption>
                  ))}
                </NativeSelect>
              </Field>
              <Field>
                <FieldLabel htmlFor="pr-emp">Employee</FieldLabel>
                <NativeSelect id="pr-emp" value={prep.employeeId} onChange={(e) => setPrep({ ...prep, employeeId: e.target.value })}>
                  <NativeSelectOption value="">Everyone listed ({eligible.length})</NativeSelectOption>
                  {eligible.map((e) => (
                    <NativeSelectOption key={e.id} value={String(e.id)}>
                      {e.name}
                    </NativeSelectOption>
                  ))}
                </NativeSelect>
              </Field>
              {!eligible.length ? <p className="text-xs text-status-error-text sm:col-span-2">No active employees are paid {PAY_TYPE[prep.kind].toLowerCase()}{prep.department ? ` in ${prep.department}` : ""}.</p> : null}
            </div>
          ) : null}
          <DialogFooter>
            <Button variant="outline" onClick={() => setPrep(null)}>
              Cancel
            </Button>
            <Button disabled={busy || !eligible.length} onClick={() => void prepare()}>
              {busy ? <Spinner /> : null}
              Prepare draft
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}

function RunDetail({ id, userId, may, accounts, today, onBack }: { id: number; userId: number | null; may: (a: string) => boolean; accounts: PayrollOptions["accounts"]; today: string; onBack: () => void }) {
  const [r, setR] = React.useState<PayrollRunDetail | null>(null);
  const [view, setView] = React.useState<"slips" | "groups">("slips");
  const [slip, setSlip] = React.useState<PayslipRow | null>(null);
  const [reversing, setReversing] = React.useState(false);
  const [why, setWhy] = React.useState("");
  const [paying, setPaying] = React.useState<null | { accountId: string; date: string; reference: string }>(null);
  const print = React.useRef<HTMLDivElement>(null);
  const [printing, setPrinting] = React.useState<PayslipRow[] | null>(null);
  const { busy, run } = useAction();

  const load = React.useCallback(() => {
    PayrollApi.run(id)
      .then(setR)
      .catch((e) => toast.add({ type: "error", title: "Couldn't load the run", description: apiError(e, "Try again.") }));
  }, [id]);
  React.useEffect(() => load(), [load]);
  React.useEffect(() => {
    if (printing && print.current) {
      printElement(print.current, "a4");
      setPrinting(null);
    }
  }, [printing]);

  if (!r)
    return (
      <Loading what="the run" shape="stats" rows={4} />
    );

  const act = async (p: () => Promise<PayrollRunDetail>, ok: string, fail: string) => {
    const x = await run(p, ok, fail);
    if (x) setR(x);
    return x;
  };
  const own = r.preparedById != null && r.preparedById === userId;
  const label = period(r);
  const asSlip = (s: PayslipRow) => (
    <Payslip
      key={s.id}
      employer={{ name: r.employer.name, kraPin: r.employer.pin || "–", address: "" }}
      employee={{ id: String(s.employeeId), name: s.employeeName, number: s.employeeNumber, role: s.designation ?? s.department ?? "", kraPin: s.kraPin ?? "–", nssfNo: s.nssfNo ?? "–", shaNo: s.shaNo ?? "–", bank: s.payTo ?? "–" }}
      period={label}
      payDate={r.paidDate ? human(r.paidDate) : r.status === "Approved" ? "on approval" : "not yet (draft)"}
      relief={s.relief}
      ratesAsAt={r.rates ? human(r.rates.from) : "–"}
      pay={{
        basic: s.basic, allowances: s.allowances + s.untaxedAllowances, overtime: s.overtime, commission: s.commission, bonus: s.bonus, otherDeductions: s.otherDeductions,
        loans: s.loans, loanRefs: s.items.filter((i) => i.source === "Loan" || i.source === "HELB").map((i) => i.label.split(" ").pop() ?? ""),
        advance: s.advance, advanceRefs: s.items.filter((i) => i.source === "Advance").map((i) => i.label.split(" ").pop() ?? ""),
        gross: s.gross, nssf: s.nssf, shif: s.shif, housingLevy: s.levy, taxable: s.taxable, payeBeforeRelief: s.payeBeforeRelief, paye: s.paye, totalDeductions: s.totalDeductions, net: s.net,
      }}
    />
  );

  return (
    <div className="space-y-5">
      <div className="flex flex-wrap items-start justify-between gap-3">
        <div>
          <Button size="sm" variant="ghost" onClick={onBack}>
            <ArrowLeftIcon data-icon="inline-start" />
            All runs
          </Button>
          <h2 className="mt-1 text-lg font-semibold">
            {r.number}: {r.kind}, {label}
            {r.department ? `, ${r.department}` : ""}
          </h2>
          <p className="text-sm text-muted-foreground">
            <StatusTag status={r.status} /> Prepared by {r.preparedBy ?? "–"}
            {r.approvedBy ? `, approved by ${r.approvedBy}` : ""}
            {r.paidAt ? `, paid ${human(r.paidDate)} (${r.paidReference})` : ""}
            {r.reverseReason ? `. Reversed: ${r.reverseReason}` : ""}. Rates from {r.rates ? human(r.rates.from) : "–"}.
          </p>
        </div>
        <div className="flex flex-wrap gap-2">
          <Button size="sm" variant="outline" disabled={!r.payslips.length} onClick={() => setPrinting(r.payslips)}>
            <PrinterIcon data-icon="inline-start" />
            Print all payslips
          </Button>
          {r.status === "Draft" && may("Create") ? (
            <Button size="sm" variant="outline" disabled={busy} onClick={() => void act(() => PayrollApi.recalculate(r.id), "Recalculated from today's records", "Not recalculated")}>
              <RefreshCwIcon data-icon="inline-start" />
              Recalculate
            </Button>
          ) : null}
          {r.status === "Draft" && (may("Delete") || may("Create")) ? (
            <Button size="sm" variant="ghost" disabled={busy} onClick={() => void run(() => PayrollApi.discard(r.id), `${r.number} discarded`).then((x) => (x ? onBack() : null))}>
              Discard draft
            </Button>
          ) : null}
          {r.status === "Draft" && may("Approve") ? (
            <Button size="sm" disabled={busy || own} title={own ? "Someone other than whoever prepared the run approves it." : undefined} onClick={() => void act(() => PayrollApi.approve(r.id), `${r.number} approved and posted`, "Not approved")}>
              <CheckIcon data-icon="inline-start" />
              Approve
            </Button>
          ) : null}
          {r.status === "Approved" && !r.paidAt && may("Approve") ? (
            <Button size="sm" disabled={busy} onClick={() => setPaying({ accountId: "", date: today, reference: "" })}>
              <WalletIcon data-icon="inline-start" />
              Record as paid…
            </Button>
          ) : null}
          {r.status === "Approved" && !r.paidAt && may("Delete") ? (
            <Button size="sm" variant="outline" disabled={busy} onClick={() => (setReversing(true), setWhy(""))}>
              <Undo2Icon data-icon="inline-start" />
              Reverse…
            </Button>
          ) : null}
        </div>
      </div>
      {r.status === "Draft" && own ? <p className="text-xs text-muted-foreground">You prepared this run, so someone else approves it.</p> : null}

      <div className="grid gap-3 sm:grid-cols-4">
        {(
          [
            ["Gross pay", r.totals.gross],
            ["PAYE", r.totals.paye],
            ["NSSF, SHIF and levy", (r.totals.nssf ?? 0) + (r.totals.shif ?? 0) + (r.totals.levy ?? 0)],
            ["Net pay", r.totals.net],
          ] as const
        ).map(([k, v]) => (
          <div key={k} className="rounded-lg border border-[var(--color-border-panel)] p-3 bg-card">
            <p className="text-xs text-muted-foreground">{k}</p>
            <p className="text-lg font-semibold tabular-nums">{kes(v ?? 0)}</p>
          </div>
        ))}
      </div>
      <p className="text-xs text-muted-foreground">
        Employer&apos;s NSSF {kes(r.totals.nssfEmployer ?? 0)} and housing levy {kes(r.totals.levyEmployer ?? 0)} are on top of gross pay.
      </p>

      <div className="flex gap-2">
        <Button size="sm" variant={view === "slips" ? "secondary" : "ghost"} onClick={() => setView("slips")}>
          Payslips
        </Button>
        <Button size="sm" variant={view === "groups" ? "secondary" : "ghost"} onClick={() => setView("groups")}>
          By department
        </Button>
      </div>

      {view === "groups" ? (
        <PayslipGroups rows={r.payslips.map((s) => ({ name: s.employeeName, department: s.department ?? "No department", kind: r.kind, gross: s.gross, net: s.net, paye: s.paye }))} />
      ) : (
        <div className="overflow-x-auto rounded-md border border-[var(--color-border-panel)] bg-card">
          <Table>
            <TableHeader>
              <TableRow>
                <TableHead>Employee</TableHead>
                <TableHead className="text-right">Gross</TableHead>
                <TableHead className="text-right">NSSF</TableHead>
                <TableHead className="text-right">SHIF</TableHead>
                <TableHead className="text-right">Levy</TableHead>
                <TableHead className="text-right">PAYE</TableHead>
                <TableHead className="text-right">Other</TableHead>
                <TableHead className="text-right">Net</TableHead>
              </TableRow>
            </TableHeader>
            <TableBody>
              {r.payslips.map((s) => (
                <TableRow key={s.id}>
                  <TableCell>
                    <button type="button" className="font-medium text-primary hover:underline" onClick={() => setSlip(s)}>
                      {s.employeeName}
                    </button>
                    <span className="block text-xs text-muted-foreground">{s.employeeNumber}</span>
                    {s.warnings.map((w) => (
                      <span key={w} className="block text-xs text-status-warning-text">
                        {w}
                      </span>
                    ))}
                  </TableCell>
                  <TableCell className="text-right tabular-nums">{kes(s.gross)}</TableCell>
                  <TableCell className="text-right tabular-nums">{kes(s.nssf)}</TableCell>
                  <TableCell className="text-right tabular-nums">{kes(s.shif)}</TableCell>
                  <TableCell className="text-right tabular-nums">{kes(s.levy)}</TableCell>
                  <TableCell className="text-right tabular-nums">{kes(s.paye)}</TableCell>
                  <TableCell className="text-right tabular-nums">{kes(s.otherDeductions + s.loans + s.advance)}</TableCell>
                  <TableCell className="text-right font-medium tabular-nums">{kes(s.net)}</TableCell>
                </TableRow>
              ))}
            </TableBody>
            <TableFooter>
              <TableRow>
                <TableCell>Total</TableCell>
                {(["gross", "nssf", "shif", "levy", "paye"] as const).map((k) => (
                  <TableCell key={k} className="text-right tabular-nums">
                    {kes(r.totals[k] ?? 0)}
                  </TableCell>
                ))}
                <TableCell className="text-right tabular-nums">{kes((r.totals.otherDeductions ?? 0) + (r.totals.loans ?? 0) + (r.totals.advance ?? 0))}</TableCell>
                <TableCell className="text-right tabular-nums">{kes(r.totals.net ?? 0)}</TableCell>
              </TableRow>
            </TableFooter>
          </Table>
        </div>
      )}

      <Dialog open={!!slip} onOpenChange={(o) => (!o ? setSlip(null) : null)}>
        <DialogContent className="sm:max-w-3xl">
          <DialogHeader>
            <DialogTitle>
              {slip?.employeeName}, {label}
            </DialogTitle>
            <DialogDescription>Each line and where it came from.</DialogDescription>
          </DialogHeader>
          {slip ? (
            <div className="max-h-[65vh] space-y-4 overflow-y-auto">
              <Table>
                <TableBody>
                  {slip.items.map((i) => (
                    <TableRow key={i.id}>
                      <TableCell className="text-xs text-muted-foreground">{i.source}</TableCell>
                      <TableCell>{i.label}</TableCell>
                      <TableCell className="text-right tabular-nums">{["Deduction", "Loan", "HELB", "Advance"].includes(i.source) ? `−${kes(i.amount)}` : kes(i.amount)}</TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
              <div className="overflow-x-auto">{asSlip(slip)}</div>
            </div>
          ) : null}
          <DialogFooter>
            <Button variant="outline" onClick={() => slip && setPrinting([slip])}>
              <PrinterIcon data-icon="inline-start" />
              Print payslip
            </Button>
            <Button onClick={() => setSlip(null)}>Close</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog open={reversing} onOpenChange={setReversing}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Reverse {r.number}</DialogTitle>
            <DialogDescription>Its ledger posting is reversed and its overtime, bonuses and advances go back to waiting, so the period can be run again.</DialogDescription>
          </DialogHeader>
          <Field>
            <FieldLabel htmlFor="pr-why">Why</FieldLabel>
            <Input id="pr-why" value={why} onChange={(e) => setWhy(e.target.value)} />
          </Field>
          <DialogFooter>
            <Button variant="outline" onClick={() => setReversing(false)}>
              Cancel
            </Button>
            <Button disabled={busy || why.trim().length < 3} onClick={() => void act(() => PayrollApi.reverse(r.id, why.trim()), `${r.number} reversed`, "Not reversed").then((x) => (x ? setReversing(false) : null))}>
              Reverse run
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog open={!!paying} onOpenChange={(o) => (!o ? setPaying(null) : null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Record net pay as paid</DialogTitle>
            <DialogDescription>{kes(r.totals.net ?? 0)} out of the account it was paid from, clearing Net salaries payable.</DialogDescription>
          </DialogHeader>
          {paying ? (
            <div className="grid gap-3">
              <Field className="sm:col-span-2">
                <FieldLabel htmlFor="pp-acct">Paid from</FieldLabel>
                <NativeSelect id="pp-acct" value={paying.accountId} onChange={(e) => setPaying({ ...paying, accountId: e.target.value })}>
                  <NativeSelectOption value="">Choose the account</NativeSelectOption>
                  {accounts.map((a) => (
                    <NativeSelectOption key={a.id} value={String(a.id)}>
                      {a.code} {a.name}
                    </NativeSelectOption>
                  ))}
                </NativeSelect>
              </Field>
              <Field>
                <FieldLabel htmlFor="pp-date">Date</FieldLabel>
                <Input id="pp-date" type="date" max={today} value={paying.date} onChange={(e) => setPaying({ ...paying, date: e.target.value })} />
              </Field>
              <Field>
                <FieldLabel htmlFor="pp-ref">Bank or M-Pesa reference</FieldLabel>
                <Input id="pp-ref" value={paying.reference} onChange={(e) => setPaying({ ...paying, reference: e.target.value })} />
              </Field>
            </div>
          ) : null}
          <DialogFooter>
            <Button variant="outline" onClick={() => setPaying(null)}>
              Cancel
            </Button>
            <Button
              disabled={busy || !paying?.accountId || !paying.reference.trim()}
              onClick={() => void act(() => PayrollApi.paid(r.id, { accountId: Number(paying!.accountId), date: paying!.date, reference: paying!.reference.trim() }), "Net pay recorded as paid", "Not recorded").then((x) => (x ? setPaying(null) : null))}
            >
              Record payment
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <div className="hidden">
        <div ref={print}>{printing?.map((s) => <div key={s.id} style={{ breakAfter: "page" }}>{asSlip(s)}</div>)}</div>
      </div>
    </div>
  );
}
