"use client";

/**
 * Employees (HR-01, HR-02; plan K): the design's `EmployeesList` (search, filters, CSV import and export, deactivate)
 * and `EmployeeForm` in three tabs. Pay, ID and payment details are shown and changed only with HRM → Salaries (Q1):
 * without it the pay tab is closed and those fields are never sent.
 */

import * as React from "react";

import { EmployeeForm, employeeProblems, type EmployeeDraft, type EmployeeTab } from "@/components/medora/hr-more";
import { PanelError } from "@/components/medora/errors";
import { EmployeesList, type EmployeeListRow } from "@/components/medora/registers";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Field, FieldDescription, FieldLabel } from "@/components/ui/field";
import { NativeSelect, NativeSelectOption } from "@/components/ui/native-select";
import { Spinner } from "@/components/ui/spinner";
import { toast } from "@/components/ui/toast";
import { HrmApi, type EmployeeOptions, type EmployeeRecord } from "@/lib/api";
import { count, formatKES } from "@/lib/format";
import { Loading } from "@/components/medora/loading";

const PAY_KEYS: (keyof EmployeeDraft)[] = ["idNumber", "rate", "kraPin", "nssfNo", "shaNo", "payBy", "bank", "account", "mpesa"];
const LIST_TYPES = ["Permanent", "Contract", "Casual", "Piece rate"] as const;
const PER: Record<string, string> = { Monthly: "month", "Piece rate": "piece", "Casual (hourly)": "hour" };

export const apiError = (e: unknown, fallback: string): string => {
  const m = (e as { response?: { data?: { message?: string | string[] } } })?.response?.data?.message;
  return Array.isArray(m) ? m.join(" ") : m || fallback;
};

const blank = (today: string): EmployeeDraft => ({
  first: "", last: "", idNumber: "", dob: "", phone: "", email: "", branch: "", department: "Dispensary", designation: "", type: "", start: today,
  payType: "Monthly", rate: "", statutory: { nssf: true, shif: true, levy: true, paye: true }, user: "", payBy: "Bank", bank: "", account: "", mpesa: "", kraPin: "", nssfNo: "", shaNo: "",
});

/** 2547… back to the 07… people type. */
const local = (p: string | null | undefined) => (p && /^254\d{9}$/.test(p) ? `0${p.slice(3)}` : (p ?? ""));

function draftOf(e: EmployeeRecord, today: string): EmployeeDraft {
  return {
    first: e.firstName, last: e.lastName, idNumber: e.idNumber ?? "", dob: e.dob ?? "", phone: local(e.phone), email: e.email ?? "", branch: e.branch ?? "",
    department: e.department ?? "Dispensary", designation: e.designation ?? "", type: e.type ?? "", start: e.startDate ?? today,
    payType: (e.payType as EmployeeDraft["payType"]) || "Monthly", rate: e.rate == null ? "" : String(e.rate), statutory: e.statutory, user: e.username ?? "",
    payBy: (e.payBy as EmployeeDraft["payBy"]) || "Bank", bank: e.bank ?? "", account: e.account ?? "", mpesa: local(e.mpesa), kraPin: e.kraPin ?? "", nssfNo: e.nssfNo ?? "", shaNo: e.shaNo ?? "",
  };
}

/** `addRef`: R2 (card 037), the page header's "Add employee" opens the form through it. */
/** `importOpen`: the page header's More opens the CSV import (page header spec). */
export function EmployeesView({ today, may, addRef, importOpen = false, onImportOpenChange }: { today: string; may: (a: string) => boolean; addRef?: React.RefObject<(() => void) | null>; importOpen?: boolean; onImportOpenChange?: (open: boolean) => void }) {
  const [rows, setRows] = React.useState<EmployeeRecord[] | null>(null);
  const [failed, setFailed] = React.useState(false); // R12 (card 037): a failed load says so, with Try again
  const [opts, setOpts] = React.useState<EmployeeOptions | null>(null);
  const [editing, setEditing] = React.useState<null | { id: number | null }>(null);
  const [d, setD] = React.useState<EmployeeDraft>(blank(today));
  const [gender, setGender] = React.useState("");
  const [tab, setTab] = React.useState<EmployeeTab>("Personal");
  const [tried, setTried] = React.useState(false);
  const [busy, setBusy] = React.useState(false);

  const load = React.useCallback(() => {
    HrmApi.employees()
      .then((r) => (setRows(r), setFailed(false)))
      .catch((e) => (setRows([]), setFailed(true), toast.add({ type: "error", title: "Couldn't load employees", description: apiError(e, "Try again.") })));
  }, []);
  React.useEffect(() => load(), [load]);

  const open = async (id: number | null) => {
    if (id == null ? !may("Create") : !may("Update")) return toast.add({ type: "error", title: id == null ? "You can't add employees" : "You can't change employees" });
    try {
      const [o, e] = await Promise.all([HrmApi.options(id ?? undefined), id == null ? Promise.resolve(null) : HrmApi.employee(id)]);
      if (id == null && !o.canChangePay) return toast.add({ type: "error", title: "Adding an employee sets their pay", description: "That needs Update on HRM → Salaries. Ask an Admin." });
      setOpts(o);
      setD(e ? draftOf(e, today) : { ...blank(today), branch: o.branches.length === 1 ? o.branches[0].name : "", type: o.types[0]?.name ?? "" });
      setGender(e?.gender ?? "");
      setTab("Personal");
      setTried(false);
      setEditing({ id });
    } catch (err) {
      toast.add({ type: "error", title: "Couldn't open the record", description: apiError(err, "Try again.") });
    }
  };
  React.useEffect(() => {
    if (!addRef) return;
    addRef.current = () => void open(null);
    return () => {
      addRef.current = null;
    };
  });

  const pay = !!opts?.canSeePay;
  const changePay = !!opts?.canChangePay;
  const all = employeeProblems(d, today, pay ? (rows ?? []).filter((r) => r.id !== editing?.id && r.idNumber).map((r) => r.idNumber as string) : []);
  const problems = Object.fromEntries(Object.entries(all).filter(([k]) => changePay || !PAY_KEYS.includes(k as keyof EmployeeDraft))) as typeof all;
  if (!d.type) problems.type = "Choose the employee type.";

  const save = async () => {
    if (!editing || !opts) return;
    setTried(true);
    if (Object.keys(problems).length) return;
    const id = (list: { id: number; name: string }[], name: string) => list.find((x) => x.name === name)?.id ?? null;
    const body: Record<string, unknown> = {
      firstName: d.first, lastName: d.last, dob: d.dob, gender, phone: d.phone, email: d.email, branchId: id(opts.branches, d.branch), department: d.department,
      designation: d.designation, typeId: id(opts.types, d.type), startDate: d.start, userId: opts.users.find((u) => u.username === d.user)?.id ?? null,
    };
    if (changePay) Object.assign(body, { payType: d.payType, idNumber: d.idNumber, rate: d.rate, statutory: d.statutory, kraPin: d.kraPin, nssfNo: d.nssfNo, shaNo: d.shaNo, payBy: d.payBy, bank: d.bank, account: d.account, mpesa: d.mpesa });
    setBusy(true);
    try {
      const e = editing.id == null ? await HrmApi.createEmployee(body) : await HrmApi.updateEmployee(editing.id, body);
      toast.add({ title: editing.id == null ? `${e.name} added as ${e.number}` : `${e.name} saved` });
      setEditing(null);
      load();
    } catch (err) {
      toast.add({ type: "error", title: "Not saved", description: apiError(err, "Try again.") });
    } finally {
      setBusy(false);
    }
  };

  if (failed) return <PanelError framed what="employees" onRetry={load} />;
  if (!rows)
    return (
      <Loading what="employees" columns={6} rows={8} toolbar={3} />
    );

  const incomplete = rows.filter((r) => r.active && r.missing.length);
  const list: EmployeeListRow[] = rows.map((r) => ({
    id: String(r.id), number: r.number, name: r.name, role: r.designation || "No job title yet",
    type: (LIST_TYPES as readonly string[]).includes(r.type ?? "") ? (r.type as EmployeeListRow["type"]) : ((r.type ?? "Contract") as EmployeeListRow["type"]),
    branch: r.branch ?? "No branch", pay: r.payHidden ? r.payType : r.rate ? `${formatKES(r.rate)} / ${PER[r.payType] ?? "month"}` : "Not set", active: r.active,
  }));

  return (
    <div className="space-y-4">
      {incomplete.length ? (
        <div className="rounded-lg border border-status-warning-border bg-status-warning-bg p-3 text-sm text-status-warning-text">
          <p className="font-medium">{incomplete.length === 1 ? "1 record needs completing" : `${incomplete.length} records need completing`}</p>
          <ul className="mt-1 list-disc pl-5">
            {incomplete.slice(0, 8).map((r) => (
              <li key={r.id}>
                <button type="button" className="cursor-pointer underline-offset-2 hover:underline" onClick={() => void open(r.id)}>
                  {r.name}
                </button>
                : {r.missing.join(", ")}
              </li>
            ))}
          </ul>
        </div>
      ) : null}
      <EmployeesList
        rows={list}
        importOpen={importOpen}
        onImportOpenChange={onImportOpenChange}
        onOpen={(id) => void open(Number(id))}
        onToggleActive={(id) => {
          if (!may("Update")) return toast.add({ type: "error", title: "You can't change employees" });
          const r = rows.find((x) => x.id === Number(id));
          if (!r) return;
          HrmApi.setActive(r.id, !r.active)
            .then(() => {
              toast.add({ title: r.active ? `${r.name} deactivated` : `${r.name} reactivated`, description: r.active ? "Left out of future payroll runs; history kept." : "Included in the next payroll run." });
              load();
            })
            .catch((e) => toast.add({ type: "error", title: "Not changed", description: apiError(e, "Try again.") }));
        }}
        onImport={(xs) => {
          if (!may("Create")) return toast.add({ type: "error", title: "You can't add employees" });
          HrmApi.importEmployees(xs)
            .then((r) => {
              toast.add({ title: `${count(r.created, "employee")} added`, description: "Complete each record (ID, pay, payment details) before payroll." });
              load();
            })
            .catch((e) => toast.add({ type: "error", title: "Nothing imported", description: apiError(e, "Try again.") }));
        }}
      />

      <Dialog open={!!editing} onOpenChange={(o) => !o && !busy && setEditing(null)}>
        <DialogContent className="sm:max-w-3xl">
          <DialogHeader>
            <DialogTitle>{editing?.id == null ? "Add employee" : `${d.first} ${d.last}`}</DialogTitle>
            <DialogDescription>{pay ? "Personal, employment and pay details." : "Pay, ID and payment details are kept by roles with HRM → Salaries."}</DialogDescription>
          </DialogHeader>
          {opts ? (
            <div className="space-y-4">
              <EmployeeForm
                d={d}
                onChange={setD}
                tab={tab}
                onTab={(t) => (t === "Pay and statutory" && !pay ? toast.add({ title: "Pay details are private", description: "They need Read on HRM → Salaries." }) : setTab(t))}
                problems={tried ? problems : {}}
                branches={opts.branches.map((b) => b.name)}
                types={opts.types.map((t) => t.name)}
                users={opts.users.map((u) => u.username)}
              />
              {tab === "Personal" ? (
                <Field className="sm:w-1/2">
                  <FieldLabel htmlFor="ef-gender">Gender (optional)</FieldLabel>
                  <NativeSelect id="ef-gender" value={gender} onChange={(e) => setGender(e.target.value)}>
                    <NativeSelectOption value="">Not given</NativeSelectOption>
                    <NativeSelectOption>Woman</NativeSelectOption>
                    <NativeSelectOption>Man</NativeSelectOption>
                  </NativeSelect>
                  <FieldDescription>Only for leave that applies to women or men, such as maternity and paternity leave.</FieldDescription>
                </Field>
              ) : null}
              {pay && !changePay && tab === "Pay and statutory" ? <p className="text-xs text-muted-foreground">You can see pay details but not change them.</p> : null}
            </div>
          ) : null}
          <DialogFooter>
            <Button variant="outline" disabled={busy} onClick={() => setEditing(null)}>
              Cancel
            </Button>
            <Button disabled={busy} onClick={() => void save()}>
              {busy ? <Spinner /> : null}
              {editing?.id == null ? "Add employee" : "Save"}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
