"use client";

import { toastApiError } from "@/lib/toast-api-error";
import { useMemo, useState } from "react";
import { useRouter } from "next/navigation";
import { Pencil, Plus, Trash2 } from "lucide-react";
import { toast } from "sonner";

import type {
  AccessGroupRow,
  CatalogRoute,
  PortalAppCatalogGroup,
  RouteCatalogGroup,
} from "@/app/customer/_lib/admin/access-types";
import { customerCsrfHeader } from "@/lib/customer-csrf.client";
import { ErrorBanner } from "@/components/shared/error-banner";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetFooter,
  SheetHeader,
  SheetTitle,
} from "@/components/ui/sheet";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { cn } from "@/lib/utils";

type GroupsPageClientProps = {
  tenant: string;
  initialGroups: AccessGroupRow[];
  appCatalog: PortalAppCatalogGroup[];
  initialErrorMessage: string | null;
  canManageRoles?: boolean;
};

type GroupFormState = {
  name: string;
  routes: string[];
};

type ModulePermissionRow = {
  key: string;
  label: string;
  description: string;
  navGroupKey: string | null;
  navGroupLabel: string | null;
  navSortOrder: number | null;
  view?: CatalogRoute;
  create?: CatalogRoute;
  update?: CatalogRoute;
  delete?: CatalogRoute;
  extras: CatalogRoute[];
};

type PermissionListItem =
  | { type: "module"; row: ModulePermissionRow }
  | { type: "nav_group"; key: string; label: string; rows: ModulePermissionRow[] };

const CUD_ACTIONS = ["create", "update", "delete"] as const;

function parseRouteAction(route: string): { module: string; action: string } {
  const [module = route, action = "index"] = route.split("/", 2);
  return { module, action };
}

function routeNavGroup(route: CatalogRoute): { key: string | null; label: string | null } {
  const key = route.nav_group_key?.trim() || null;
  // Roles UI uses legacy Cash naming; corp sidebar keeps Fx Currency.
  const label =
    route.nav_group_roles_label?.trim() || route.nav_group_label?.trim() || key;
  return { key, label };
}

function buildModuleRows(section: RouteCatalogGroup): ModulePermissionRow[] {
  const byModule = new Map<string, ModulePermissionRow>();
  const leftovers: CatalogRoute[] = [];

  for (const route of section.routes) {
    const { module, action } = parseRouteAction(route.route);
    const isCud = (CUD_ACTIONS as readonly string[]).includes(action);
    const isView = action === "index" || action === "view";
    const nav = routeNavGroup(route);

    if (!isCud && !isView) {
      leftovers.push(route);
      continue;
    }

    const existing = byModule.get(module) ?? {
      key: module,
      label: route.name.replace(/^(Create|Update|Delete)\s+/i, "").trim() || module,
      description: isView ? route.description : "",
      navGroupKey: null,
      navGroupLabel: null,
      navSortOrder: null,
      extras: [],
    };

    if (isView) {
      existing.view = route;
      existing.label = route.name;
      existing.description = route.description;
      existing.navGroupKey = nav.key;
      existing.navGroupLabel = nav.label;
      existing.navSortOrder =
        typeof route.nav_sort_order === "number" ? route.nav_sort_order : existing.navSortOrder;
    } else if (action === "create") {
      existing.create = route;
    } else if (action === "update") {
      existing.update = route;
    } else if (action === "delete") {
      existing.delete = route;
    }

    if (!existing.navGroupKey && nav.key) {
      existing.navGroupKey = nav.key;
      existing.navGroupLabel = nav.label;
    }
    if (
      existing.navSortOrder === null &&
      typeof route.nav_sort_order === "number"
    ) {
      existing.navSortOrder = route.nav_sort_order;
    }

    byModule.set(module, existing);
  }

  const rows = [...byModule.values()];
  for (const route of leftovers) {
    const nav = routeNavGroup(route);
    rows.push({
      key: route.route,
      label: route.name,
      description: route.description,
      navGroupKey: nav.key,
      navGroupLabel: nav.label,
      navSortOrder: typeof route.nav_sort_order === "number" ? route.nav_sort_order : null,
      extras: [route],
    });
  }

  // Match Transactions sidebar order (nav_sort_order) when available.
  return rows
    .map((row, index) => ({ row, index }))
    .sort((a, b) => {
      const aOrder = a.row.navSortOrder ?? Number.MAX_SAFE_INTEGER;
      const bOrder = b.row.navSortOrder ?? Number.MAX_SAFE_INTEGER;
      if (aOrder !== bOrder) return aOrder - bOrder;
      return a.index - b.index;
    })
    .map(({ row }) => row);
}

/** Nest sidebar nav groups (e.g. Fx Currency) so Roles matches Transactions IA. */
function partitionPermissionItems(rows: ModulePermissionRow[]): PermissionListItem[] {
  const emittedGroups = new Set<string>();
  const items: PermissionListItem[] = [];

  for (const row of rows) {
    const groupKey = row.navGroupKey;
    if (!groupKey) {
      items.push({ type: "module", row });
      continue;
    }
    if (emittedGroups.has(groupKey)) {
      continue;
    }
    emittedGroups.add(groupKey);
    const groupRows = rows.filter((candidate) => candidate.navGroupKey === groupKey);
    items.push({
      type: "nav_group",
      key: groupKey,
      label: row.navGroupLabel || groupKey,
      rows: groupRows,
    });
  }

  return items;
}

function actionButtonsForRow(row: ModulePermissionRow): Array<{ key: string; label: string; route: CatalogRoute }> {
  const buttons: Array<{ key: string; label: string; route: CatalogRoute }> = [];
  if (row.view) buttons.push({ key: "view", label: "View", route: row.view });
  if (row.create) buttons.push({ key: "create", label: "Create", route: row.create });
  if (row.update) buttons.push({ key: "update", label: "Update", route: row.update });
  if (row.delete) buttons.push({ key: "delete", label: "Delete", route: row.delete });
  for (const extra of row.extras) {
    buttons.push({ key: extra.route, label: extra.name, route: extra });
  }
  return buttons;
}

export function GroupsPageClient({
  tenant,
  initialGroups,
  appCatalog,
  initialErrorMessage,
  canManageRoles = false,
}: GroupsPageClientProps) {
  const router = useRouter();
  const [groups, setGroups] = useState(initialGroups);
  const [sheetOpen, setSheetOpen] = useState(false);
  const [editingGroup, setEditingGroup] = useState<AccessGroupRow | null>(null);
  const [form, setForm] = useState<GroupFormState>({ name: "", routes: [] });
  const [saving, setSaving] = useState(false);

  const allRoutes = useMemo(
    () => appCatalog.flatMap((app) => app.groups.flatMap((group) => group.routes.map((route) => route.route))),
    [appCatalog],
  );

  function toggleAppRoutes(app: PortalAppCatalogGroup, checked: boolean) {
    const routes = app.groups.flatMap((group) => group.routes.map((route) => route.route));
    setForm((current) => ({
      ...current,
      routes: checked
        ? [...new Set([...current.routes, ...routes])]
        : current.routes.filter((route) => !routes.includes(route)),
    }));
  }

  function isAppFullySelected(app: PortalAppCatalogGroup) {
    const routes = app.groups.flatMap((group) => group.routes.map((route) => route.route));
    return routes.length > 0 && routes.every((route) => form.routes.includes(route));
  }

  function openCreate() {
    setEditingGroup(null);
    setForm({ name: "", routes: [...allRoutes] });
    setSheetOpen(true);
  }

  async function openEdit(group: AccessGroupRow) {
    setEditingGroup(group);
    setSaving(true);
    try {
      const response = await fetch(`/customer/${tenant}/admin/groups/${group.id}/detail`, {
        credentials: "same-origin",
      });
      const data = (await response.json()) as { data?: { routes?: string[]; name?: string } };
      setForm({
        name: data.data?.name ?? group.name,
        routes: data.data?.routes ?? [],
      });
      setSheetOpen(true);
    } catch {
      toast.error("Could not load role details.");
    } finally {
      setSaving(false);
    }
  }

  function toggleRoute(route: string, checked: boolean) {
    setForm((current) => ({
      ...current,
      routes: checked
        ? [...new Set([...current.routes, route])]
        : current.routes.filter((value) => value !== route),
    }));
  }

  function toggleModuleRow(row: ModulePermissionRow, checked: boolean) {
    const routes = actionButtonsForRow(row).map((item) => item.route.route);
    setForm((current) => ({
      ...current,
      routes: checked
        ? [...new Set([...current.routes, ...routes])]
        : current.routes.filter((route) => !routes.includes(route)),
    }));
  }

  function isModuleFullySelected(row: ModulePermissionRow) {
    const routes = actionButtonsForRow(row).map((item) => item.route.route);
    return routes.length > 0 && routes.every((route) => form.routes.includes(route));
  }

  function toggleNavGroupRows(rows: ModulePermissionRow[], checked: boolean) {
    const routes = rows.flatMap((row) => actionButtonsForRow(row).map((item) => item.route.route));
    setForm((current) => ({
      ...current,
      routes: checked
        ? [...new Set([...current.routes, ...routes])]
        : current.routes.filter((route) => !routes.includes(route)),
    }));
  }

  function isNavGroupFullySelected(rows: ModulePermissionRow[]) {
    const routes = rows.flatMap((row) => actionButtonsForRow(row).map((item) => item.route.route));
    return routes.length > 0 && routes.every((route) => form.routes.includes(route));
  }

  function renderModuleRow(row: ModulePermissionRow) {
    const actions = actionButtonsForRow(row);
    const hasActions = actions.some((action) =>
      ["view", "create", "update", "delete"].includes(action.key),
    );

    if (!hasActions || actions.length <= 1) {
      return actions.map((action) => {
        const checked = form.routes.includes(action.route.route);
        return (
          <label key={action.route.route} className="flex items-start gap-2 text-sm">
            <Checkbox
              checked={checked}
              onCheckedChange={(value) => toggleRoute(action.route.route, value === true)}
            />
            <span>
              <span className="font-medium">{row.label}</span>
              <span className="block text-xs text-muted-foreground">
                {row.description || action.route.description}
              </span>
            </span>
          </label>
        );
      });
    }

    return (
      <div key={row.key} className="space-y-2 rounded-md border bg-card px-3 py-2.5">
        <label className="flex items-start gap-2 text-sm">
          <Checkbox
            checked={isModuleFullySelected(row)}
            onCheckedChange={(value) => toggleModuleRow(row, value === true)}
          />
          <span>
            <span className="font-medium">{row.label}</span>
            {row.description ? (
              <span className="block text-xs text-muted-foreground">{row.description}</span>
            ) : null}
          </span>
        </label>
        <div className="flex flex-wrap gap-3 pl-6">
          {actions.map((action) => {
            const checked = form.routes.includes(action.route.route);
            return (
              <label
                key={action.route.route}
                className="inline-flex items-center gap-1.5 text-xs font-medium"
              >
                <Checkbox
                  checked={checked}
                  onCheckedChange={(value) => toggleRoute(action.route.route, value === true)}
                />
                {action.label}
              </label>
            );
          })}
        </div>
      </div>
    );
  }

  async function handleSave() {
    if (!form.name.trim()) {
      toast.error("Role name is required.");
      return;
    }

    setSaving(true);
    try {
      const url = editingGroup
        ? `/customer/${tenant}/admin/groups/${editingGroup.id}`
        : `/customer/${tenant}/admin/groups/create`;
      const response = await fetch(url, {
        method: "POST",
        credentials: "same-origin",
        headers: {
          "Content-Type": "application/json",
          Accept: "application/json",
          ...customerCsrfHeader(tenant),
        },
        body: JSON.stringify({
          name: form.name.trim(),
          routes: form.routes.map((route) => ({ route, access: "allow" })),
        }),
      });
      const data = (await response.json()) as { status?: string; message?: string };
      if (!response.ok || data.status !== "success") {
        throw new Error(data.message ?? "Could not save role.");
      }

      toast.success(data.message ?? "Role saved.");
      setSheetOpen(false);
      router.refresh();
    } catch (error) {
      toastApiError(error, "Could not save role.");
    } finally {
      setSaving(false);
    }
  }

  async function handleDelete(group: AccessGroupRow) {
    if (!window.confirm(`Delete role "${group.name}"?`)) {
      return;
    }

    try {
      const response = await fetch(`/customer/${tenant}/admin/groups/${group.id}`, {
        method: "DELETE",
        credentials: "same-origin",
      });
      const data = (await response.json()) as { status?: string; message?: string };
      if (!response.ok || data.status !== "success") {
        throw new Error(data.message ?? "Could not delete role.");
      }

      setGroups((current) => current.filter((item) => item.id !== group.id));
      toast.success(data.message ?? "Role deleted.");
      router.refresh();
    } catch (error) {
      toastApiError(error, "Could not delete role.");
    }
  }

  if (initialErrorMessage) {
    return <ErrorBanner message={initialErrorMessage} />;
  }

  return (
    <div className="flex flex-col gap-4">
      <div className="flex items-center justify-between gap-3">
        <div>
          <h1 className="text-2xl font-semibold tracking-tight">Roles & Permissions</h1>
          <p className="text-sm text-muted-foreground">
            {canManageRoles
              ? "Create roles with View / Create / Update / Delete per module, then assign them to Customers and Users."
              : "View roles used when assigning permissions to Customers. Creating or editing roles requires admin manage access."}
          </p>
        </div>
        {canManageRoles ? (
          <Button onClick={openCreate}>
            <Plus />
            New role
          </Button>
        ) : null}
      </div>

      <div className="rounded-xl border">
        <Table>
          <TableHeader>
            <TableRow>
              <TableHead>Role</TableHead>
              <TableHead>Users</TableHead>
              <TableHead>Routes</TableHead>
              <TableHead className="text-right">Actions</TableHead>
            </TableRow>
          </TableHeader>
          <TableBody>
            {groups.length === 0 ? (
              <TableRow>
                <TableCell colSpan={4} className="py-8 text-center text-muted-foreground">
                  No roles yet. Create one to start assigning permissions.
                </TableCell>
              </TableRow>
            ) : (
              groups.map((group) => (
                <TableRow key={group.id}>
                  <TableCell className="font-medium">{group.name}</TableCell>
                  <TableCell>{group.users_count}</TableCell>
                  <TableCell>
                    <Badge variant="secondary">{group.routes_count} routes</Badge>
                  </TableCell>
                  <TableCell className="text-right">
                    {canManageRoles ? (
                      <div className="flex justify-end gap-2">
                        <Button size="sm" variant="outline" onClick={() => void openEdit(group)} disabled={saving}>
                          <Pencil />
                          Edit
                        </Button>
                        <Button size="sm" variant="outline" onClick={() => void handleDelete(group)}>
                          <Trash2 />
                          Delete
                        </Button>
                      </div>
                    ) : (
                      <span className="text-muted-foreground text-xs">View only</span>
                    )}
                  </TableCell>
                </TableRow>
              ))
            )}
          </TableBody>
        </Table>
      </div>

      <Sheet
        open={sheetOpen}
        onOpenChange={(open) => {
          setSheetOpen(open);
          if (!open) {
            setEditingGroup(null);
          }
        }}
      >
        <SheetContent className="flex h-full !w-[min(860px,calc(100vw-1rem))] !max-w-none flex-col gap-0 overflow-hidden bg-muted/30 p-0">
          <SheetHeader className="shrink-0 border-b bg-card px-6 py-5 pr-14">
            <SheetTitle>{editingGroup ? "Edit role" : "Create role"}</SheetTitle>
            <SheetDescription>
              Choose which modules this role can View, Create, Update, or Delete.
            </SheetDescription>
          </SheetHeader>

          <div className="min-h-0 flex-1 space-y-4 overflow-y-auto px-6 py-6">
            <div className="space-y-2">
              <Label htmlFor="group-name">Role name</Label>
              <Input
                id="group-name"
                value={form.name}
                onChange={(event) => setForm((current) => ({ ...current, name: event.target.value }))}
                placeholder="Portfolio managers"
              />
            </div>

            {appCatalog.map((app) => (
              <div key={app.app} className="space-y-3 rounded-xl border bg-card p-4">
                <label className="flex items-start gap-2 text-sm font-medium">
                  <Checkbox
                    checked={isAppFullySelected(app)}
                    onCheckedChange={(value) => toggleAppRoutes(app, value === true)}
                  />
                  <span>
                    {app.label}
                    {app.description ? (
                      <span className="block text-xs font-normal text-muted-foreground">
                        {app.description}
                      </span>
                    ) : null}
                  </span>
                </label>

                {(() => {
                  const mergedSection: RouteCatalogGroup = {
                    label: "Permissions",
                    routes: app.groups.flatMap((group) => group.routes),
                  };
                  const moduleRows = buildModuleRows(mergedSection);
                  const permissionItems = partitionPermissionItems(moduleRows);
                  const hasGranular = moduleRows.some(
                    (row) => row.create || row.update || row.delete,
                  );

                  return (
                    <div className="ml-0 space-y-3 rounded-lg border bg-muted/20 p-3 sm:ml-6">
                      <div
                        className={cn(
                          "grid gap-3",
                          hasGranular ? "grid-cols-1" : "sm:grid-cols-2",
                        )}
                      >
                        {permissionItems.map((item) => {
                          if (item.type === "nav_group") {
                            return (
                              <div
                                key={`nav-group-${item.key}`}
                                className="space-y-2 rounded-md border bg-card px-3 py-2.5"
                              >
                                <label className="flex items-start gap-2 text-sm">
                                  <Checkbox
                                    checked={isNavGroupFullySelected(item.rows)}
                                    onCheckedChange={(value) =>
                                      toggleNavGroupRows(item.rows, value === true)
                                    }
                                  />
                                  <span>
                                    <span className="font-medium">{item.label}</span>
                                    <span className="block text-xs text-muted-foreground">
                                      Modules nested under this sidebar group
                                    </span>
                                  </span>
                                </label>
                                <div className="space-y-2 pl-6">
                                  {item.rows.map((row) => renderModuleRow(row))}
                                </div>
                              </div>
                            );
                          }

                          return renderModuleRow(item.row);
                        })}
                      </div>
                    </div>
                  );
                })()}
              </div>
            ))}
          </div>

          <SheetFooter className="shrink-0 border-t bg-card px-6 py-4 sm:flex-row sm:justify-end">
            <Button variant="outline" onClick={() => setSheetOpen(false)} disabled={saving}>
              Cancel
            </Button>
            <Button onClick={() => void handleSave()} disabled={saving}>
              {saving ? "Saving..." : "Save role"}
            </Button>
          </SheetFooter>
        </SheetContent>
      </Sheet>
    </div>
  );
}
