"use client";

import * as React from "react";
import { LayoutDashboardIcon, LockIcon } from "lucide-react";

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

/*
 * Dashboard panels as permissions (PRD DB-14, Settings 9.6). Each panel is switched on or off per role; the
 * dashboard renders only the panels the user's role allows and closes up the gaps. The API must enforce the
 * same rule (a hidden panel's data is not returned), so this is never just a visual filter.
 */

export type PanelGroup = "Headline figures" | "Money" | "Sales" | "Stock";

export type DashboardPanel = {
  id: string;
  title: string;
  /** What the panel shows, in the permission grid. */
  description: string;
  group: PanelGroup;
  /** Shows money owed, profit or costs: worth a second thought before giving it to a role. */
  sensitive?: boolean;
  /** Grid span at wide widths (out of 12). */
  span?: 3 | 4 | 6 | 8 | 12;
  /** DB requirement it implements. */
  ref?: string;
};

/** Role → set of panel ids. Admin is always every panel, so a pharmacy can't hide its own figures from itself. */
export type PanelGrants = Record<string, string[]>;

const GROUPS: PanelGroup[] = ["Headline figures", "Money", "Sales", "Stock"];

/**
 * Permission grid: panels down (grouped), roles across. Each role column has an all-or-nothing checkbox
 * (mixed when partly on) and "Copy from" another role. Sensitive panels say so. Admin is fixed on.
 */
export function PanelPermissions({ panels, roles, grants, onChange, lockedRole = "Admin" }: { panels: DashboardPanel[]; roles: string[]; grants: PanelGrants; onChange: (g: PanelGrants) => void; lockedRole?: string }) {
  const has = (role: string, id: string) => role === lockedRole || (grants[role] ?? []).includes(id);
  const set = (role: string, ids: string[]) => onChange({ ...grants, [role]: ids });
  const toggle = (role: string, id: string, on: boolean) => set(role, on ? [...new Set([...(grants[role] ?? []), id])] : (grants[role] ?? []).filter((x) => x !== id));
  const editable = roles.filter((r) => r !== lockedRole);

  return (
    <div className="overflow-x-auto rounded-lg border border-[var(--color-border-panel)] bg-card">
      <table className="w-full border-collapse text-sm">
        <caption className="sr-only">Dashboard panels each role can see</caption>
        <thead className="bg-[var(--color-bg-surface-subtle)] text-xs text-muted-foreground">
          <tr>
            <th scope="col" className="min-w-60 px-3 py-2 text-left font-medium">
              Panel
            </th>
            {roles.map((r) => {
              const on = panels.filter((p) => has(r, p.id)).length;
              const all = on === panels.length;
              return (
                <th key={r} scope="col" className="w-32 px-2 py-2 text-center align-bottom font-medium">
                  <span className="block text-foreground">{r}</span>
                  <span className="mt-1 flex items-center justify-center gap-1.5 font-normal">
                    {r === lockedRole ? (
                      <span className="inline-flex items-center gap-1">
                        <LockIcon className="size-3" aria-hidden /> All, fixed
                      </span>
                    ) : (
                      <>
                        <Checkbox
                          aria-label={`All panels for ${r}`}
                          checked={all}
                          indeterminate={on > 0 && !all}
                          onCheckedChange={(v) => set(r, v ? panels.map((p) => p.id) : [])}
                        />
                        <span className="tabular-nums">
                          {on} of {panels.length}
                        </span>
                      </>
                    )}
                  </span>
                </th>
              );
            })}
          </tr>
          {editable.length > 1 ? (
            <tr className="border-t border-[var(--color-border-subtle)]">
              <th scope="row" className="px-3 py-1.5 text-left font-normal">
                Copy panels from
              </th>
              {roles.map((r) => (
                <td key={r} className="px-2 py-1.5 text-center">
                  {r === lockedRole ? null : (
                    <NativeSelect
                      size="sm"
                      aria-label={`Copy panels to ${r} from another role`}
                      value=""
                      onChange={(e) => {
                        const from = e.target.value;
                        if (from) set(r, from === lockedRole ? panels.map((p) => p.id) : [...(grants[from] ?? [])]);
                      }}
                    >
                      <NativeSelectOption value="">Choose…</NativeSelectOption>
                      {roles
                        .filter((x) => x !== r)
                        .map((x) => (
                          <NativeSelectOption key={x} value={x}>
                            {x}
                          </NativeSelectOption>
                        ))}
                    </NativeSelect>
                  )}
                </td>
              ))}
            </tr>
          ) : null}
        </thead>
        {GROUPS.map((g) => {
          const list = panels.filter((p) => p.group === g);
          if (!list.length) return null;
          return (
            <tbody key={g} className="divide-y divide-[var(--color-border-subtle)] border-t-2 border-[var(--color-border-panel)]">
              <tr className="bg-muted/30">
                <th scope="rowgroup" colSpan={roles.length + 1} className="px-3 py-1.5 text-left text-xs font-semibold tracking-wide uppercase">
                  {g}
                </th>
              </tr>
              {list.map((p) => (
                <tr key={p.id}>
                  <th scope="row" className="px-3 py-2 text-left font-normal">
                    <span className="font-medium">{p.title}</span>
                    {p.sensitive ? <span className="ml-1.5 text-xs font-medium text-status-warning-text">Sensitive</span> : null}
                    <span className="block text-xs text-muted-foreground">{p.description}</span>
                  </th>
                  {roles.map((r) => (
                    <td key={r} className="px-2 py-2 text-center">
                      <Checkbox aria-label={`${r} sees ${p.title}`} checked={has(r, p.id)} disabled={r === lockedRole} onCheckedChange={(v) => toggle(r, p.id, !!v)} />
                    </td>
                  ))}
                </tr>
              ))}
            </tbody>
          );
        })}
      </table>
    </div>
  );
}

/**
 * The dashboard body: renders only allowed panels, in the registry's order, on a 12-column grid that closes
 * up. A role with no panels gets an explanation rather than an empty page (the launcher still shows above).
 */
/** Column classes written out, so Tailwind sees each one. */
const SPAN: Record<number, string> = { 3: "@3xl:col-span-3", 4: "@3xl:col-span-4", 5: "@3xl:col-span-5", 6: "@3xl:col-span-6", 7: "@3xl:col-span-7", 8: "@3xl:col-span-8", 9: "@3xl:col-span-9", 10: "@3xl:col-span-10", 11: "@3xl:col-span-11", 12: "@3xl:col-span-12" };

/**
 * Template 11.4 reflow: panels fill rows of 12 in order; a row left short (its partner hidden by permission or by the
 * branch filter) gives the rest to its last panel, so a 6 + 6 row with one hidden becomes 12 and nothing stands alone.
 */
export function reflow(spans: number[]): number[] {
  const out = [...spans];
  let start = 0;
  let used = 0;
  const close = (end: number) => {
    if (end > start && used < 12) out[end - 1] += 12 - used;
  };
  spans.forEach((s, i) => {
    if (used + s > 12) {
      close(i);
      start = i;
      used = 0;
    }
    used += s;
  });
  close(spans.length);
  return out;
}

export function DashboardGrid({ panels, allowed, render, role }: { panels: DashboardPanel[]; allowed: string[]; render: (p: DashboardPanel) => React.ReactNode; role: string }) {
  const shown = panels.filter((p) => allowed.includes(p.id));
  if (!shown.length) {
    return (
      <div className="flex flex-col items-center gap-2 rounded-lg border border-dashed border-border px-6 py-10 text-center">
        <LayoutDashboardIcon className="size-6 text-muted-foreground" aria-hidden />
        <p className="text-sm font-medium">No dashboard panels for {role}</p>
        <p className="max-w-sm text-sm text-muted-foreground">An administrator chooses which figures each role sees, in Settings, Assign roles. The menus above still work.</p>
      </div>
    );
  }
  // Template 11.4, top to bottom: the stock alert strip (DB-10), the headline figures as one row of equal cards
  // (auto-rows-fr, so five never leave one stranded at another height), then panels in rows of 12 (8 + 4, 6 + 6).
  const strip = shown.filter((p) => p.id === "stock-alerts");
  const headline = shown.filter((p) => p.group === "Headline figures");
  const rest = shown.filter((p) => p.group !== "Headline figures" && p.id !== "stock-alerts");
  const spans = reflow(rest.map((p) => p.span ?? 6));
  return (
    <div className="@container space-y-6">
      {strip.map((p) => (
        <section key={p.id} aria-label={p.title}>
          {render(p)}
        </section>
      ))}
      {headline.length ? (
        <div className="grid auto-rows-fr grid-cols-1 gap-6 @md:grid-cols-2 @2xl:grid-cols-3 @5xl:grid-cols-5">
          {headline.map((p) => (
            <section key={p.id} aria-label={p.title} className="min-w-0">
              {render(p)}
            </section>
          ))}
        </div>
      ) : null}
      <div className="grid grid-cols-1 gap-6 @3xl:grid-cols-12">
        {rest.map((p, i) => (
          <section key={p.id} aria-label={p.title} className={cn("min-w-0", SPAN[spans[i]])}>
            {render(p)}
          </section>
        ))}
      </div>
    </div>
  );
}
