"use client";

/**
 * A branch's locations (ST-04) and money accounts (ST-02), from Settings → Branches (plan C, DECISIONS.md 26 Oct 2026).
 * The design's `LocationsEditor` and `MoneyLinks` edit a copy; Save turns the difference into API calls, as the
 * other Settings lists do. "In transit" belongs to branch transfers, so it is listed apart and never edited.
 */

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

import { LocationsEditor, MoneyLinks, moneyLinkProblems, type MoneyLink, type PlaceLocation } from "@/components/medora/admin-more";
import { RecordDrawer } from "@/components/medora/drawer";
import { Button } from "@/components/ui/button";
import { Field, FieldDescription, 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 { toast } from "@/components/ui/toast";
import { LocationsApi, MoneyAccountsApi, type LocationRow, type MoneyLinksRow } from "@/lib/api";
import { Loading } from "@/components/medora/loading";

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 toPlace = (l: LocationRow): PlaceLocation => ({ id: String(l.id), name: l.name, kind: l.physical ? "Physical" : "Virtual", inPos: l.showInPos, units: l.units, details: l.details ?? "" });

/** ST-04: where a branch keeps stock and which places the till sells from. */
export function LocationsDrawer({ branch, onClose }: { branch: { id: number; name: string }; onClose: () => void }) {
  const [rows, setRows] = React.useState<LocationRow[] | null>(null);
  const [draft, setDraft] = React.useState<PlaceLocation[]>([]);
  const [add, setAdd] = React.useState({ name: "", kind: "Physical" as "Physical" | "Virtual" });
  const [saving, setSaving] = React.useState(false);

  const load = React.useCallback(async () => {
    const r = await LocationsApi.list(branch.id);
    setRows(r);
    setDraft(r.filter((l) => !l.system).map(toPlace));
  }, [branch.id]);
  React.useEffect(() => {
    load().catch((e) => {
      setRows([]);
      toast.add({ type: "error", title: "Couldn't load locations", description: apiError(e, "Try again.") });
    });
  }, [load]);

  const transit = rows?.filter((l) => l.system) ?? [];
  const before = new Map((rows ?? []).filter((l) => !l.system).map((l) => [String(l.id), toPlace(l)]));
  const dirty = JSON.stringify([...before.values()]) !== JSON.stringify(draft);
  const nameTaken = (n: string) => draft.some((l) => l.name.trim().toLowerCase() === n.trim().toLowerCase()) || n.trim().toLowerCase() === "in transit";

  const save = async () => {
    setSaving(true);
    try {
      // Deletes first, so a name freed by a delete can be reused by an addition
      for (const [id, l] of before) if (!draft.some((d) => d.id === id)) await LocationsApi.remove(Number(id)).catch((e) => Promise.reject(new Error(`${l.name}: ${apiError(e, "not deleted")}`)));
      for (const d of draft) {
        const was = before.get(d.id);
        if (!was) await LocationsApi.create({ branchId: branch.id, name: d.name, physical: d.kind === "Physical", showInPos: d.inPos, details: d.details });
        else if (was.inPos !== d.inPos || (was.details ?? "") !== (d.details ?? "")) await LocationsApi.update(Number(d.id), { showInPos: d.inPos, details: d.details ?? "" });
      }
      toast.add({ title: `${branch.name}: locations saved` });
      onClose();
    } catch (e) {
      toast.add({ type: "error", title: "Locations not all saved", description: e instanceof Error && !("response" in e) ? e.message : apiError(e, "Try again.") });
      await load().catch(() => undefined);
    } finally {
      setSaving(false);
    }
  };

  return (
    <RecordDrawer
      open
      onOpenChange={(o) => !o && onClose()}
      variant="edit"
      width="lg"
      saving={saving}
      dirty={dirty}
      title={`Locations: ${branch.name}`}
      subtitle="Physical places (the shop floor, a store room) or virtual ones (online orders). The till sells only from locations marked so, and new stock arrives at the first."
      footer={
        <>
          <Button variant="outline" disabled={saving} onClick={onClose}>
            Cancel
          </Button>
          <Button disabled={saving || !dirty} onClick={() => void save()}>
            {saving ? <Spinner /> : null}
            Save locations
          </Button>
        </>
      }
    >
      {rows === null ? (
        <Loading what="locations" shape="list" rows={3} />
      ) : (
        <div className="space-y-4">
          <LocationsEditor locations={draft} onChange={setDraft} onBlocked={(m) => toast.add({ type: "error", title: "Can't do that", description: m })} />
          <div className="flex flex-wrap items-end gap-2">
            <Field className="min-w-48 flex-1">
              <FieldLabel htmlFor="loc-name">New location</FieldLabel>
              <Input id="loc-name" placeholder="e.g. Store room" value={add.name} onChange={(e) => setAdd({ ...add, name: e.target.value })} />
            </Field>
            <Field className="w-36">
              <FieldLabel htmlFor="loc-kind">Kind</FieldLabel>
              <NativeSelect id="loc-kind" value={add.kind} onChange={(e) => setAdd({ ...add, kind: e.target.value as "Physical" | "Virtual" })}>
                <NativeSelectOption value="Physical">Physical</NativeSelectOption>
                <NativeSelectOption value="Virtual">Virtual</NativeSelectOption>
              </NativeSelect>
            </Field>
            <Button
              variant="outline"
              disabled={add.name.trim().length < 2 || nameTaken(add.name)}
              onClick={() => {
                setDraft([...draft, { id: `new-${Date.now()}`, name: add.name.trim(), kind: add.kind, inPos: add.kind === "Physical", units: 0, details: "" }]);
                setAdd({ name: "", kind: "Physical" });
              }}
            >
              <PlusIcon data-icon="inline-start" />
              Add
            </Button>
          </div>
          {add.name.trim() && nameTaken(add.name) ? <p className="text-xs text-status-error-text">This branch already has a location called {add.name.trim()}.</p> : null}
          {transit.length ? (
            <p className="text-xs text-muted-foreground">
              {transit.map((t) => `${t.name}: ${t.units.toLocaleString("en-US")} units`).join(", ")}. Stock on its way from another branch; it is managed by branch transfers.
            </p>
          ) : null}
        </div>
      )}
    </RecordDrawer>
  );
}

/** ST-02: which ledger accounts are this branch's cash drawer, M-Pesa tills, card terminals and banks. */
export function MoneyAccountsDrawer({ branch, onClose }: { branch: { id: number; name: string }; onClose: () => void }) {
  const [data, setData] = React.useState<MoneyLinksRow | null>(null);
  const [links, setLinks] = React.useState<MoneyLink[]>([]);
  const [saving, setSaving] = React.useState(false);
  const [tried, setTried] = React.useState(false);

  React.useEffect(() => {
    MoneyAccountsApi.get(branch.id)
      .then((d) => {
        setData(d);
        setLinks(d.links.map((l) => ({ role: l.role as MoneyLink["role"], account: l.account })));
      })
      .catch((e) => toast.add({ type: "error", title: "Couldn't load money accounts", description: apiError(e, "Try again.") }));
  }, [branch.id]);

  const label = (a: { code: string; name: string }) => `${a.code} ${a.name}`;
  const taken = Object.fromEntries((data?.accounts ?? []).filter((a) => a.takenBy).map((a) => [label(a), a.takenBy!]));
  const problems = moneyLinkProblems(links, taken);
  const dirty = !!data && JSON.stringify(data.links.map((l) => [l.role, l.account])) !== JSON.stringify(links.map((l) => [l.role, l.account]));

  const save = async () => {
    setTried(true);
    if (problems.length || !data) return;
    const byLabel = new Map(data.accounts.map((a) => [label(a), a.id]));
    const body = links.filter((l) => l.account).map((l) => ({ role: l.role, accountId: byLabel.get(l.account)! }));
    setSaving(true);
    try {
      await MoneyAccountsApi.save(branch.id, body);
      toast.add({ title: `${branch.name}: money accounts saved`, description: "Sales and drops from now on post to these accounts." });
      onClose();
    } catch (e) {
      toast.add({ type: "error", title: "Money accounts not saved", description: apiError(e, "Try again.") });
    } finally {
      setSaving(false);
    }
  };

  return (
    <RecordDrawer
      open
      onOpenChange={(o) => !o && onClose()}
      variant="edit"
      width="lg"
      saving={saving}
      dirty={dirty}
      title={`Money accounts: ${branch.name}`}
      subtitle="The ledger accounts this branch's takings go to. An account belongs to one branch. Add accounts in Accounts → General ledger (1001–1099)."
      footer={
        <>
          <Button variant="outline" disabled={saving} onClick={onClose}>
            Cancel
          </Button>
          <Button disabled={saving || !dirty} onClick={() => void save()}>
            {saving ? <Spinner /> : null}
            Save money accounts
          </Button>
        </>
      }
    >
      {!data ? (
        <Loading what="money accounts" shape="list" rows={4} />
      ) : (
        <div className="space-y-3">
          <MoneyLinks links={links} onChange={setLinks} accounts={data.accounts.map((a) => ({ code: a.code, name: a.name }))} />
          {(tried || links.some((l) => l.account && taken[l.account])) && problems.length ? (
            <ul className="space-y-1 rounded-md bg-status-error-bg px-3 py-2 text-sm text-status-error-text">
              {problems.map((p) => (
                <li key={p}>{p}</li>
              ))}
            </ul>
          ) : null}
          <FieldDescription>Sales, refunds and payments post to the first account of each kind; a cash or M-Pesa drop can go to any of the banks, and cash to the branch safe. Earlier postings stay where they are.</FieldDescription>
        </div>
      )}
    </RecordDrawer>
  );
}
