"use client";

import * as React from "react";

import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { cn } from "@/lib/utils";

import { kraPinOk } from "./catalog-ops";
import { count } from "@/lib/format";

const n2 = (n: number) => n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
/** 2026-11-23 → 23 Nov 2026; 2026-11 → Nov 2026. */
const human = (iso: string) => (iso.length === 7 ? `${MON[Number(iso.slice(5, 7)) - 1]} ${iso.slice(0, 4)}` : `${Number(iso.slice(8, 10))} ${MON[Number(iso.slice(5, 7)) - 1]} ${iso.slice(0, 4)}`);

/* --------------------------------- HR-01/02. Employee form --------------------------------- */

export type EmployeeDraft = {
  first: string;
  last: string;
  idNumber: string;
  dob: string;
  phone: string;
  email: string;
  branch: string;
  department: string;
  designation: string;
  type: string;
  start: string;
  payType: "Monthly" | "Piece rate" | "Casual (hourly)";
  rate: string;
  statutory: { nssf: boolean; shif: boolean; levy: boolean; paye: boolean };
  user: string;
  payBy: "Bank" | "M-Pesa";
  bank: string;
  account: string;
  mpesa: string;
  kraPin: string;
  nssfNo: string;
  shaNo: string;
};

export type EmployeeTab = "Personal" | "Employment" | "Pay and statutory";

const TAB_OF: Record<keyof EmployeeDraft, EmployeeTab> = {
  first: "Personal", last: "Personal", idNumber: "Personal", dob: "Personal", phone: "Personal", email: "Personal",
  branch: "Employment", department: "Employment", designation: "Employment", type: "Employment", start: "Employment", user: "Employment",
  payType: "Pay and statutory", rate: "Pay and statutory", statutory: "Pay and statutory", payBy: "Pay and statutory", bank: "Pay and statutory", account: "Pay and statutory", mpesa: "Pay and statutory", kraPin: "Pay and statutory", nssfNo: "Pay and statutory", shaNo: "Pay and statutory",
};

const ageOn = (dob: string, today: string) => {
  const a = new Date(dob);
  const t = new Date(today);
  return t.getFullYear() - a.getFullYear() - (t.getMonth() < a.getMonth() || (t.getMonth() === a.getMonth() && t.getDate() < a.getDate()) ? 1 : 0);
};

export function employeeProblems(d: EmployeeDraft, today: string, takenIds: string[]): Partial<Record<keyof EmployeeDraft, string>> {
  const p: Partial<Record<keyof EmployeeDraft, string>> = {};
  if (!d.first.trim()) p.first = "Enter the first name.";
  if (!d.last.trim()) p.last = "Enter the last name.";
  if (!/^\d{7,9}$/.test(d.idNumber)) p.idNumber = "A national ID number has 7 to 9 digits.";
  else if (takenIds.includes(d.idNumber)) p.idNumber = "Another employee has this ID number.";
  if (!d.dob) p.dob = "Enter the date of birth.";
  else if (ageOn(d.dob, today) < 18) p.dob = "Employees must be 18 or older.";
  if (!/^(\+?254|0)[17]\d{8}$/.test(d.phone.replace(/\s/g, ""))) p.phone = "Enter a mobile number, e.g. 0712 345 678.";
  if (!d.branch) p.branch = "Choose a branch.";
  if (!d.designation.trim()) p.designation = "Enter the job title.";
  if (!d.start) p.start = "Enter the start date.";
  if (!(Number(d.rate) > 0)) p.rate = d.payType === "Monthly" ? "Enter the monthly basic pay." : d.payType === "Piece rate" ? "Enter the rate per piece." : "Enter the hourly rate.";
  if (!kraPinOk(d.kraPin) || d.kraPin[0] !== "A") p.kraPin = "An individual's KRA PIN is A, 9 digits and a letter, e.g. A012345678Z.";
  if (d.statutory.nssf && !d.nssfNo.trim()) p.nssfNo = "Enter the NSSF number, or untick NSSF.";
  if (d.statutory.shif && !d.shaNo.trim()) p.shaNo = "Enter the SHA number, or untick SHIF.";
  if (d.payBy === "Bank" && (!d.bank.trim() || !/^\d{6,16}$/.test(d.account))) p.account = "Enter the bank and a 6–16 digit account number.";
  if (d.payBy === "M-Pesa" && !/^(\+?254|0)[17]\d{8}$/.test(d.mpesa.replace(/\s/g, ""))) p.mpesa = "Enter the M-Pesa number pay goes to.";
  return p;
}

/** Problem count per tab, so the tab strip can say where to look. */
export const problemsByTab = (p: Partial<Record<keyof EmployeeDraft, string>>) => (Object.keys(p) as (keyof EmployeeDraft)[]).reduce<Record<string, number>>((acc, k) => ({ ...acc, [TAB_OF[k]]: (acc[TAB_OF[k]] ?? 0) + 1 }), {});

/**
 * Employee form (HR-01, HR-02) in three tabs. Saving checks every tab and the tab strip says how many
 * problems each has, so nothing hides behind a tab. Statutory deductions are ticked per employee (a casual
 * below the PAYE threshold may have none); each ticked one needs its number.
 */
export function EmployeeForm({ d, onChange, tab, onTab, problems, branches, types, users }: { d: EmployeeDraft; onChange: (d: EmployeeDraft) => void; tab: EmployeeTab; onTab: (t: EmployeeTab) => void; problems: Partial<Record<keyof EmployeeDraft, string>>; branches: string[]; types: string[]; users: string[] }) {
  const set = <K extends keyof EmployeeDraft>(k: K, v: EmployeeDraft[K]) => onChange({ ...d, [k]: v });
  const counts = problemsByTab(problems);
  const f = (k: keyof EmployeeDraft, label: string, node: React.ReactNode, hint?: string) => (
    <div className="space-y-1">
      <label htmlFor={`ef-${k}`} className="text-sm font-medium">
        {label}
      </label>
      {node}
      {problems[k] ? <p className="text-xs text-status-error-text">{problems[k]}</p> : hint ? <p className="text-xs text-muted-foreground">{hint}</p> : null}
    </div>
  );
  const inp = (k: keyof EmployeeDraft, extra?: React.ComponentProps<typeof Input>) => <Input id={`ef-${k}`} value={String(d[k])} onChange={(e) => set(k, e.target.value as never)} aria-invalid={!!problems[k]} {...extra} />;
  const sel = (k: keyof EmployeeDraft, options: string[], placeholder?: string) => (
    <NativeSelect id={`ef-${k}`} value={String(d[k])} onChange={(e) => set(k, e.target.value as never)} aria-invalid={!!problems[k]}>
      {placeholder ? <NativeSelectOption value="">{placeholder}</NativeSelectOption> : null}
      {options.map((o) => (
        <NativeSelectOption key={o}>{o}</NativeSelectOption>
      ))}
    </NativeSelect>
  );
  return (
    <div className="space-y-4">
      <div role="tablist" aria-label="Employee details" className="flex flex-wrap gap-1 border-b border-[var(--color-border-subtle)]">
        {(["Personal", "Employment", "Pay and statutory"] as const).map((t) => (
          <button key={t} type="button" role="tab" aria-selected={tab === t} onClick={() => onTab(t)} className={cn("-mb-px cursor-pointer border-b-2 px-3 py-1.5 text-sm", tab === t ? "border-primary font-medium" : "border-transparent text-muted-foreground hover:text-foreground")}>
            {t}
            {counts[t] ? <span className="ml-1.5 rounded-full bg-status-error-bg px-1.5 text-xs font-semibold text-status-error-text">{counts[t]}</span> : null}
          </button>
        ))}
      </div>
      <div role="tabpanel" aria-label={tab} className="grid gap-4 sm:grid-cols-2">
        {tab === "Personal" ? (
          <>
            {f("first", "First name", inp("first"))}
            {f("last", "Last name", inp("last"))}
            {f("idNumber", "National ID number", inp("idNumber", { inputMode: "numeric" }))}
            {f("dob", "Date of birth", inp("dob", { type: "date" }))}
            {f("phone", "Mobile number", inp("phone", { inputMode: "tel" }))}
            {f("email", "Email (optional)", inp("email", { type: "email" }), "Payslips are emailed here.")}
          </>
        ) : tab === "Employment" ? (
          <>
            {f("branch", "Branch", sel("branch", branches, "Choose a branch"))}
            {f("department", "Department", sel("department", ["Dispensary", "Front shop", "Stores", "Admin", "Delivery"]))}
            {f("designation", "Job title", inp("designation"))}
            {f("type", "Employee type", sel("type", types))}
            {f("start", "Start date", inp("start", { type: "date" }))}
            {f("user", "Medora user account (optional)", sel("user", users, "Not linked"), "Linking lets commission be worked out from their own sales.")}
          </>
        ) : (
          <>
            {f("payType", "Paid", sel("payType", ["Monthly", "Piece rate", "Casual (hourly)"]))}
            {f("rate", d.payType === "Monthly" ? "Basic pay per month (KES)" : d.payType === "Piece rate" ? "Rate per piece (KES)" : "Hourly rate (KES)", inp("rate", { inputMode: "decimal", className: "tabular-nums" }))}
            <fieldset className="space-y-1 sm:col-span-2">
              <legend className="text-sm font-medium">Statutory deductions that apply</legend>
              <div className="flex flex-wrap gap-x-5 gap-y-1 text-sm">
                {(["nssf", "shif", "levy", "paye"] as const).map((k) => (
                  <label key={k} className="inline-flex items-center gap-1.5">
                    <Checkbox checked={d.statutory[k]} onCheckedChange={(v) => set("statutory", { ...d.statutory, [k]: !!v })} />
                    {{ nssf: "NSSF", shif: "SHIF", levy: "Housing levy", paye: "PAYE" }[k]}
                  </label>
                ))}
              </div>
            </fieldset>
            {f("kraPin", "KRA PIN", inp("kraPin", { className: "font-mono uppercase", onChange: (e) => set("kraPin", e.target.value.toUpperCase()) }))}
            {d.statutory.nssf ? f("nssfNo", "NSSF number", inp("nssfNo")) : null}
            {d.statutory.shif ? f("shaNo", "SHA number", inp("shaNo")) : null}
            {f("payBy", "Pay goes to", sel("payBy", ["Bank", "M-Pesa"]))}
            {d.payBy === "Bank" ? (
              <>
                {f("bank", "Bank", inp("bank"))}
                {f("account", "Account number", inp("account", { inputMode: "numeric", className: "font-mono" }))}
              </>
            ) : (
              f("mpesa", "M-Pesa number", inp("mpesa", { inputMode: "tel" }))
            )}
          </>
        )}
      </div>
    </div>
  );
}

/* ------------------------------------- HR-04. Leave ------------------------------------- */

export type LeaveType = { name: string; daysPerYear: number; paid: boolean };
export type LeaveBalance = { employee: string; type: string; allocated: number; taken: number; booked: number };

/** Working days between two dates inclusive, skipping weekends and the given public holidays. */
export function workingDays(from: string, to: string, holidays: string[]) {
  if (!from || !to || to < from) return 0;
  let n = 0;
  // UTC throughout: ISO dates parse as UTC midnight, so local getters would shift days in some timezones.
  for (let d = new Date(`${from}T00:00:00Z`); d <= new Date(`${to}T00:00:00Z`); d.setUTCDate(d.getUTCDate() + 1)) {
    const iso = d.toISOString().slice(0, 10);
    const wd = d.getUTCDay();
    if (wd !== 0 && wd !== 6 && !holidays.includes(iso)) n += 1;
  }
  return n;
}

/** Leave balances (HR-04): allocated, taken, booked and left per employee and type; nothing left reads as a word. */
export function LeaveBalances({ rows }: { rows: LeaveBalance[] }) {
  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">Leave balances</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Employee", "Leave", "Allocated", "Taken", "Booked", "Left"].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", !["Employee", "Leave"].includes(h) && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {rows.map((r) => {
            const left = r.allocated - r.taken - r.booked;
            return (
              <tr key={`${r.employee}-${r.type}`}>
                <th scope="row" className="px-3 py-2 text-left font-medium">
                  {r.employee}
                </th>
                <td className="px-3 py-2">{r.type}</td>
                <td className="px-3 py-2 text-right">{r.allocated}</td>
                <td className="px-3 py-2 text-right">{r.taken}</td>
                <td className="px-3 py-2 text-right">{r.booked}</td>
                <td className={cn("px-3 py-2 text-right font-semibold", left <= 0 && "text-status-error-text")}>{left <= 0 ? "None left" : left}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

/** Problems with a leave request: dates, balance, and overlap with leave already booked. */
export function leaveProblems(r: { employee: string; type: string; from: string; to: string }, balance: LeaveBalance | undefined, booked: { employee: string; from: string; to: string }[], holidays: string[]) {
  const p: string[] = [];
  if (!r.from || !r.to) p.push("Choose the first and last day.");
  else if (r.to < r.from) p.push("The last day is before the first.");
  const days = workingDays(r.from, r.to, holidays);
  if (r.from && r.to && r.to >= r.from && days === 0) p.push("Those dates are all weekends or public holidays.");
  if (balance && days > balance.allocated - balance.taken - balance.booked) p.push(`${days} working days asked, ${Math.max(0, balance.allocated - balance.taken - balance.booked)} left of ${r.type.toLowerCase()} leave.`);
  const clash = booked.find((b) => b.employee === r.employee && r.from <= b.to && r.to >= b.from);
  if (clash) p.push(`Overlaps leave already booked from ${human(clash.from)} to ${human(clash.to)}.`);
  return { days, problems: p };
}

/* ----------------------------- HR-13/14. Allowances and deductions ----------------------------- */

export type PayItem = { id: string; employee: string; kind: "Allowance" | "Deduction"; category: string; amount: number; recurring: boolean; from: string; until?: string; taxable?: boolean };

/** Does a pay item apply to a payroll month (YYYY-MM)? One-off items apply only in their own month. */
export const appliesTo = (i: PayItem, month: string) => (i.recurring ? i.from <= month && (!i.until || i.until >= month) : i.from === month);

/** Allowances and deductions (HR-13, HR-14): what applies to this month's payroll, and why. */
export function PayItemsTable({ items, month }: { items: PayItem[]; month: string }) {
  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">Allowances and deductions</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Employee", "Item", "Amount", "When", `In ${human(month)}`].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", h === "Amount" && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        <tbody className="divide-y divide-[var(--color-border-subtle)]">
          {items.map((i) => {
            const on = appliesTo(i, month);
            return (
              <tr key={i.id} className={cn(!on && "text-muted-foreground")}>
                <th scope="row" className="px-3 py-2 text-left font-medium">
                  {i.employee}
                </th>
                <td className="px-3 py-2">
                  {i.kind}: {i.category}
                  {i.kind === "Allowance" ? <span className="block text-xs text-muted-foreground">{i.taxable === false ? "Not taxable" : "Taxable"}</span> : null}
                </td>
                <td className="px-3 py-2 text-right">{i.kind === "Deduction" ? `−${n2(i.amount)}` : n2(i.amount)}</td>
                <td className="px-3 py-2 text-xs">{i.recurring ? `Every month from ${human(i.from)}${i.until ? ` to ${human(i.until)}` : ""}` : `Once, ${human(i.from)}`}</td>
                <td className="px-3 py-2 text-xs font-medium">{on ? "Included" : i.recurring && i.until && i.until < month ? "Ended" : "Not this month"}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

/* ------------------------------- HR-05/06/08. Payroll runs ------------------------------- */

export type RunKind = "Monthly" | "Piece workers" | "Casuals";
export type RunEmployee = { name: string; department: string; kind: RunKind; gross: number; net: number; paye: number; phone?: string; email?: string };

/** Payslips grouped by department with subtotals (HR-08 grouped view). */
export function PayslipGroups({ rows }: { rows: RunEmployee[] }) {
  const depts = [...new Set(rows.map((r) => r.department))];
  const tot = (rs: RunEmployee[]) => rs.reduce((s, r) => ({ gross: s.gross + r.gross, paye: s.paye + r.paye, net: s.net + r.net }), { gross: 0, paye: 0, net: 0 });
  const all = tot(rows);
  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">Payslips by department</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
          <tr>
            {["Employee", "Gross", "PAYE", "Net"].map((h) => (
              <th key={h} scope="col" className={cn("px-3 py-2 font-medium", h !== "Employee" && "text-right")}>
                {h}
              </th>
            ))}
          </tr>
        </thead>
        {depts.map((d) => {
          const rs = rows.filter((r) => r.department === d);
          const t = tot(rs);
          return (
            <tbody key={d} className="divide-y divide-[var(--color-border-subtle)] border-t-2 border-[var(--color-border-panel)]">
              <tr className="bg-muted/30">
                <th colSpan={4} scope="rowgroup" className="px-3 py-1.5 text-left text-xs font-semibold tracking-wide uppercase">
                  {d}
                </th>
              </tr>
              {rs.map((r) => (
                <tr key={r.name}>
                  <th scope="row" className="px-3 py-1.5 pl-6 text-left font-normal">
                    {r.name}
                  </th>
                  <td className="px-3 py-1.5 text-right">{n2(r.gross)}</td>
                  <td className="px-3 py-1.5 text-right">{n2(r.paye)}</td>
                  <td className="px-3 py-1.5 text-right">{n2(r.net)}</td>
                </tr>
              ))}
              <tr className="font-semibold">
                <th scope="row" className="px-3 py-1.5 text-left">
                  {d} total
                </th>
                <td className="px-3 py-1.5 text-right">{n2(t.gross)}</td>
                <td className="px-3 py-1.5 text-right">{n2(t.paye)}</td>
                <td className="px-3 py-1.5 text-right">{n2(t.net)}</td>
              </tr>
            </tbody>
          );
        })}
        <tfoot className="border-t-2 border-[var(--color-border-panel)] font-semibold">
          <tr>
            <th scope="row" className="px-3 py-2 text-left">
              All, {count(rows.length, "employee")}
            </th>
            <td className="px-3 py-2 text-right">{n2(all.gross)}</td>
            <td className="px-3 py-2 text-right">{n2(all.paye)}</td>
            <td className="px-3 py-2 text-right">{n2(all.net)}</td>
          </tr>
        </tfoot>
      </table>
    </div>
  );
}

/** Who can't be sent their payslip by the chosen channel, and why (HR-08). */
export const unreachable = (rows: RunEmployee[], channel: "SMS" | "Email") => rows.filter((r) => (channel === "SMS" ? !r.phone : !r.email));
