"use client";

import * as React from "react";
import Image from "next/image";

import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { localToday } from "@/lib/format";
import { cn } from "@/lib/utils";

import { computePay, KE_PAYROLL } from "./payroll";

const n2 = (n: number) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const r2 = (n: number) => Math.round(n * 100) / 100;
const kes = (n: number) => `KES ${n2(n)}`;
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

/* ---------------------------------- 23. Time logs ---------------------------------- */

export type TimeLog = { id: string; date: string; employee: string; timeIn: string; timeOut: string; rate: number; paid: boolean };

/** Hours between two HH:MM times; past midnight counts as overnight. Returns null when either is missing. */
export function hoursWorked(timeIn: string, timeOut: string) {
  if (!/^\d{2}:\d{2}$/.test(timeIn) || !/^\d{2}:\d{2}$/.test(timeOut)) return null;
  const [h1, m1] = timeIn.split(":").map(Number);
  const [h2, m2] = timeOut.split(":").map(Number);
  let mins = h2 * 60 + m2 - (h1 * 60 + m1);
  const overnight = mins < 0;
  if (overnight) mins += 24 * 60;
  return { hours: r2(mins / 60), overnight };
}

/**
 * Overtime and casual time logs (PRD 7.4, 7.5): time in and out, hours worked out (overnight shifts say so),
 * rate and amount. More than 16 hours is refused as a likely typing mistake. Paid logs are locked: a payroll
 * run already used them.
 */
export function TimeLogTable({ logs, onChange, label }: { logs: TimeLog[]; onChange: (id: string, patch: Partial<TimeLog>) => void; label: string }) {
  const rows = logs.map((l) => ({ l, h: hoursWorked(l.timeIn, l.timeOut) }));
  const unpaid = rows.filter((r) => !r.l.paid && r.h && r.h.hours <= 16);
  const total = unpaid.reduce((s, r) => s + r.h!.hours * r.l.rate, 0);
  return (
    <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
      <table className="w-full border-collapse text-sm tabular-nums">
        <caption className="sr-only">{label}</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Date", "Employee", "In", "Out", "Hours", "Rate", "Amount", "Status"].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", ["Hours", "Rate", "Amount"].includes(h) && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {rows.map(({ l, h }) => {
            const tooLong = !!h && h.hours > 16;
            return (
              <tr key={l.id} className={cn(l.paid && "text-muted-foreground")}>
                <td className="px-3 py-1.5 whitespace-nowrap">{l.date}</td>
                <td className="px-3 py-1.5">{l.employee}</td>
                {(["timeIn", "timeOut"] as const).map((k) => (
                  <td key={k} className="px-3 py-1.5">
                    <Input type="time" className="h-8 w-28" aria-label={`${k === "timeIn" ? "Time in" : "Time out"}, ${l.employee}, ${l.date}`} value={l[k]} disabled={l.paid} aria-invalid={tooLong} onChange={(e) => onChange(l.id, { [k]: e.target.value })} />
                  </td>
                ))}
                <td className="px-3 py-1.5 text-right">
                  {h ? n2(h.hours) : "–"}
                  {h?.overnight ? <span className="block text-[11px] text-muted-foreground">Overnight</span> : null}
                  {tooLong ? <span className="block text-[11px] font-medium text-status-error-text">Over 16 h: check the times</span> : null}
                </td>
                <td className="px-3 py-1.5 text-right">{n2(l.rate)}</td>
                <td className="px-3 py-1.5 text-right font-medium">{h && !tooLong ? n2(h.hours * l.rate) : "–"}</td>
                <td className="px-3 py-1.5 text-xs">{l.paid ? "Paid, Sep payroll" : "Unpaid"}</td>
              </tr>
            );
          })}
        </tbody>
        <tfoot className="border-t-2 border-[var(--color-border-panel)] font-semibold">
          <tr>
            <th scope="row" colSpan={6} className="px-3 py-2 text-left">
              Unpaid, goes to the next payroll ({unpaid.length} {unpaid.length === 1 ? "log" : "logs"})
            </th>
            <td className="px-3 py-2 text-right">{n2(total)}</td>
            <td />
          </tr>
        </tfoot>
      </table>
    </div>
  );
}

/* ------------------------------------- 24. Loans ------------------------------------- */

export type Loan = { ref: string; kind: "Company loan" | "HELB"; employee: string; principal: number; instalment: number; firstMonth: string; repaid: number };

/** Month-by-month: each payroll deducts the instalment until the balance is zero; the last one is smaller. */
export function loanSchedule(l: Loan) {
  const rows: { month: string; deduction: number; balance: number }[] = [];
  let bal = r2(l.principal);
  let [y, m] = l.firstMonth.split("-").map(Number);
  while (bal > 0.005 && rows.length < 120) {
    const d = Math.min(l.instalment, bal);
    bal = r2(bal - d);
    rows.push({ month: `${MONTHS[m - 1]} ${y}`, deduction: d, balance: bal });
    m += 1;
    if (m > 12) {
      m = 1;
      y += 1;
    }
  }
  return rows;
}

/* --------------------------- 25. Statutory rates with dates --------------------------- */

export type RateVersion = { id: string; from: string; to?: string; nssfUpper: number; shifPct: number; levyPct: number; relief: number; note: string };

/**
 * Statutory rate versions (PRD 7.9): every change by law is a new version from its effective date; earlier
 * payslips keep the version that applied to their month. Shows which version a payroll month uses.
 */
export function RateVersions({ versions, month, formatDate }: { versions: RateVersion[]; month: string; formatDate: (iso: string) => string }) {
  const applies = (v: RateVersion) => v.from <= `${month}-01` && (!v.to || v.to >= `${month}-01`);
  return (
    <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
      <table className="w-full border-collapse text-sm tabular-nums">
        <caption className="sr-only">Statutory rate versions</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["In force", "NSSF ceiling", "SHIF", "Housing levy", "Personal relief", "Why it changed"].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", ["NSSF ceiling", "SHIF", "Housing levy", "Personal relief"].includes(h) && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {versions.map((v, i) => {
            const prev = versions[i + 1];
            const changed = (k: keyof RateVersion) => prev && prev[k] !== v[k];
            return (
              <tr key={v.id} className={cn(applies(v) && "bg-[var(--color-bg-selected)]")}>
                <td className="px-3 py-2 whitespace-nowrap">
                  {v.from > `${month}-31` ? `From ${formatDate(v.from)}` : `${formatDate(v.from)} – ${v.to ? formatDate(v.to) : "now"}`}
                  {applies(v) ? <span className="block text-xs font-semibold text-primary">Used for this payroll</span> : null}
                  {v.from > `${month}-31` ? <span className="block text-xs font-medium text-status-info-text">Scheduled</span> : null}
                </td>
                {(["nssfUpper", "shifPct", "levyPct", "relief"] as const).map((k) => (
                  <td key={k} className={cn("px-3 py-2 text-right", changed(k) && "font-semibold")}>
                    {k.endsWith("Pct") ? `${v[k]}%` : n2(v[k])}
                    {changed(k) ? <span className="block text-[11px] font-normal text-muted-foreground">was {k.endsWith("Pct") ? `${prev![k]}%` : n2(prev![k] as number)}</span> : null}
                  </td>
                ))}
                <td className="px-3 py-2 text-muted-foreground">{v.note}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

/* ----------------------------------- 26. Commissions ----------------------------------- */

export type CommissionRow = { employee: string; sales: number; returns: number; target: number; pct: number; override?: { amount: number; reason: string } };

/** Commission on net sales above target (sales less returns), at the employee's rate. Never negative. */
export const commissionOf = (r: CommissionRow) => r2(Math.max(0, r.sales - r.returns - r.target) * (r.pct / 100));

/* -------------------------------------- 27. P9 -------------------------------------- */

export type P9Month = { basic: number; allowances: number };

/**
 * P9 tax deduction card (PRD 7.2, HR-09): one row per month of the year from the payroll actually run,
 * using the same calculation as the payslips, with year totals. Printable on A4. The column layout must be
 * checked against KRA's current P9A form before release.
 */
/** A P9 month as the payslips worked it out (plan L): live payroll passes these instead of recomputing with `KE_PAYROLL`. */
export type P9Row = { basic: number; allowances: number; gross: number; nssf: number; shif: number; housingLevy: number; taxable: number; payeBeforeRelief: number; relief: number; paye: number };

export const P9Card = React.forwardRef<HTMLDivElement, { employer: { name: string; pin: string }; employee: { name: string; pin: string; number: string }; year: number; months: P9Month[]; rows?: P9Row[]; ratesAsAt?: string }>(function P9Card({ employer, employee, year, months, rows: given, ratesAsAt = KE_PAYROLL.effective }, ref) {
  const rows: P9Row[] = given ?? months.map((m) => {
    const p = computePay({ basic: m.basic, allowances: m.allowances, overtime: 0, otherDeductions: 0 });
    return { ...p, relief: Math.min(KE_PAYROLL.paye.personalRelief, p.payeBeforeRelief) };
  });
  const sum = (k: "gross" | "nssf" | "shif" | "housingLevy" | "taxable" | "payeBeforeRelief" | "paye") => rows.reduce((s, r) => s + r[k], 0);
  const cell = "px-1.5 py-1 text-right";
  return (
    <div ref={ref} className="mx-auto box-border w-[297mm] bg-white p-[10mm] text-[8pt] leading-tight text-black shadow-md print:w-auto print:p-0 print:shadow-none" aria-label={`P9 for ${employee.name}, ${year}`}>
      <header className="flex items-start justify-between gap-6 border-b border-black pb-2">
        <div>
          <Image src="/medora-logo.svg" alt="Medora" width={884} height={238} unoptimized className="mb-1 h-[7mm] w-auto grayscale contrast-[1.8]" />
          <p className="font-bold">{employer.name}</p>
          <p>Employer PIN {employer.pin}</p>
        </div>
        <div className="text-right">
          <h1 className="text-[13pt] font-bold uppercase">Tax deduction card (P9), {year}</h1>
          <p>
            {employee.name} · {employee.number}
          </p>
          <p>Employee PIN {employee.pin}</p>
          <p className="font-bold uppercase">Confidential pay information</p>
        </div>
      </header>
      <table className="mt-3 w-full border-collapse tabular-nums">
        <thead>
          <tr className="border-b border-black text-[7pt] uppercase">
            {["Month", "Basic", "Allowances", "Gross pay", "NSSF", "SHIF", "Housing levy", "Taxable pay", "Tax charged", "Personal relief", "PAYE"].map((h, i) => (
              <th key={h} className={cn("px-1.5 py-1 font-bold", i === 0 ? "text-left" : "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((r, i) => (
            <tr key={i} className="border-b border-neutral-300">
              <td className="px-1.5 py-1">{MONTHS[i]}</td>
              <td className={cell}>{n2(r.basic)}</td>
              <td className={cell}>{n2(r.allowances)}</td>
              <td className={cell}>{n2(r.gross)}</td>
              <td className={cell}>{n2(r.nssf)}</td>
              <td className={cell}>{n2(r.shif)}</td>
              <td className={cell}>{n2(r.housingLevy)}</td>
              <td className={cell}>{n2(r.taxable)}</td>
              <td className={cell}>{n2(r.payeBeforeRelief)}</td>
              <td className={cell}>{n2(r.relief)}</td>
              <td className={cn(cell, "font-semibold")}>{n2(r.paye)}</td>
            </tr>
          ))}
        </tbody>
        <tfoot>
          <tr className="border-t-2 border-black font-bold">
            <td className="px-1.5 py-1">Total</td>
            <td className={cell}>{n2(rows.reduce((s, r) => s + r.basic, 0))}</td>
            <td className={cell}>{n2(rows.reduce((s, r) => s + r.allowances, 0))}</td>
            <td className={cell}>{n2(sum("gross"))}</td>
            <td className={cell}>{n2(sum("nssf"))}</td>
            <td className={cell}>{n2(sum("shif"))}</td>
            <td className={cell}>{n2(sum("housingLevy"))}</td>
            <td className={cell}>{n2(sum("taxable"))}</td>
            <td className={cell}>{n2(sum("payeBeforeRelief"))}</td>
            <td className={cell}>{n2(rows.reduce((s, r) => s + r.relief, 0))}</td>
            <td className={cell}>{n2(sum("paye"))}</td>
          </tr>
        </tfoot>
      </table>
      <p className="mt-3 text-[7pt]">Figures are the payroll runs approved for {year}. Statutory rates as at {ratesAsAt}. Total PAYE is the amount to be shown on the employee&apos;s annual return.</p>
    </div>
  );
});
/* ---------------------------------- 24b. Issue a loan ---------------------------------- */

/** "YYYY-MM" of the month after today: a loan issued now is first deducted in the next payroll. */
function nextMonth(): string {
  const [y, m] = localToday().split("-").map(Number);
  return m === 12 ? `${y + 1}-01` : `${y}-${String(m + 1).padStart(2, "0")}`;
}

/**
 * Issue a loan (PRD 7.6): the instalment can't leave the employee with less than a third of their basic pay
 * after all deductions (Employment Act s.19(3)); the form says what they'd take home. Shows how many
 * payrolls it takes and the last month.
 */
export function LoanForm({ employees, onSave, firstMonth: initialMonth = nextMonth() }: { employees: { name: string; basic: number; deductions: number }[]; firstMonth?: string; onSave: (l: { employee: string; kind: "Company loan" | "HELB"; principal: number; instalment: number; firstMonth: string }) => void }) {
  const [employee, setEmployee] = React.useState(employees[0].name);
  const [kind, setKind] = React.useState<"Company loan" | "HELB">("Company loan");
  const [principal, setPrincipal] = React.useState("");
  const [instalment, setInstalment] = React.useState("");
  const [firstMonth, setFirstMonth] = React.useState(initialMonth);
  const [tried, setTried] = React.useState(false);
  const emp = employees.find((e) => e.name === employee)!;
  const p = Number(principal.replace(/,/g, ""));
  const i = Number(instalment.replace(/,/g, ""));
  const takeHome = emp.basic - emp.deductions - (i || 0);
  const floor = r2(emp.basic / 3);
  const errs = {
    principal: !(p > 0) ? "Enter the amount lent." : undefined,
    instalment: !(i > 0) ? "Enter the monthly instalment." : i > p ? "The instalment can't be more than the loan." : takeHome < floor ? `This leaves ${employee} with ${kes(takeHome)}, under a third of basic pay (${kes(floor)}). Lower the instalment.` : undefined,
  };
  const schedule = p > 0 && i > 0 ? loanSchedule({ ref: "", kind, employee, principal: p, instalment: i, firstMonth, repaid: 0 }) : [];
  const input = "w-full";
  return (
    <div className="space-y-3 text-sm">
      <div className="grid gap-3 sm:grid-cols-2">
        <label className="space-y-1">
          <span className="font-medium">Employee</span>
          <NativeSelect className={input} value={employee} onChange={(e) => setEmployee(e.target.value)}>
            {employees.map((e) => (
              <NativeSelectOption key={e.name}>{e.name}</NativeSelectOption>
            ))}
          </NativeSelect>
        </label>
        <label className="space-y-1">
          <span className="font-medium">Kind</span>
          <NativeSelect className={input} value={kind} onChange={(e) => setKind(e.target.value as "Company loan" | "HELB")}>
            <NativeSelectOption>Company loan</NativeSelectOption>
            <NativeSelectOption>HELB</NativeSelectOption>
          </NativeSelect>
        </label>
        <label className="space-y-1">
          <span className="font-medium">{kind === "HELB" ? "Balance HELB says is owed" : "Amount lent"} (KES)</span>
          <Input className={cn(input, "tabular-nums")} inputMode="decimal" value={principal} onChange={(e) => setPrincipal(e.target.value)} aria-invalid={tried && !!errs.principal} />
          {tried && errs.principal ? <span className="block text-xs text-status-error-text">{errs.principal}</span> : null}
        </label>
        <label className="space-y-1">
          <span className="font-medium">Monthly instalment (KES)</span>
          <Input className={cn(input, "tabular-nums")} inputMode="decimal" value={instalment} onChange={(e) => setInstalment(e.target.value)} aria-invalid={tried && !!errs.instalment} />
          {tried && errs.instalment ? (
            <span className="block text-xs text-status-error-text">{errs.instalment}</span>
          ) : i > 0 ? (
            <span className="block text-xs text-muted-foreground">Takes home about {kes(takeHome)} of {kes(emp.basic)} basic.</span>
          ) : null}
        </label>
        <label className="space-y-1">
          <span className="font-medium">First deduction</span>
          <Input className={input} type="month" value={firstMonth} onChange={(e) => setFirstMonth(e.target.value)} />
        </label>
      </div>
      {schedule.length ? (
        <p className="text-muted-foreground">
          {schedule.length} {schedule.length === 1 ? "deduction" : "deductions"}, last in {schedule.at(-1)!.month}
          {schedule.at(-1)!.deduction !== i ? ` (${kes(schedule.at(-1)!.deduction)})` : ""}.
        </p>
      ) : null}
      <div className="flex justify-end">
        <Button
          onClick={() => {
            setTried(true);
            if (!Object.values(errs).some(Boolean)) onSave({ employee, kind, principal: p, instalment: i, firstMonth });
          }}
        >
          Issue loan
        </Button>
      </div>
    </div>
  );
}

/* ------------------------------ 26b. Commission override ------------------------------ */

/** Override a worked-out commission: a different amount needs a reason, and both figures stay on record. */
export function CommissionOverride({ row, onSave, onCancel }: { row: CommissionRow; onSave: (o: { amount: number; reason: string } | undefined) => void; onCancel: () => void }) {
  const worked = commissionOf(row);
  const [amount, setAmount] = React.useState(String(row.override?.amount ?? worked));
  const [reason, setReason] = React.useState(row.override?.reason ?? "");
  const [tried, setTried] = React.useState(false);
  const n = Number(amount.replace(/,/g, ""));
  const same = Math.abs(n - worked) < 0.005;
  const err = Number.isNaN(n) || n < 0 ? "Enter an amount." : !same && !reason.trim() ? "Say why it differs from the worked-out figure." : undefined;
  return (
    <div className="flex flex-wrap items-end gap-2 text-sm">
      <label className="space-y-1">
        <span className="block text-xs font-medium">Pay (worked out {n2(worked)})</span>
        <Input className="h-8 w-28 tabular-nums" inputMode="decimal" value={amount} onChange={(e) => setAmount(e.target.value)} aria-label={`Commission for ${row.employee}`} />
      </label>
      <label className="min-w-48 flex-1 space-y-1">
        <span className="block text-xs font-medium">Reason</span>
        <Input className="h-8" value={reason} onChange={(e) => setReason(e.target.value)} aria-label={`Reason for ${row.employee}'s commission`} aria-invalid={tried && !!err} />
      </label>
      <Button size="sm" variant="ghost" onClick={onCancel}>
        Cancel
      </Button>
      <Button size="sm"
        onClick={() => {
          setTried(true);
          if (!err) onSave(same ? undefined : { amount: n, reason: reason.trim() });
        }}
      >
        Save
      </Button>
      {tried && err ? <p className="w-full text-xs text-status-error-text">{err}</p> : null}
    </div>
  );
}
