"use client";

/**
 * Leave (HR-04; plan K): the design's `LeaveBalances` for a year, booking leave checked with its `leaveProblems`
 * (working days, the balance, overlaps), leave records with cancelling, and allocations set by hand.
 */

import * as React from "react";
import { BanIcon, PencilIcon } from "lucide-react";

import { LeaveBalances, leaveProblems } from "@/components/medora/hr-more";
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, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { toast } from "@/components/ui/toast";
import { HrmApi, type LeaveBalanceRow, type LeaveRecordRow, type LeaveTypeRow } from "@/lib/api";

import { apiError } from "./employees-view";
import { Surface } from "@/components/medora/surface";
import { Loading } from "@/components/medora/loading";

const MON = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const human = (iso: string) => `${Number(iso.slice(8, 10))} ${MON[Number(iso.slice(5, 7)) - 1]} ${iso.slice(0, 4)}`;

export function LeaveView({ today, may }: { today: string; may: (a: string) => boolean }) {
  const thisYear = Number(today.slice(0, 4));
  const [year, setYear] = React.useState(thisYear);
  const [balances, setBalances] = React.useState<LeaveBalanceRow[] | null>(null);
  const [records, setRecords] = React.useState<LeaveRecordRow[]>([]);
  const [types, setTypes] = React.useState<LeaveTypeRow[]>([]);
  const [holidays, setHolidays] = React.useState<string[]>([]);
  const [r, setR] = React.useState({ employeeId: "", typeId: "", from: today, to: today, reason: "", document: "" });
  const [tried, setTried] = React.useState(false);
  const [busy, setBusy] = React.useState(false);
  const [cancelling, setCancelling] = React.useState<LeaveRecordRow | null>(null);
  const [why, setWhy] = React.useState("");
  const [alloc, setAlloc] = React.useState<null | { employeeId: string; typeId: string; days: string; reason: string }>(null);

  const load = React.useCallback(() => {
    setBalances(null);
    Promise.all([HrmApi.balances(year), HrmApi.records({ from: `${year}-01-01`, to: `${year}-12-31` }), HrmApi.leaveTypes(), HrmApi.holidays()])
      .then(([b, rec, t, h]) => {
        setBalances(b);
        setRecords(rec);
        setTypes(t);
        setHolidays(h.map((x) => x.date));
      })
      .catch((e) => (setBalances([]), toast.add({ type: "error", title: "Couldn't load leave", description: apiError(e, "Try again.") })));
  }, [year]);
  React.useEffect(() => load(), [load]);

  if (!balances)
    return (
      <Loading what="leave" columns={5} rows={6} numeric={[1, 2, 3, 4]} />
    );

  const employees = [...new Map(balances.map((b) => [b.employeeId, b.employee])).entries()];
  const theirTypes = balances.filter((b) => String(b.employeeId) === r.employeeId);
  const bal = theirTypes.find((b) => String(b.typeId) === r.typeId);
  const type = types.find((t) => String(t.id) === r.typeId);
  const name = employees.find(([id]) => String(id) === r.employeeId)?.[1] ?? "";
  const booked = records.filter((x) => x.status === "Booked" && String(x.employeeId) === r.employeeId).map((x) => ({ employee: name, from: x.from, to: x.to }));
  const check = leaveProblems({ employee: name, type: type?.name ?? "", from: r.from, to: r.to }, bal ? { employee: name, type: bal.type, allocated: bal.allocated, taken: bal.taken, booked: bal.booked } : undefined, booked, holidays);
  const problems = [...(!r.employeeId ? ["Choose the employee."] : []), ...(!r.typeId ? ["Choose the leave type."] : []), ...check.problems, ...(type?.document && !r.document.trim() ? [`${type.name} leave needs a document: say what it is.`] : [])];

  const book = async () => {
    setTried(true);
    if (problems.length) return;
    setBusy(true);
    try {
      const x = await HrmApi.book({ employeeId: Number(r.employeeId), leaveTypeId: Number(r.typeId), from: r.from, to: r.to, reason: r.reason, document: r.document });
      toast.add({ title: `${x.number} booked`, description: `${x.days} days of ${x.type.toLowerCase()} leave for ${x.employee}.` });
      setR({ ...r, reason: "", document: "" });
      setTried(false);
      load();
    } catch (e) {
      toast.add({ type: "error", title: "Leave not booked", description: apiError(e, "Try again.") });
    } finally {
      setBusy(false);
    }
  };

  const cancel = async () => {
    if (!cancelling) return;
    setBusy(true);
    try {
      await HrmApi.cancelLeave(cancelling.id, why.trim());
      toast.add({ title: `${cancelling.number} cancelled`, description: "Its days are back in the balance." });
      setCancelling(null);
      load();
    } catch (e) {
      toast.add({ type: "error", title: "Not cancelled", description: apiError(e, "Try again.") });
    } finally {
      setBusy(false);
    }
  };

  const saveAlloc = async () => {
    if (!alloc) return;
    setBusy(true);
    try {
      await HrmApi.setAllocation({ employeeId: Number(alloc.employeeId), leaveTypeId: Number(alloc.typeId), year, days: Number(alloc.days), reason: alloc.reason.trim() });
      toast.add({ title: "Allocation saved" });
      setAlloc(null);
      load();
    } catch (e) {
      toast.add({ type: "error", title: "Not saved", description: apiError(e, "Try again.") });
    } finally {
      setBusy(false);
    }
  };

  return (
    <div className="space-y-6">
      <Surface className="flex flex-wrap items-end justify-between gap-3">
        <Field className="w-32">
          <FieldLabel htmlFor="lv-year">Year</FieldLabel>
          <NativeSelect id="lv-year" value={String(year)} onChange={(e) => setYear(Number(e.target.value))}>
            {[thisYear - 1, thisYear, thisYear + 1].map((y) => (
              <NativeSelectOption key={y} value={String(y)}>
                {y}
              </NativeSelectOption>
            ))}
          </NativeSelect>
        </Field>
        {may("Update") && balances.length ? (
          <Button size="sm" variant="outline" onClick={() => setAlloc({ employeeId: "", typeId: "", days: "", reason: "" })}>
            <PencilIcon data-icon="inline-start" />
            Change an allocation…
          </Button>
        ) : null}
      </Surface>

      {!types.length ? (
        <p className="text-sm text-muted-foreground">No leave types yet. Add them on the Setup tab.</p>
      ) : balances.length ? (
        <section className="space-y-2">
          <h2 className="text-sm font-semibold">Balances, {year}</h2>
          <LeaveBalances rows={balances.map((b) => ({ employee: b.employee, type: b.overridden ? `${b.type} (set by hand)` : b.type, allocated: b.allocated, taken: b.taken, booked: b.booked }))} />
          <p className="text-xs text-muted-foreground">Allocated includes days carried over from last year, up to each type&apos;s limit. Taken is leave that has started; booked is leave still to come.</p>
        </section>
      ) : (
        <p className="text-sm text-muted-foreground">No active employees in your branch.</p>
      )}

      {may("Create") && types.length && employees.length ? (
        <section className="space-y-3 rounded-lg border border-[var(--color-border-panel)] p-4 bg-card">
          <h2 className="text-sm font-semibold">Book leave</h2>
          <div className="grid gap-3">
            <Field>
              <FieldLabel htmlFor="lv-emp">Employee</FieldLabel>
              <NativeSelect id="lv-emp" value={r.employeeId} onChange={(e) => setR({ ...r, employeeId: e.target.value, typeId: "" })}>
                <NativeSelectOption value="">Choose…</NativeSelectOption>
                {employees.map(([id, n]) => (
                  <NativeSelectOption key={id} value={String(id)}>
                    {n}
                  </NativeSelectOption>
                ))}
              </NativeSelect>
            </Field>
            <Field>
              <FieldLabel htmlFor="lv-type">Leave</FieldLabel>
              <NativeSelect id="lv-type" value={r.typeId} onChange={(e) => setR({ ...r, typeId: e.target.value })}>
                <NativeSelectOption value="">Choose…</NativeSelectOption>
                {theirTypes.map((b) => (
                  <NativeSelectOption key={b.typeId} value={String(b.typeId)}>
                    {b.type}
                  </NativeSelectOption>
                ))}
              </NativeSelect>
            </Field>
            <div className="grid gap-3 sm:grid-cols-2">
              <Field>
                <FieldLabel htmlFor="lv-from">First day</FieldLabel>
                <Input id="lv-from" type="date" value={r.from} onChange={(e) => setR({ ...r, from: e.target.value, to: r.to < e.target.value ? e.target.value : r.to })} />
              </Field>
              <Field>
                <FieldLabel htmlFor="lv-to">Last day</FieldLabel>
                <Input id="lv-to" type="date" value={r.to} min={r.from} onChange={(e) => setR({ ...r, to: e.target.value })} />
              </Field>
            </div>
          </div>
          <div className="grid gap-3">
            <Field>
              <FieldLabel htmlFor="lv-why">Reason (optional)</FieldLabel>
              <Input id="lv-why" value={r.reason} onChange={(e) => setR({ ...r, reason: e.target.value })} />
            </Field>
            {type?.document ? (
              <Field>
                <FieldLabel htmlFor="lv-doc">Document</FieldLabel>
                <Input id="lv-doc" value={r.document} placeholder="e.g. Sick note, Dr Ouma, 3 Nov" onChange={(e) => setR({ ...r, document: e.target.value })} />
              </Field>
            ) : null}
          </div>
          <p className="text-sm tabular-nums">
            {check.days} working {check.days === 1 ? "day" : "days"}
            {bal ? `; ${Math.max(0, bal.allocated - bal.taken - bal.booked)} left` : ""}
            {type && !type.paid ? " · unpaid" : ""}.
          </p>
          {tried && problems.length ? (
            <ul role="alert" className="list-disc rounded-md border border-status-error-border bg-status-error-bg py-2 pr-3 pl-8 text-sm text-status-error-text">
              {problems.map((p) => (
                <li key={p}>{p}</li>
              ))}
            </ul>
          ) : null}
          <div className="flex justify-end">
            <Button disabled={busy} onClick={() => void book()}>
              {busy ? <Spinner /> : null}
              Book leave
            </Button>
          </div>
        </section>
      ) : null}

      <section className="space-y-2">
        <h2 className="text-sm font-semibold">Leave in {year}</h2>
        {records.length ? (
          <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
            <Table>
              <TableHeader>
                <TableRow>
                  <TableHead>Leave</TableHead>
                  <TableHead>Employee</TableHead>
                  <TableHead>Dates</TableHead>
                  <TableHead className="text-right">Days</TableHead>
                  <TableHead>Reason</TableHead>
                  <TableHead>Status</TableHead>
                  <TableHead />
                </TableRow>
              </TableHeader>
              <TableBody>
                {records.map((x) => (
                  <TableRow key={x.id}>
                    <TableCell>
                      <span className="block font-mono text-xs">{x.number}</span>
                      {x.type}
                      {x.paid ? "" : " (unpaid)"}
                    </TableCell>
                    <TableCell>{x.employee}</TableCell>
                    <TableCell className="whitespace-nowrap">
                      {human(x.from)} – {human(x.to)}
                    </TableCell>
                    <TableCell className="text-right tabular-nums">{x.days}</TableCell>
                    <TableCell className="text-xs">
                      {x.reason || "–"}
                      {x.document ? <span className="block text-muted-foreground">Document: {x.document}</span> : null}
                    </TableCell>
                    <TableCell className="text-xs">{x.status === "Cancelled" ? <span className="text-muted-foreground">Cancelled: {x.cancelReason}</span> : x.from <= today ? "Taken" : "Booked"}</TableCell>
                    <TableCell>
                      {x.status === "Booked" && may("Delete") ? (
                        <Button size="sm" variant="ghost" onClick={() => (setCancelling(x), setWhy(""))}>
                          <BanIcon data-icon="inline-start" />
                          Cancel
                        </Button>
                      ) : null}
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          </div>
        ) : (
          <p className="text-sm text-muted-foreground">No leave booked in {year}.</p>
        )}
      </section>

      <Dialog open={!!cancelling} onOpenChange={(o) => !o && !busy && setCancelling(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Cancel {cancelling?.number}?</DialogTitle>
            <DialogDescription>
              {cancelling ? `${cancelling.days} days of ${cancelling.type.toLowerCase()} leave for ${cancelling.employee} go back into the balance.` : null}
            </DialogDescription>
          </DialogHeader>
          <Field>
            <FieldLabel htmlFor="lv-cwhy">Why</FieldLabel>
            <Input id="lv-cwhy" value={why} onChange={(e) => setWhy(e.target.value)} placeholder="e.g. Trip postponed" />
          </Field>
          <DialogFooter>
            <Button variant="outline" disabled={busy} onClick={() => setCancelling(null)}>
              Back
            </Button>
            <Button variant="destructive" disabled={busy || why.trim().length < 3} onClick={() => void cancel()}>
              {busy ? <Spinner /> : null}
              Cancel leave
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>

      <Dialog open={!!alloc} onOpenChange={(o) => !o && !busy && setAlloc(null)}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>Change an allocation for {year}</DialogTitle>
            <DialogDescription>Replaces the worked-out days (yearly days plus carry-over) for one employee and leave type, e.g. pro rata for someone who joined mid-year.</DialogDescription>
          </DialogHeader>
          {alloc ? (
            <div className="grid gap-3">
              <Field>
                <FieldLabel htmlFor="al-emp">Employee</FieldLabel>
                <NativeSelect id="al-emp" value={alloc.employeeId} onChange={(e) => setAlloc({ ...alloc, employeeId: e.target.value, typeId: "" })}>
                  <NativeSelectOption value="">Choose…</NativeSelectOption>
                  {employees.map(([id, n]) => (
                    <NativeSelectOption key={id} value={String(id)}>
                      {n}
                    </NativeSelectOption>
                  ))}
                </NativeSelect>
              </Field>
              <Field>
                <FieldLabel htmlFor="al-type">Leave</FieldLabel>
                <NativeSelect id="al-type" value={alloc.typeId} onChange={(e) => setAlloc({ ...alloc, typeId: e.target.value, days: String(balances.find((b) => String(b.employeeId) === alloc.employeeId && String(b.typeId) === e.target.value)?.allocated ?? "") })}>
                  <NativeSelectOption value="">Choose…</NativeSelectOption>
                  {balances
                    .filter((b) => String(b.employeeId) === alloc.employeeId)
                    .map((b) => (
                      <NativeSelectOption key={b.typeId} value={String(b.typeId)}>
                        {b.type}
                      </NativeSelectOption>
                    ))}
                </NativeSelect>
              </Field>
              <Field>
                <FieldLabel htmlFor="al-days">Days</FieldLabel>
                <Input id="al-days" inputMode="numeric" className="tabular-nums" value={alloc.days} onChange={(e) => setAlloc({ ...alloc, days: e.target.value })} />
              </Field>
              <Field>
                <FieldLabel htmlFor="al-why">Why</FieldLabel>
                <Input id="al-why" value={alloc.reason} onChange={(e) => setAlloc({ ...alloc, reason: e.target.value })} placeholder="e.g. Joined in May: pro rata" />
              </Field>
            </div>
          ) : null}
          <DialogFooter>
            <Button variant="outline" disabled={busy} onClick={() => setAlloc(null)}>
              Back
            </Button>
            <Button disabled={busy || !alloc?.employeeId || !alloc.typeId || !/^\d+$/.test(alloc.days) || alloc.reason.trim().length < 3} onClick={() => void saveAlloc()}>
              {busy ? <Spinner /> : null}
              Save allocation
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}
