"use client";

import * as React from "react";
import { BanIcon, KeyRoundIcon, PencilIcon, PlusIcon, RotateCcwIcon, SearchIcon, Trash2Icon } from "lucide-react";

import { Example } from "@/components/design-system/example";
import { Section } from "@/components/design-system/section";
import { deactivateBlock, PermissionGrid, roleDeleteBlock, roleNameProblem, UserForm, userProblems, UserStatus, type Action, type AppUser, type Grants, type PermPage, type Role, type UserDraft } from "@/components/medora/access";
import { ConfirmDialog } from "@/components/medora/confirm-dialog";
import { RecordDrawer } from "@/components/medora/drawer";
import { RowActions } from "@/components/medora/row-actions";
import { Button } from "@/components/ui/button";
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";

const CRUD: Action[] = ["Read", "Create", "Update", "Delete", "Export", "Show in menu", "All branches", "All records"];
const READ: Action[] = ["Read", "Export", "Show in menu", "All branches", "All records"];
const page = (menu: string, name: string, actions: Action[]): PermPage => ({ id: `${menu}/${name}`, menu, name, actions });

/** The PRD sitemap's 10 menus with a representative set of their pages. */
const PAGES: PermPage[] = [
  page("Dashboard", "Dashboard panels", ["Read", "Show in menu", "All branches"]),
  page("POS", "POS sales", ["Read", "Create", "Show in menu"]),
  page("POS", "Held orders", ["Read", "Delete", "Show in menu", "All records"]),
  page("POS", "Credit notes and reversals", ["Read", "Create", "Approve", "Export", "Show in menu", "All branches", "All records"]),
  page("POS", "Shifts and Z-reports", ["Read", "Create", "Export", "Show in menu", "All branches", "All records"]),
  page("Inventory", "Products", [...CRUD, "Approve"]),
  page("Inventory", "Purchase orders", [...CRUD, "Approve"]),
  page("Inventory", "Waste", [...CRUD, "Approve"]),
  page("Inventory", "Branch transfers", CRUD),
  page("Customers", "Customer list", CRUD),
  page("Customers", "Prescriptions and medical", CRUD),
  page("Suppliers", "Supplier list", CRUD),
  page("Accounts", "Receivables", [...CRUD, "Approve"]),
  page("Accounts", "General ledger", [...CRUD, "Approve"]),
  page("Accounts", "Expenses", [...CRUD, "Approve"]),
  page("HRM", "Employees", CRUD),
  page("HRM", "Payroll", [...CRUD, "Approve"]),
  page("Reports", "Sales reports", READ),
  page("Reports", "Stock reports", READ),
  page("Settings", "Users and roles", CRUD),
  page("Settings", "General settings", ["Read", "Update", "Show in menu"]),
  page("Integrations", "M-Pesa and eTIMS", ["Read", "Update", "Show in menu"]),
];

const all = (p: PermPage) => [...p.actions];
const SEED_GRANTS: Grants = {
  Pharmacist: Object.fromEntries(
    PAGES.filter((p) => ["Dashboard", "POS", "Inventory", "Customers", "Suppliers", "Reports"].includes(p.menu)).map((p) => [p.id, all(p).filter((a) => a !== "Delete" && a !== "All branches" && !(p.menu === "Inventory" && a === "Approve"))]),
  ),
  Cashier: {
    "Dashboard/Dashboard panels": ["Read", "Show in menu"],
    "POS/POS sales": ["Read", "Create", "Show in menu"],
    "POS/Held orders": ["Read", "Show in menu"],
    "POS/Shifts and Z-reports": ["Read", "Create", "Show in menu"],
    "Customers/Customer list": ["Read", "Create", "Show in menu"],
  },
  "Inventory Manager": Object.fromEntries(PAGES.filter((p) => p.menu === "Inventory" || p.menu === "Suppliers").map((p) => [p.id, all(p).filter((a) => a !== "Approve")])),
};

const SEED_ROLES: Role[] = [
  { name: "Admin", users: 2, builtIn: true },
  { name: "Pharmacist", users: 4, builtIn: true },
  { name: "Cashier", users: 7, builtIn: true },
  { name: "Inventory Manager", users: 1, builtIn: true },
  { name: "Relief pharmacist", users: 0 },
];

function RolesAndPermissions() {
  const [roles, setRoles] = React.useState(SEED_ROLES);
  const [saved, setSaved] = React.useState<Grants>(SEED_GRANTS);
  const [draft, setDraft] = React.useState<Grants>(SEED_GRANTS);
  const [role, setRole] = React.useState("Cashier");
  const [saving, setSaving] = React.useState(false);
  const [naming, setNaming] = React.useState<{ editing?: string; value: string; tried: boolean } | null>(null);
  const [deleting, setDeleting] = React.useState<Role | null>(null);
  const changed = roles.filter((r) => JSON.stringify(draft[r.name] ?? {}) !== JSON.stringify(saved[r.name] ?? {})).map((r) => r.name);
  const problem = naming ? roleNameProblem(naming.value, roles, naming.editing) : undefined;

  return (
    <Example title="Roles and permissions" description="Choose a role, then tick what it may do on each page. Ticking Create, Approve or Export also ticks Read; clearing Read clears the rest. Menus and the All column select in bulk" bare>
      <div className="grid gap-4 p-4 xl:grid-cols-[16rem_minmax(0,1fr)]">
        <div className="space-y-2">
          <div className="flex items-center justify-between">
            <h4 className="text-sm font-semibold">Roles</h4>
            <Button size="xs" variant="outline" onClick={() => setNaming({ value: "", tried: false })}>
              <PlusIcon data-icon="inline-start" />
              New role
            </Button>
          </div>
          <ul className="divide-y divide-[var(--color-border-subtle)] rounded-lg border border-[var(--color-border-panel)] text-sm">
            {roles.map((r) => (
              <li key={r.name} className={r.name === role ? "bg-[var(--color-bg-selected)]" : undefined}>
                <div className="flex items-center gap-1 px-2 py-1.5">
                  <button type="button" className="min-w-0 flex-1 cursor-pointer text-left" aria-pressed={r.name === role} onClick={() => setRole(r.name)}>
                    <span className="font-medium">{r.name}</span>
                    {changed.includes(r.name) ? <span className="ml-1.5 text-xs text-status-warning-text">Unsaved</span> : null}
                    <span className="block text-xs text-muted-foreground tabular-nums">
                      {r.users} {r.users === 1 ? "user" : "users"}
                      {r.builtIn ? " · default" : ""}
                    </span>
                  </button>
                  <RowActions
                    label={`role ${r.name}`}
                    actions={[
                      ...(r.name !== "Admin" ? [{ label: "Rename…", icon: PencilIcon, onSelect: () => setNaming({ editing: r.name, value: r.name, tried: false }) }] : []),
                      ...(r.name !== "Admin" ? [{ label: "Delete…", icon: Trash2Icon, destructive: true, onSelect: () => (roleDeleteBlock(r) ? toast.add({ type: "error", title: `Can't delete ${r.name}`, description: roleDeleteBlock(r) }) : setDeleting(r)) }] : []),
                    ]}
                  />
                </div>
              </li>
            ))}
          </ul>
          {naming ? (
            <div className="space-y-2 rounded-lg border border-[var(--color-border-panel)] p-2">
              <Input aria-label={naming.editing ? `New name for ${naming.editing}` : "Name of the new role"} value={naming.value} onChange={(e) => setNaming({ ...naming, value: e.target.value })} aria-invalid={naming.tried && !!problem} autoFocus />
              {naming.tried && problem ? <p className="text-xs text-status-error-text">{problem}</p> : null}
              <div className="flex justify-end gap-1">
                <Button size="xs" variant="ghost" onClick={() => setNaming(null)}>
                  Cancel
                </Button>
                <Button
                  size="xs"
                  onClick={() => {
                    if (problem) return setNaming({ ...naming, tried: true });
                    const name = naming.value.trim();
                    if (naming.editing) {
                      setRoles((xs) => xs.map((r) => (r.name === naming.editing ? { ...r, name } : r)));
                      const move = (g: Grants) => Object.fromEntries(Object.entries(g).map(([k, v]) => [k === naming.editing ? name : k, v]));
                      setDraft(move);
                      setSaved(move);
                      if (role === naming.editing) setRole(name);
                    } else {
                      setRoles((xs) => [...xs, { name, users: 0 }]);
                      setRole(name);
                    }
                    setNaming(null);
                  }}
                >
                  {naming.editing ? "Rename" : "Create"}
                </Button>
              </div>
            </div>
          ) : null}
        </div>

        <div className="min-w-0 space-y-3">
          <div className="flex flex-wrap items-center gap-2">
            <h4 className="text-sm font-semibold">{role}</h4>
            {role !== "Admin" ? (
              <NativeSelect
                size="sm"
                aria-label={`Copy permissions to ${role} from another role`}
                value=""
                className="ml-auto"
                onChange={(e) => {
                  const from = e.target.value;
                  if (!from) return;
                  setDraft((d) => ({ ...d, [role]: from === "Admin" ? Object.fromEntries(PAGES.map((p) => [p.id, all(p)])) : JSON.parse(JSON.stringify(d[from] ?? {})) }));
                  toast.add({ title: `Copied ${from}'s permissions to ${role}`, description: "Adjust, then save." });
                }}
              >
                <NativeSelectOption value="">Copy from role…</NativeSelectOption>
                {roles
                  .filter((r) => r.name !== role)
                  .map((r) => (
                    <NativeSelectOption key={r.name}>{r.name}</NativeSelectOption>
                  ))}
              </NativeSelect>
            ) : null}
          </div>
          <div className="max-h-[520px] overflow-y-auto">
            <PermissionGrid pages={PAGES} role={role} locked={role === "Admin"} grants={draft[role] ?? {}} onChange={(g) => setDraft((d) => ({ ...d, [role]: g }))} />
          </div>
          {changed.length ? (
            <div role="region" aria-label="Unsaved changes" className="flex flex-wrap items-center gap-3 border-t border-[var(--color-border-subtle)] pt-3">
              <p className="mr-auto text-sm">
                <span className="font-medium">Unsaved changes</span> <span className="text-muted-foreground">for {changed.join(", ")}. Users get them at their next request; the API enforces them.</span>
              </p>
              <Button variant="ghost" disabled={saving} onClick={() => setDraft(saved)}>
                Discard
              </Button>
              <Button
                disabled={saving}
                onClick={() => {
                  setSaving(true);
                  setTimeout(() => {
                    setSaving(false);
                    setSaved(draft);
                    toast.add({ title: "Permissions saved", description: changed.join(", ") });
                  }, 700);
                }}
              >
                {saving ? <Spinner /> : null}
                {saving ? "Saving…" : "Save permissions"}
              </Button>
            </div>
          ) : null}
        </div>
      </div>
      {deleting ? (
        <ConfirmDialog
          open
          onOpenChange={(o) => !o && setDeleting(null)}
          level="A"
          title={`Delete the ${deleting.name} role?`}
          consequences={["No users have it.", "Its permissions are removed. The activity log keeps the record of who had it."]}
          actionLabel="Delete role"
          onConfirm={() => {
            setRoles((xs) => xs.filter((r) => r.name !== deleting.name));
            if (role === deleting.name) setRole("Cashier");
            setDeleting(null);
          }}
        />
      ) : null}
    </Example>
  );
}

/* --------------------------------------- Users --------------------------------------- */

const BRANCHES = ["Westlands Main", "Kilimani", "Nyali", "Milimani"];
const SEED_USERS: AppUser[] = [
  { id: 1, username: "grace.w", first: "Grace", last: "Wanjiru", phone: "0712 000 111", email: "grace@medora.co.ke", role: "Admin", branch: "Westlands Main", location: "", login: "Password", status: "Active", lastSeen: "Now" },
  { id: 2, username: "kevin.m", first: "Kevin", last: "Mutua", phone: "0722 000 214", email: "", role: "Cashier", branch: "Westlands Main", location: "Front shop", login: "Passcode", status: "Active", lastSeen: "14:32" },
  { id: 3, username: "peter.k", first: "Peter", last: "Kamau", phone: "", email: "peter@medora.co.ke", role: "Pharmacist", branch: "Kilimani", location: "", login: "Password", status: "Active", lastSeen: "Yesterday" },
  { id: 4, username: "faith.n", first: "Faith", last: "Njeri", phone: "0110 222 333", email: "", role: "Cashier", branch: "Nyali", location: "", login: "Passcode", status: "Deactivated", lastSeen: "3 Sep" },
];
const ME = 1;
const blankUser: UserDraft = { username: "", first: "", last: "", phone: "", email: "", role: "", branch: "", location: "", login: "Password", secret: "" };

function UsersDemo() {
  const [users, setUsers] = React.useState(SEED_USERS);
  const [q, setQ] = React.useState("");
  const [fRole, setFRole] = React.useState("");
  const [fStatus, setFStatus] = React.useState("Active");
  const [form, setForm] = React.useState<{ draft: UserDraft; id?: number; tried: boolean } | null>(null);
  const [resetting, setResetting] = React.useState<AppUser | null>(null);
  const shown = users.filter((u) => (!fRole || u.role === fRole) && (!fStatus || u.status === fStatus) && (!q.trim() || `${u.first} ${u.last} ${u.username}`.toLowerCase().includes(q.trim().toLowerCase())));
  const problems = form ? userProblems(form.draft, users, form.id) : {};
  const set = (id: number, patch: Partial<AppUser>) => setUsers((xs) => xs.map((x) => (x.id === id ? { ...x, ...patch } : x)));

  return (
    <Example title="Users" description="Search and filter; edit, reset a password, deactivate or reactivate. Users are never deleted: their sales and the activity log keep their name. You can't deactivate yourself or the last active Admin" bare>
      <div className="flex flex-wrap items-center gap-2 border-b border-border px-4 py-3">
        <div className="relative w-full sm:w-56">
          <SearchIcon className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" aria-hidden />
          <Input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Find a user" aria-label="Find a user" className="pl-8" />
        </div>
        <NativeSelect aria-label="Role" value={fRole} onChange={(e) => setFRole(e.target.value)}>
          <NativeSelectOption value="">All roles</NativeSelectOption>
          {["Admin", "Pharmacist", "Cashier", "Inventory Manager"].map((r) => (
            <NativeSelectOption key={r}>{r}</NativeSelectOption>
          ))}
        </NativeSelect>
        <NativeSelect aria-label="Status" value={fStatus} onChange={(e) => setFStatus(e.target.value)}>
          <NativeSelectOption value="">Any status</NativeSelectOption>
          <NativeSelectOption>Active</NativeSelectOption>
          <NativeSelectOption>Deactivated</NativeSelectOption>
        </NativeSelect>
        <Button size="sm" className="ml-auto" onClick={() => setForm({ draft: blankUser, tried: false })}>
          <PlusIcon data-icon="inline-start" />
          New user
        </Button>
      </div>
      <div className="overflow-x-auto">
        <table className="w-full border-collapse text-sm">
          <thead className="bg-[var(--color-bg-surface-subtle)] text-left text-xs text-muted-foreground">
            <tr>
              {["Name", "Role and branch", "Signs in with", "Status", ""].map((h, i) => (
                <th key={i} scope="col" className="px-3 py-2 font-medium">
                  {h || <span className="sr-only">Actions</span>}
                </th>
              ))}
            </tr>
          </thead>
          <tbody className="divide-y divide-[var(--color-border-subtle)]">
            {shown.map((u) => {
              const block = deactivateBlock(u, users, ME);
              return (
                <tr key={u.id}>
                  <th scope="row" className="px-3 py-2 text-left font-medium">
                    {u.first} {u.last}
                    {u.id === ME ? <span className="ml-1.5 text-xs font-normal text-muted-foreground">You</span> : null}
                    <span className="block font-mono text-xs font-normal text-muted-foreground">{u.username}</span>
                  </th>
                  <td className="px-3 py-2">
                    {u.role}
                    <span className="block text-xs text-muted-foreground">
                      {u.branch}
                      {u.location ? `, ${u.location}` : ""}
                    </span>
                  </td>
                  <td className="px-3 py-2">{u.login}</td>
                  <td className="px-3 py-2">
                    <UserStatus u={u} />
                    {u.lastSeen ? <span className="block text-xs text-muted-foreground">Last seen {u.lastSeen}</span> : null}
                  </td>
                  <td className="px-2 py-2 text-right">
                    <RowActions
                      label={`${u.first} ${u.last}`}
                      actions={[
                        { label: "Edit", icon: PencilIcon, onSelect: () => setForm({ draft: { ...u, secret: "" }, id: u.id, tried: false }) },
                        ...(u.status === "Active" ? [{ label: `Reset ${u.login.toLowerCase()}…`, icon: KeyRoundIcon, onSelect: () => setResetting(u) }] : []),
                        u.status === "Active"
                          ? { label: "Deactivate", icon: BanIcon, destructive: true, onSelect: () => (block ? toast.add({ type: "error", title: `Can't deactivate ${u.first}`, description: block }) : (set(u.id, { status: "Deactivated" }), toast.add({ title: `${u.first} ${u.last} deactivated`, description: "They're signed out everywhere." }))) }
                          : { label: "Reactivate", icon: RotateCcwIcon, onSelect: () => set(u.id, { status: "Active" }) },
                      ]}
                    />
                  </td>
                </tr>
              );
            })}
            {!shown.length ? (
              <tr>
                <td colSpan={5} className="px-3 py-8 text-center text-sm text-muted-foreground">
                  No users match these filters.
                </td>
              </tr>
            ) : null}
          </tbody>
        </table>
      </div>
      {form ? (
        <RecordDrawer
          open
          onOpenChange={(o) => !o && setForm(null)}
          variant="edit"
          width="lg"
          dirty={JSON.stringify(form.draft) !== JSON.stringify(form.id ? { ...users.find((u) => u.id === form.id)!, secret: "" } : blankUser)}
          title={form.id ? `Edit ${form.draft.first} ${form.draft.last}` : "New user"}
          footer={
            <>
              <Button variant="outline" onClick={() => setForm(null)}>
                Cancel
              </Button>
              <Button
                onClick={() => {
                  if (Object.keys(problems).length) return setForm({ ...form, tried: true });
                  const { secret: _secret, ...rest } = form.draft;
                  void _secret;
                  if (form.id) set(form.id, rest);
                  else setUsers((xs) => [...xs, { ...rest, id: xs.length + 1, status: "Active" }]);
                  toast.add({ title: form.id ? "User saved" : `${rest.first} ${rest.last} added`, description: form.id ? undefined : `Signs in as ${rest.username}; asked to change the ${rest.login.toLowerCase()} at first sign-in.` });
                  setForm(null);
                }}
              >
                {form.id ? "Save user" : "Create user"}
              </Button>
            </>
          }
        >
          <UserForm draft={form.draft} onChange={(d) => setForm({ ...form, draft: d })} roles={["Admin", "Pharmacist", "Cashier", "Inventory Manager", "Relief pharmacist"]} branches={BRANCHES} locations={["Dispensary", "Store room", "Front shop"]} problems={form.tried ? problems : {}} editing={!!form.id} />
        </RecordDrawer>
      ) : null}
      {resetting ? (
        <ConfirmDialog
          open
          onOpenChange={(o) => !o && setResetting(null)}
          level="A"
          title={`Reset ${resetting.first}'s ${resetting.login.toLowerCase()}?`}
          consequences={[resetting.login === "Password" ? (resetting.email ? `A one-time link goes to ${resetting.email}.` : "A temporary password is shown once, for you to give them in person.") : "A new 6-digit passcode is shown once, for you to give them in person.", "They're signed out everywhere and must set a new one at next sign-in."]}
          actionLabel="Reset"
          onConfirm={() => {
            toast.add({ title: `${resetting.first}'s ${resetting.login.toLowerCase()} reset` });
            setResetting(null);
          }}
        />
      ) : null}
    </Example>
  );
}

export function AccessSection() {
  return (
    <Section
      id="access"
      title="Users, roles and permissions"
      description="Who can do what. Roles group permissions; every user has one role and one branch. The grid sets Read, Create, Update, Delete, Approve, Export, Show in menu, All branches and All records per page, and the API enforces exactly what it says: hiding a menu is never the control."
    >
      <RolesAndPermissions />
      <UsersDemo />
    </Section>
  );
}
