"use client";

import { Fragment, useMemo } from "react";

import { Checkbox } from "@/components/ui/checkbox";
import {
  actionButtonsForRow,
  applyBondAccumulatorMergeRules,
  applyCommodityDerivMergeRules,
  applyFxSwapsMergeRules,
  applyRolesUiHiddenRouteRules,
  applyStockDerivMergeRules,
  applyStructureLegacyAliasRules,
  applyTransactionsExtrasRules,
  buildModuleRows,
  groupPortalAppsForDisplay,
  moduleRowDuplicatesAppLabel,
  partitionPermissionItems,
  rolesUiRulesFromApps,
  routesForModuleRow,
  type ModulePermissionRow,
  type PermissionCatalogRoute,
} from "@/lib/portal-permission-catalog";
import { cn } from "@/lib/utils";

export type PortalPermissionApp = {
  app: string;
  label: string;
  description: string;
  groups: Array<{
    label: string;
    routes: PermissionCatalogRoute[];
  }>;
};

type PortalPermissionTreeProps = {
  apps: PortalPermissionApp[];
  selectedRoutes: string[];
  onSelectedRoutesChange: (routes: string[]) => void;
  /** Extra caveat rendered under an app's description, keyed by app key. */
  appNotes?: Record<string, string>;
  preferRolesGroupLabels?: boolean;
  readOnly?: boolean;
  /** Case-insensitive filter over app / route labels and descriptions. */
  searchQuery?: string;
  className?: string;
};

function matchesQuery(
  query: string,
  ...parts: Array<string | null | undefined>
): boolean {
  if (!query) return true;
  const haystack = parts.filter(Boolean).join(" ").toLowerCase();
  return haystack.includes(query);
}

export function PortalPermissionTree({
  apps,
  selectedRoutes,
  onSelectedRoutesChange,
  appNotes,
  preferRolesGroupLabels = false,
  readOnly = false,
  searchQuery = "",
  className,
}: PortalPermissionTreeProps) {
  const q = searchQuery.trim().toLowerCase();
  const displayApps = useMemo(() => {
    const grouped = groupPortalAppsForDisplay(apps);
    if (!q) return grouped;

    return grouped
      .map((app) => {
        const appMatches = matchesQuery(q, app.label, app.description);
        const groups = app.groups
          .map((group) => ({
            ...group,
            routes: appMatches
              ? group.routes
              : group.routes.filter((route) =>
                  matchesQuery(
                    q,
                    route.name,
                    route.roles_label,
                    route.description,
                    route.route,
                    group.label,
                  ),
                ),
          }))
          .filter((group) => group.routes.length > 0);

        if (!appMatches && groups.length === 0) {
          return null;
        }

        return { ...app, groups: groups.length > 0 ? groups : app.groups };
      })
      .filter((app): app is PortalPermissionApp => app !== null);
  }, [apps, q]);

  const rolesUiRules = rolesUiRulesFromApps(apps);

  function normalizeSelectedRoutes(routes: string[], previousRoutes: string[] = selectedRoutes): string[] {
    return applyTransactionsExtrasRules(
      applyStockDerivMergeRules(
        applyCommodityDerivMergeRules(
          applyBondAccumulatorMergeRules(
            applyFxSwapsMergeRules(
              applyStructureLegacyAliasRules(
                applyRolesUiHiddenRouteRules(routes, rolesUiRules),
                rolesUiRules,
              ),
            ),
          ),
        ),
      ),
      previousRoutes,
    );
  }

  function toggleRoute(route: string, checked: boolean) {
    if (readOnly) return;
    const next = checked
      ? [...new Set([...selectedRoutes, route])]
      : selectedRoutes.filter((value) => value !== route);
    onSelectedRoutesChange(normalizeSelectedRoutes(next, selectedRoutes));
  }

  function toggleRoutes(routes: string[], checked: boolean) {
    if (readOnly) return;
    const next = checked
      ? [...new Set([...selectedRoutes, ...routes])]
      : selectedRoutes.filter((route) => !routes.includes(route));
    onSelectedRoutesChange(normalizeSelectedRoutes(next, selectedRoutes));
  }

  function toggleApp(app: PortalPermissionApp, checked: boolean) {
    const appRoutes = app.groups.flatMap((group) => group.routes.map((route) => route.route));
    toggleRoutes(appRoutes, checked);
  }

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

  function appSelectedCount(app: PortalPermissionApp) {
    const appRoutes = app.groups.flatMap((group) => group.routes.map((route) => route.route));
    return appRoutes.filter((route) => selectedRoutes.includes(route)).length;
  }

  function isRowFullySelected(row: ModulePermissionRow) {
    const routes = routesForModuleRow(row);
    return routes.length > 0 && routes.every((route) => selectedRoutes.includes(route));
  }

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

  function renderActionChip(action: {
    key: string;
    label: string;
    route: PermissionCatalogRoute;
  }) {
    const checked = selectedRoutes.includes(action.route.route);
    return (
      <label
        key={action.route.route}
        className={cn(
          "inline-flex max-w-full items-center gap-1.5 rounded-md border px-2 py-1 text-xs font-medium transition-colors",
          checked
            ? "border-primary/40 bg-primary/10 text-foreground"
            : "border-border/70 bg-background/50 text-muted-foreground hover:border-border hover:text-foreground",
          readOnly && "pointer-events-none opacity-80",
        )}
      >
        <Checkbox
          className="size-3.5"
          checked={checked}
          disabled={readOnly}
          onCheckedChange={(value) => toggleRoute(action.route.route, value === true)}
        />
        <span className="leading-none">{action.label}</span>
      </label>
    );
  }

  function renderModuleRow(
    row: ModulePermissionRow,
    options?: { fullWidth?: boolean; hideTitle?: boolean },
  ) {
    const actions = actionButtonsForRow(row);
    const hasGranular = actions.some((action) =>
      ["view", "create", "update", "delete"].includes(action.key),
    );
    const fullWidth = options?.fullWidth === true;
    const hideTitle = options?.hideTitle === true;

    if (!hasGranular || actions.length <= 1) {
      return actions.map((action) => {
        const checked = selectedRoutes.includes(action.route.route);
        const label = hideTitle ? action.label : row.label;
        const description = hideTitle
          ? action.route.description || row.description
          : row.description || action.route.description;
        return (
          <label
            key={action.route.route}
            className={cn(
              "flex min-w-0 items-start gap-2.5 rounded-lg border border-transparent px-2.5 py-2 text-sm transition-colors hover:border-border/80 hover:bg-muted/40",
              checked && "border-border/60 bg-muted/30",
              fullWidth && "sm:col-span-2",
              readOnly && "pointer-events-none opacity-80",
            )}
          >
            <Checkbox
              className="mt-0.5"
              checked={checked}
              disabled={readOnly}
              onCheckedChange={(value) => toggleRoute(action.route.route, value === true)}
            />
            <span className="min-w-0 leading-snug">
              <span className="font-medium">{label}</span>
              {description ? (
                <span className="mt-0.5 block text-[11px] leading-relaxed text-muted-foreground">
                  {description}
                </span>
              ) : null}
            </span>
          </label>
        );
      });
    }

    if (hideTitle) {
      return (
        <div
          key={row.key}
          className={cn(
            "flex flex-wrap gap-1.5 rounded-lg border border-dashed border-border/70 bg-muted/15 p-2.5",
            fullWidth && "sm:col-span-2",
          )}
        >
          {actions.map((action) => renderActionChip(action))}
        </div>
      );
    }

    return (
      <div
        key={row.key}
        className={cn(
          "rounded-lg border bg-card/80 px-3 py-2.5 shadow-sm shadow-black/5",
          fullWidth && "sm:col-span-2",
        )}
      >
        <label className={cn("flex items-start gap-2.5 text-sm", readOnly && "pointer-events-none")}>
          <Checkbox
            className="mt-0.5"
            checked={isRowFullySelected(row)}
            disabled={readOnly}
            onCheckedChange={(value) => toggleRoutes(routesForModuleRow(row), value === true)}
          />
          <span className="min-w-0 leading-snug">
            <span className="font-medium">{row.label}</span>
            {row.description ? (
              <span className="mt-0.5 block text-[11px] leading-relaxed text-muted-foreground">
                {row.description}
              </span>
            ) : null}
          </span>
        </label>
        <div className="mt-2 flex flex-wrap gap-1.5 pl-7">
          {actions.map((action) => renderActionChip(action))}
        </div>
      </div>
    );
  }

  if (displayApps.length === 0) {
    return (
      <div
        className={cn(
          "rounded-xl border border-dashed bg-muted/20 px-4 py-8 text-center text-sm text-muted-foreground",
          className,
        )}
      >
        No permissions match your search.
      </div>
    );
  }

  return (
    <div className={cn("space-y-4", className)}>
      {displayApps.map((app) => {
        const merged = {
          label: "Permissions",
          routes: app.groups.flatMap((group) => group.routes) as PermissionCatalogRoute[],
        };
        const moduleRows = buildModuleRows(merged, preferRolesGroupLabels);
        const permissionItems = partitionPermissionItems(moduleRows);
        const total = app.groups.flatMap((group) => group.routes).length;
        const selected = appSelectedCount(app);

        return (
          <section
            key={app.app}
            className="overflow-hidden rounded-xl border bg-card shadow-sm shadow-black/5"
          >
            <div className="flex items-start gap-3 border-b bg-muted/25 px-3 py-3 sm:px-4">
              <Checkbox
                className="mt-0.5"
                checked={isAppFullySelected(app)}
                disabled={readOnly}
                onCheckedChange={(value) => toggleApp(app, value === true)}
              />
              <div className="min-w-0 flex-1">
                <div className="flex flex-wrap items-center gap-2">
                  <p className="font-semibold leading-none tracking-tight">{app.label}</p>
                  <span className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium tabular-nums text-muted-foreground">
                    {selected}/{total}
                  </span>
                </div>
                {app.description ? (
                  <p className="mt-1.5 text-xs leading-relaxed text-muted-foreground">
                    {app.description}
                  </p>
                ) : null}
                {appNotes?.[app.app] ? (
                  <p className="mt-1.5 text-xs font-medium text-amber-700 dark:text-amber-500">
                    {appNotes[app.app]}
                  </p>
                ) : null}
              </div>
            </div>

            <div className="grid grid-cols-1 gap-1.5 p-2.5 sm:grid-cols-2 sm:gap-2 sm:p-3">
              {permissionItems.map((item) => {
                if (item.type === "nav_group") {
                  return (
                    <div
                      key={`nav-group-${item.key}`}
                      className="space-y-2 rounded-lg border bg-muted/15 px-3 py-2.5 sm:col-span-2"
                    >
                      <label
                        className={cn(
                          "flex items-start gap-2.5 text-sm",
                          readOnly && "pointer-events-none",
                        )}
                      >
                        <Checkbox
                          className="mt-0.5"
                          checked={isNavGroupFullySelected(item.rows)}
                          disabled={readOnly}
                          onCheckedChange={(value) =>
                            toggleRoutes(
                              item.rows.flatMap((row) => routesForModuleRow(row)),
                              value === true,
                            )
                          }
                        />
                        <span>
                          <span className="font-medium">{item.label}</span>
                          <span className="mt-0.5 block text-[11px] text-muted-foreground">
                            {item.rows.length} modules in this group
                          </span>
                        </span>
                      </label>
                      <div className="grid grid-cols-1 gap-1.5 sm:grid-cols-2">
                        {item.rows.map((row) => (
                          <Fragment key={row.key}>{renderModuleRow(row)}</Fragment>
                        ))}
                      </div>
                    </div>
                  );
                }

                return (
                  <Fragment key={item.row.key}>
                    {renderModuleRow(item.row, {
                      fullWidth: item.row.key === "order_blotter",
                      hideTitle: moduleRowDuplicatesAppLabel(item.row.label, app.label),
                    })}
                  </Fragment>
                );
              })}
            </div>
          </section>
        );
      })}
    </div>
  );
}
