import { Button } from "@minmon/ui/components/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@minmon/ui/components/card";
import { Checkbox } from "@minmon/ui/components/checkbox";
import { StatusBadge } from "@minmon/ui/components/status-badge";
import { useMutation, useQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import { toast } from "sonner";

import { PageSection, QueryStateCard, ResourceListCard } from "@/components/dashboard-ui";
import { queryClient, trpc, trpcClient } from "@/utils/trpc";

export const Route = createFileRoute("/dashboard/users")({
  component: RouteComponent,
});

type RoleOption = {
  id: string;
  name: string;
  description: string | null;
};

function RoleBadges({ roles }: { roles: { id: string; name: string }[] }) {
  if (!roles.length) {
    return <span className="text-muted-foreground">No roles</span>;
  }

  return (
    <div className="flex flex-wrap gap-1.5">
      {roles.map((role) => (
        <StatusBadge key={role.id} variant={role.name === "Admin" ? "info" : role.name === "Editor" ? "warning" : "neutral"}>
          {role.name}
        </StatusBadge>
      ))}
    </div>
  );
}

function RoleAssignmentCard({ roles }: { roles: RoleOption[] }) {
  const users = useQuery(trpc.users.list.queryOptions());
  const updateRoles = useMutation({
    mutationFn: async ({ userId, roleIds }: { userId: string; roleIds: string[] }) =>
      trpcClient.users.updateRoles.mutate({ userId, roleIds }),
    onSuccess: async () => {
      toast.success("User roles updated");
      await queryClient.invalidateQueries();
    },
    onError: (error) => toast.error(error.message),
  });

  if (users.isLoading) {
    return <QueryStateCard title="Loading users" description="Fetching users and their current role assignments." />;
  }

  if (users.isError || !users.data) {
    return <QueryStateCard title="Users unavailable" description="User role assignments could not be loaded." />;
  }

  return (
    <ResourceListCard
      title="User role assignments"
      description="Grant one or more roles to each user. Changes are saved immediately."
      columns={[
        {
          key: "user",
          header: "User",
          render: (item: (typeof users.data)[number]) => (
            <div className="grid gap-1">
              <span className="font-medium">{item.name}</span>
              <span className="text-xs text-muted-foreground">{item.email}</span>
            </div>
          ),
        },
        {
          key: "roles",
          header: "Current roles",
          render: (item: (typeof users.data)[number]) => <RoleBadges roles={item.roles} />,
        },
        {
          key: "assign",
          header: "Assign roles",
          render: (item: (typeof users.data)[number]) => {
            const assignedRoleIds = new Set(item.roles.map((role) => role.id));

            return (
              <div className="grid gap-2 sm:grid-cols-3">
                {roles.map((role) => {
                  const checked = assignedRoleIds.has(role.id);
                  const nextRoleIds = checked
                    ? item.roles.map((assignedRole) => assignedRole.id).filter((roleId) => roleId !== role.id)
                    : [...item.roles.map((assignedRole) => assignedRole.id), role.id];

                  return (
                    <label key={role.id} className="flex items-center gap-2 text-sm">
                      <Checkbox
                        checked={checked}
                        disabled={updateRoles.isPending || nextRoleIds.length === 0}
                        onCheckedChange={() => updateRoles.mutate({ userId: item.id, roleIds: nextRoleIds })}
                      />
                      <span>{role.name}</span>
                    </label>
                  );
                })}
              </div>
            );
          },
        },
      ]}
      items={users.data}
      emptyTitle="No users found"
      emptyDescription="Users appear here after they sign up or are seeded."
    />
  );
}

function RouteComponent() {
  const roles = useQuery(trpc.users.roles.queryOptions());

  if (roles.isLoading) {
    return <QueryStateCard title="Loading roles" description="Fetching available Admin, Editor, and Viewer roles." />;
  }

  if (roles.isError || !roles.data) {
    return <QueryStateCard title="Roles unavailable" description="Role options could not be loaded right now." />;
  }

  return (
    <PageSection title="Users and roles" description="Assign Admin, Editor, or Viewer access to application users.">
      <div className="grid gap-4">
        <Card>
          <CardHeader>
            <CardTitle>Role guide</CardTitle>
            <CardDescription>Default RBAC roles seeded by the application.</CardDescription>
          </CardHeader>
          <CardContent className="grid gap-3 md:grid-cols-3">
            {roles.data.map((role) => (
              <div key={role.id} className="grid gap-1 border p-3">
                <StatusBadge variant={role.name === "Admin" ? "info" : role.name === "Editor" ? "warning" : "neutral"}>
                  {role.name}
                </StatusBadge>
                <p className="text-sm text-muted-foreground">{role.description}</p>
              </div>
            ))}
          </CardContent>
        </Card>
        <RoleAssignmentCard roles={roles.data} />
      </div>
    </PageSection>
  );
}
