import { useProjectContext } from "@/sdk";
import {
  type OrganizationRole,
  useOrganizationRoles,
} from "@/hooks/use-organization-roles";
import { useOrgAuthClient } from "@/hooks/use-org-auth-client";
import { useT } from "@/i18n/use-t.ts";
import { KEYS } from "@/lib/query-keys";
import { track } from "@/lib/posthog-client";
import { getRoleDotColor } from "@/lib/role-color";
import { Badge } from "@decocms/ui/components/badge.tsx";
import { Button } from "@decocms/ui/components/button.tsx";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@decocms/ui/components/alert-dialog.tsx";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@decocms/ui/components/dropdown-menu.tsx";
import {
  Plus,
  Lock01,
  DotsVertical,
  Trash01,
  Loading01,
} from "@untitledui/icons";
import { cn } from "@decocms/ui/lib/utils.ts";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useNavigate, useSearch } from "@tanstack/react-router";
import { Suspense, useState } from "react";
import { toast } from "sonner";
import { CollectionTableWrapper } from "@/components/collections/collection-table-wrapper.tsx";
import type { TableColumn } from "@/components/collections/collection-table.tsx";
import { Page } from "@/components/page";
import { ErrorBoundary } from "@/components/error-boundary";
import { EmptyState } from "@/components/empty-state.tsx";
import { SearchInput } from "@decocms/ui/components/search-input.tsx";
import {
  RoleDetailPage,
  getTargetKey,
  type RoleEditorTarget,
} from "@/views/settings/org-role-detail.tsx";
import { RequirePrivileged } from "@/components/require-privileged";

const BUILTIN_ROLE_KEYS = [
  { role: "owner", labelKey: "settings.roles.roleOwner" },
  { role: "admin", labelKey: "settings.roles.roleAdmin" },
  { role: "user", labelKey: "settings.roles.roleUser" },
] as const;

// ============================================================================
// Roles Table (main page content)
// ============================================================================

type RoleRow =
  | {
      kind: "builtin";
      role: (typeof BUILTIN_ROLE_KEYS)[number] & {
        memberCount: number;
        label: string;
      };
    }
  | { kind: "custom"; role: OrganizationRole & { memberCount: number } };

function RolesPageContent() {
  const t = useT();
  const [search, setSearch] = useState("");
  const [roleToDelete, setRoleToDelete] = useState<{
    id: string;
    label: string;
  } | null>(null);

  const navigate = useNavigate();
  const { role: roleParam } = useSearch({ strict: false }) as {
    role?: string;
  };

  const { locator } = useProjectContext();
  const orgAuth = useOrgAuthClient();
  const queryClient = useQueryClient();
  const { customRoles, refetch: refetchRoles } = useOrganizationRoles();

  const setActiveRole = (value: string | undefined) =>
    navigate({
      to: ".",
      search: (prev: Record<string, unknown>) => ({ ...prev, role: value }),
    });

  const activeTarget: RoleEditorTarget | null = (() => {
    if (!roleParam) return null;
    if (roleParam === "new") return { kind: "new" };
    if (roleParam.startsWith("builtin-")) {
      const slug = roleParam.slice(8) as "owner" | "admin" | "user";
      return { kind: "builtin", role: slug };
    }
    const custom = customRoles.find((r) => r.id === roleParam);
    return custom ? { kind: "custom", role: custom } : null;
  })();
  const { data: membersData } = useQuery({
    queryKey: KEYS.members(locator),
    queryFn: () => orgAuth.organization.listMembers(),
  });

  const members = membersData?.data?.members ?? [];
  type Member = (typeof members)[number];

  const getMemberCount = (roleSlug: string) =>
    members.filter((m: Member) => m.role === roleSlug).length;

  const builtinRows: RoleRow[] = BUILTIN_ROLE_KEYS.map((r) => ({
    kind: "builtin" as const,
    role: { ...r, memberCount: getMemberCount(r.role), label: t(r.labelKey) },
  }));

  const customRows: RoleRow[] = customRoles.map((r) => ({
    kind: "custom" as const,
    role: { ...r, memberCount: getMemberCount(r.role) },
  }));

  let allRows: RoleRow[] = [...builtinRows, ...customRows];

  if (search) {
    const q = search.toLowerCase();
    allRows = allRows.filter((row) => row.role.label.toLowerCase().includes(q));
  }

  const deleteRoleMutation = useMutation({
    mutationFn: async (roleId: string) => {
      const result = await orgAuth.organization.deleteRole({ roleId });
      if (result?.error) throw new Error(result.error.message);
      return result?.data;
    },
    onSuccess: (_, roleId) => {
      track("role_deleted", { role_id: roleId });
      queryClient.invalidateQueries({ queryKey: KEYS.members(locator) });
      queryClient.invalidateQueries({
        queryKey: KEYS.organizationRoles(locator),
      });
      toast.success(t("settings.roles.deletedSuccessfully"));
      refetchRoles();
      if (activeTarget?.kind === "custom" && activeTarget.role.id === roleId) {
        setActiveRole(undefined);
      }
    },
    onError: (error, roleId) => {
      track("role_delete_failed", {
        role_id: roleId,
        error: error instanceof Error ? error.message : String(error),
      });
      toast.error(
        error instanceof Error ? error.message : "Failed to delete role",
      );
    },
  });

  const columns: TableColumn<RoleRow>[] = [
    {
      id: "role",
      header: t("settings.roles.columnRole"),
      render: (row) => (
        <div className="flex items-center gap-3">
          <div
            className={cn(
              "size-2.5 rounded-full shrink-0",
              getRoleDotColor(row.role.role, row.kind === "builtin"),
            )}
          />
          <span className="text-sm font-medium text-foreground truncate">
            {row.role.label}
          </span>
          {row.kind === "builtin" && (
            <Lock01 size={12} className="text-muted-foreground shrink-0" />
          )}
        </div>
      ),
      cellClassName: "flex-1 min-w-0",
      sortable: true,
    },
    {
      id: "type",
      header: t("settings.roles.columnType"),
      render: (row) =>
        row.kind === "builtin" ? (
          <Badge variant="secondary" className="text-xs">
            {t("settings.roles.builtIn")}
          </Badge>
        ) : (
          <Badge variant="outline" className="text-xs">
            {t("settings.roles.custom")}
          </Badge>
        ),
      cellClassName: "w-28 shrink-0",
    },
    {
      id: "permissions",
      header: t("settings.roles.columnPermissions"),
      render: (row) => {
        if (row.kind === "builtin") {
          if (row.role.role === "owner" || row.role.role === "admin") {
            return (
              <span className="text-sm text-muted-foreground">
                {t("settings.roles.fullAccess")}
              </span>
            );
          }
          return (
            <span className="text-sm text-muted-foreground">
              {t("settings.roles.basicAccess")}
            </span>
          );
        }
        const r = row.role;
        const parts: string[] = [];
        if (r.allowsAllStaticPermissions) {
          parts.push(t("settings.roles.fullOrgAccess"));
        } else if (r.staticPermissionCount && r.staticPermissionCount > 0) {
          parts.push(
            t("settings.roles.orgPermsCount", {
              count: r.staticPermissionCount,
            }),
          );
        }
        if (r.allowsAllConnections) {
          parts.push(t("settings.roles.allConnections"));
        } else if (r.connectionCount && r.connectionCount > 0) {
          parts.push(
            t("settings.roles.connectionCount", { count: r.connectionCount }),
          );
        }
        return (
          <span className="text-sm text-muted-foreground truncate">
            {parts.length > 0
              ? parts.join(", ")
              : t("settings.roles.noPermissions")}
          </span>
        );
      },
      cellClassName: "flex-1 min-w-0",
    },
    {
      id: "members",
      header: t("settings.roles.columnMembers"),
      render: (row) => (
        <span className="text-sm text-foreground">{row.role.memberCount}</span>
      ),
      cellClassName: "w-24 shrink-0",
    },
    {
      id: "actions",
      header: "",
      render: (row) => {
        if (row.kind === "builtin") return null;
        return (
          <DropdownMenu>
            <DropdownMenuTrigger asChild>
              <Button
                variant="ghost"
                size="sm"
                className="h-8 w-8 p-0"
                onClick={(e) => e.stopPropagation()}
              >
                <DotsVertical size={16} />
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent
              align="end"
              onClick={(e) => e.stopPropagation()}
            >
              <DropdownMenuItem
                variant="destructive"
                onClick={() => {
                  const r = row.role;
                  if (r.id) setRoleToDelete({ id: r.id, label: r.label });
                }}
              >
                <Trash01 size={16} />
                {t("settings.roles.delete")}
              </DropdownMenuItem>
            </DropdownMenuContent>
          </DropdownMenu>
        );
      },
      cellClassName: "w-12 shrink-0",
    },
  ];

  const handleRowClick = (row: RoleRow) => {
    if (row.kind === "builtin") {
      setActiveRole(`builtin-${row.role.role}`);
    } else {
      const r = row.role;
      setActiveRole(r.id);
    }
  };

  if (activeTarget) {
    return (
      <RoleDetailPage
        key={getTargetKey(activeTarget)}
        target={activeTarget}
        onBack={() => setActiveRole(undefined)}
        onSaved={() => {
          refetchRoles();
          setActiveRole(undefined);
        }}
      />
    );
  }

  return (
    <Page>
      <Page.Content>
        <Page.Body>
          <div className="flex flex-col gap-6">
            <Page.Title>{t("settings.roles.pageTitle")}</Page.Title>
            <div className="flex flex-wrap items-center justify-between gap-3">
              <SearchInput
                value={search}
                onChange={setSearch}
                placeholder={t("settings.roles.searchPlaceholder")}
                className="w-full md:w-[375px]"
                onKeyDown={(e) => {
                  if (e.key === "Escape") {
                    setSearch("");
                    (e.target as HTMLInputElement).blur();
                  }
                }}
              />
              <Button onClick={() => setActiveRole("new")}>
                <Plus size={16} />
                {t("settings.roles.createRole")}
              </Button>
            </div>
            <CollectionTableWrapper
              columns={columns}
              data={allRows}
              isLoading={false}
              onRowClick={handleRowClick}
              emptyState={
                search ? (
                  <EmptyState
                    title={t("settings.roles.noRolesFound")}
                    description={t("settings.roles.noRolesMatchSearch", {
                      search,
                    })}
                  />
                ) : (
                  <EmptyState
                    title={t("settings.roles.noRoles")}
                    description={t("settings.roles.createRoleGetStarted")}
                  />
                )
              }
            />
          </div>
        </Page.Body>
      </Page.Content>

      <AlertDialog
        open={roleToDelete !== null}
        onOpenChange={(open) => !open && setRoleToDelete(null)}
      >
        <AlertDialogContent>
          <AlertDialogHeader>
            <AlertDialogTitle>
              {t("settings.roles.deleteRoleTitle")}
            </AlertDialogTitle>
            <AlertDialogDescription>
              {t("settings.roles.deleteRoleConfirm", {
                role: roleToDelete?.label ?? "",
              })}
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>{t("settings.roles.cancel")}</AlertDialogCancel>
            <AlertDialogAction
              onClick={() => {
                if (roleToDelete?.id)
                  deleteRoleMutation.mutate(roleToDelete.id);
                setRoleToDelete(null);
              }}
              className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
            >
              {t("settings.roles.delete")}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </Page>
  );
}

function RolesPage() {
  const t = useT();
  return (
    <ErrorBoundary
      fallback={
        <Page>
          <div className="flex items-center justify-center h-full">
            <div className="text-sm text-muted-foreground">
              {t("settings.roles.failedToLoad")}
            </div>
          </div>
        </Page>
      }
    >
      <Suspense
        fallback={
          <Page>
            <div className="flex items-center justify-center h-full">
              <Loading01
                size={32}
                className="animate-spin text-muted-foreground"
              />
            </div>
          </Page>
        }
      >
        <RolesPageContent />
      </Suspense>
    </ErrorBoundary>
  );
}

export default function RolesRoute() {
  // Role-definition CRUD (listRoles/createRole/…) is owner/admin-only in Better
  // Auth, so gate on privilege rather than a grantable capability.
  return (
    <RequirePrivileged area="roles">
      <RolesPage />
    </RequirePrivileged>
  );
}
