"use client";

/**
 * Statutory rates (HR-15; plan L Q1): versions entered by the pharmacy with the date they apply from, shown on the
 * design's `RateVersions`. Nothing is built in: a version is used only after someone confirms it, saying who checked
 * it and against what, and a confirmed version is locked. Also the employer's KRA PIN for payslips and P9s.
 */

import * as React from "react";
import { CheckIcon, PlusIcon, Trash2Icon } from "lucide-react";

import { RateVersions } from "@/components/medora/hr-ops";
import { PanelError } from "@/components/medora/errors";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
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 { Spinner } from "@/components/ui/spinner";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { toast } from "@/components/ui/toast";
import { PayrollApi, type RateVersionRow } from "@/lib/api";

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

type Draft = {
  id: number | null; from: string; note: string;
  nssfPct: string; nssfUpper: string; nssfEmployerPct: string; shifPct: string; shifMin: string; levyPct: string; levyEmployerPct: string; relief: string;
  bands: { upTo: string; pct: string }[]; deductNssf: boolean; deductShif: boolean; deductLevy: boolean;
};

const FIELDS: { k: keyof Draft; label: string; hint: string }[] = [
  { k: "nssfPct", label: "NSSF, employee (%)", hint: "Of pay up to the ceiling" },
  { k: "nssfEmployerPct", label: "NSSF, employer (%)", hint: "Paid on top of pay" },
  { k: "nssfUpper", label: "NSSF ceiling (KES)", hint: "Pensionable pay limit" },
  { k: "shifPct", label: "SHIF (%)", hint: "Of gross pay" },
  { k: "shifMin", label: "SHIF minimum (KES)", hint: "Charged when the % is less" },
  { k: "levyPct", label: "Housing levy, employee (%)", hint: "Of gross pay" },
  { k: "levyEmployerPct", label: "Housing levy, employer (%)", hint: "Paid on top of pay" },
  { k: "relief", label: "Personal relief (KES a month)", hint: "Off the PAYE worked out" },
];

const blank = (from: string): Draft => ({
  id: null, from, note: "", nssfPct: "", nssfUpper: "", nssfEmployerPct: "", shifPct: "", shifMin: "", levyPct: "", levyEmployerPct: "", relief: "",
  bands: [{ upTo: "", pct: "" }, { upTo: "", pct: "" }], deductNssf: true, deductShif: true, deductLevy: true,
});
const toDraft = (v: RateVersionRow): Draft => ({
  id: v.id, from: v.from, note: v.note, nssfPct: String(v.nssfPct), nssfUpper: String(v.nssfUpper), nssfEmployerPct: String(v.nssfEmployerPct), shifPct: String(v.shifPct),
  shifMin: String(v.shifMin), levyPct: String(v.levyPct), levyEmployerPct: String(v.levyEmployerPct), relief: String(v.relief),
  bands: v.bands.map((b) => ({ upTo: b.upTo == null ? "" : String(b.upTo), pct: String(b.pct) })), deductNssf: v.deductNssf, deductShif: v.deductShif, deductLevy: v.deductLevy,
});
const dayBefore = (iso: string) => {
  const d = new Date(`${iso}T00:00:00`);
  d.setDate(d.getDate() - 1);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
};

export function RatesView({ today, may }: { today: string; may: (a: string) => boolean }) {
  const [data, setData] = React.useState<Awaited<ReturnType<typeof PayrollApi.rates>> | null>(null);
  const [failed, setFailed] = React.useState(false); // R12 (card 037): a failed load says so, with Try again
  const [draft, setDraft] = React.useState<Draft | null>(null);
  const [confirming, setConfirming] = React.useState<RateVersionRow | null>(null);
  const [checked, setChecked] = React.useState("");
  const [pin, setPin] = React.useState("");
  const { busy, run } = useAction();

  const load = React.useCallback(() => {
    PayrollApi.rates()
      .then((d) => {
        setData({ ...d, employerPin: d.employerPin ?? "" });
        setPin(d.employerPin ?? "");
        setFailed(false);
      })
      .catch((e) => (setFailed(true), toast.add({ type: "error", title: "Couldn't load statutory rates", description: apiError(e, "Try again.") })));
  }, []);
  React.useEffect(() => load(), [load]);

  if (failed) return <PanelError framed what="statutory rates" onRetry={load} />;
  if (!data)
    return (
      <Loading what="statutory rates" columns={5} rows={4} />
    );

  const sorted = [...data.versions].sort((a, b) => b.from.localeCompare(a.from));
  const save = async () => {
    if (!draft) return;
    const body = {
      ...Object.fromEntries(FIELDS.map((f) => [f.k, draft[f.k]])),
      from: draft.from, note: draft.note,
      bands: draft.bands.map((b, i) => ({ upTo: i === draft.bands.length - 1 ? null : b.upTo, pct: b.pct })),
      deductNssf: draft.deductNssf, deductShif: draft.deductShif, deductLevy: draft.deductLevy,
    };
    const ok = await run(() => (draft.id ? PayrollApi.updateRates(draft.id, body) : PayrollApi.addRates(body)), draft.id ? "Rates corrected" : "Rates entered: they still need confirming");
    if (ok) {
      setDraft(null);
      load();
    }
  };

  return (
    <div className="space-y-8">
      {data.problem ? (
        <p role="status" className="rounded-md border border-status-warning-border bg-status-warning-bg px-3 py-2 text-sm text-status-warning-text">
          {data.problem}
        </p>
      ) : null}

      <Section
        title="Statutory rate versions"
        description="Versions of PAYE, NSSF, SHIF and the levy; a run uses the confirmed one in force."
        actions={
          may("Update") ? (
            <Button size="sm" variant="outline" onClick={() => setDraft(blank(today))}>
              <PlusIcon data-icon="inline-start" />
              New version…
            </Button>
          ) : null
        }
      >
        {sorted.length ? (
          <>
            <RateVersions
              month={today.slice(0, 7)}
              formatDate={human}
              versions={sorted.map((v, i) => ({ id: String(v.id), from: v.from, to: i > 0 ? dayBefore(sorted[i - 1].from) : undefined, nssfUpper: v.nssfUpper, shifPct: v.shifPct, levyPct: v.levyPct, relief: v.relief, note: v.note }))}
            />
            <div className="overflow-x-auto rounded-md border border-[var(--color-border-panel)] bg-card">
              <Table>
                <TableHeader>
                  <TableRow>
                    <TableHead>From</TableHead>
                    <TableHead>PAYE bands</TableHead>
                    <TableHead>Contributions</TableHead>
                    <TableHead>Confirmed</TableHead>
                    <TableHead className="w-0" />
                  </TableRow>
                </TableHeader>
                <TableBody>
                  {sorted.map((v) => (
                    <TableRow key={v.id}>
                      <TableCell className="whitespace-nowrap">
                        {human(v.from)}
                        {v.id === data.inForceId ? <span className="block text-xs font-semibold text-primary">In force today</span> : v.scheduled ? <span className="block text-xs text-muted-foreground">Scheduled</span> : null}
                      </TableCell>
                      <TableCell className="text-xs tabular-nums">
                        {v.bands.map((b, i) => (
                          <span key={i} className="block">
                            {b.pct}% {b.upTo == null ? "above" : `up to ${b.upTo.toLocaleString("en-US")}`}
                          </span>
                        ))}
                      </TableCell>
                      <TableCell className="text-xs">
                        NSSF {v.nssfPct}% + {v.nssfEmployerPct}% employer, levy {v.levyPct}% + {v.levyEmployerPct}% employer, SHIF min {v.shifMin.toLocaleString("en-US")}
                        <span className="block text-muted-foreground">
                          Before PAYE: {[v.deductNssf && "NSSF", v.deductShif && "SHIF", v.deductLevy && "levy"].filter(Boolean).join(", ") || "nothing"}
                        </span>
                      </TableCell>
                      <TableCell className="text-xs">
                        {v.confirmed ? (
                          <>
                            <span className="font-medium">{v.confirmedBy ?? "Confirmed"}</span>, {human(String(v.confirmedAt).slice(0, 10))}
                            <span className="block text-muted-foreground">{v.confirmNote}</span>
                          </>
                        ) : (
                          <span className="font-medium text-status-warning-text">Not confirmed</span>
                        )}
                      </TableCell>
                      <TableCell className="whitespace-nowrap text-right">
                        {!v.confirmed && may("Approve") ? (
                          <Button size="sm" variant="outline" onClick={() => (setConfirming(v), setChecked(""))}>
                            <CheckIcon data-icon="inline-start" />
                            Confirm…
                          </Button>
                        ) : null}
                        {!v.confirmed && may("Update") ? (
                          <>
                            <Button size="sm" variant="ghost" onClick={() => setDraft(toDraft(v))}>
                              Correct
                            </Button>
                            <Button size="icon-sm" variant="ghost" aria-label={`Remove the rates from ${human(v.from)}`} disabled={busy} onClick={() => void run(() => PayrollApi.removeRates(v.id), "Rates removed").then(load)}>
                              <Trash2Icon />
                            </Button>
                          </>
                        ) : null}
                      </TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </div>
          </>
        ) : (
          <p className="text-sm text-muted-foreground">No rates entered yet. Payroll can&apos;t run until a version is entered and confirmed.</p>
        )}
      </Section>

      <Section title="Employer KRA PIN" description="Printed on payslips and P9 cards.">
        <Surface className="flex flex-wrap items-end gap-2">
          <Field className="w-56">
            <FieldLabel htmlFor="pr-pin">KRA PIN</FieldLabel>
            <Input id="pr-pin" value={pin} placeholder="P051234567X" disabled={!may("Update")} onChange={(e) => setPin(e.target.value.toUpperCase())} />
          </Field>
          {may("Update") && pin !== data.employerPin ? (
            <Button size="sm" disabled={busy} onClick={() => void run(() => PayrollApi.setEmployerPin(pin.trim()), "Employer PIN saved").then(load)}>
              Save PIN
            </Button>
          ) : null}
        </Surface>
      </Section>

      <Dialog open={!!draft} onOpenChange={(o) => (!o ? setDraft(null) : null)}>
        <DialogContent className="sm:max-w-2xl">
          <DialogHeader>
            <DialogTitle>{draft?.id ? "Correct unconfirmed rates" : "New statutory rate version"}</DialogTitle>
            <DialogDescription>Percentages as percentages (6 means 6%). They are used only after someone confirms them.</DialogDescription>
          </DialogHeader>
          {draft ? (
            <div className="max-h-[60vh] space-y-4 overflow-y-auto pr-1">
              <div className="grid gap-3">
                <Field>
                  <FieldLabel htmlFor="rv-from">In force from</FieldLabel>
                  <Input id="rv-from" type="date" value={draft.from} onChange={(e) => setDraft({ ...draft, from: e.target.value })} />
                </Field>
                <Field>
                  <FieldLabel htmlFor="rv-note">Why (the law or notice)</FieldLabel>
                  <Input id="rv-note" value={draft.note} onChange={(e) => setDraft({ ...draft, note: e.target.value })} />
                </Field>
                {FIELDS.map((f) => (
                  <Field key={f.k}>
                    <FieldLabel htmlFor={`rv-${f.k}`}>{f.label}</FieldLabel>
                    <Input id={`rv-${f.k}`} inputMode="decimal" value={draft[f.k] as string} placeholder={f.hint} onChange={(e) => setDraft({ ...draft, [f.k]: e.target.value })} />
                  </Field>
                ))}
              </div>
              <fieldset className="space-y-2">
                <legend className="text-sm font-medium">PAYE bands (monthly taxable pay)</legend>
                {draft.bands.map((b, i) => {
                  const last = i === draft.bands.length - 1;
                  return (
                    <div key={i} className="flex flex-wrap items-center gap-2 text-sm">
                      <Input aria-label={`Band ${i + 1} rate`} className="w-24" inputMode="decimal" placeholder="%" value={b.pct} onChange={(e) => setDraft({ ...draft, bands: draft.bands.map((x, j) => (j === i ? { ...x, pct: e.target.value } : x)) })} />
                      <span>% {last ? "on everything above" : "up to"}</span>
                      {!last ? <Input aria-label={`Band ${i + 1} upper limit`} className="w-36" inputMode="decimal" placeholder="KES" value={b.upTo} onChange={(e) => setDraft({ ...draft, bands: draft.bands.map((x, j) => (j === i ? { ...x, upTo: e.target.value } : x)) })} /> : null}
                      {draft.bands.length > 1 ? (
                        <Button size="icon-sm" variant="ghost" aria-label={`Remove band ${i + 1}`} onClick={() => setDraft({ ...draft, bands: draft.bands.filter((_, j) => j !== i) })}>
                          <Trash2Icon />
                        </Button>
                      ) : null}
                    </div>
                  );
                })}
                <Button size="sm" variant="outline" onClick={() => setDraft({ ...draft, bands: [...draft.bands.slice(0, -1), { upTo: "", pct: "" }, draft.bands[draft.bands.length - 1]] })}>
                  <PlusIcon data-icon="inline-start" />
                  Add band
                </Button>
              </fieldset>
              <fieldset className="space-y-1.5 text-sm">
                <legend className="font-medium">Taken off pay before PAYE is worked out</legend>
                {(
                  [
                    ["deductNssf", "NSSF"],
                    ["deductShif", "SHIF"],
                    ["deductLevy", "Housing levy"],
                  ] as const
                ).map(([k, l]) => (
                  <label key={k} className="flex items-center gap-2">
                    <Checkbox checked={draft[k]} onCheckedChange={(c) => setDraft({ ...draft, [k]: c === true })} />
                    {l}
                  </label>
                ))}
              </fieldset>
            </div>
          ) : null}
          <DialogFooter>
            <Button variant="outline" onClick={() => setDraft(null)}>
              Cancel
            </Button>
            <Button disabled={busy} onClick={() => void save()}>
              {busy ? <Spinner /> : null}
              Save rates
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog open={!!confirming} onOpenChange={(o) => (!o ? setConfirming(null) : null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Confirm the rates from {confirming ? human(confirming.from) : ""}</DialogTitle>
            <DialogDescription>Payroll will use them for every period they apply to, and they can&apos;t be changed after this. Say who checked them and against what.</DialogDescription>
          </DialogHeader>
          <Field>
            <FieldLabel htmlFor="rv-checked">Checked by, against</FieldLabel>
            <Input id="rv-checked" value={checked} placeholder="e.g. Our accountant, against the KRA, NSSF and SHA notices" onChange={(e) => setChecked(e.target.value)} />
          </Field>
          <DialogFooter>
            <Button variant="outline" onClick={() => setConfirming(null)}>
              Cancel
            </Button>
            <Button
              disabled={busy || checked.trim().length < 5}
              onClick={() =>
                void run(() => PayrollApi.confirmRates(confirming!.id, checked.trim()), "Rates confirmed").then((ok) => {
                  if (ok) {
                    setConfirming(null);
                    load();
                  }
                })
              }
            >
              Confirm rates
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
