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 { useForm } from "@tanstack/react-form";
import { useMutation, useQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import { toast } from "sonner";
import z from "zod";

import { ConfirmDeleteIconButton, FormCard, PageSection, QueryStateCard, ResourceListCard, TextField } 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;
};

type UserCreateFormValues = {
  name: string;
  email: string;
  password: string;
  roleIds: string[];
};

const userCreateSchema = z.object({
  name: z.string().min(2, "Name must be at least 2 characters"),
  email: z.email("Invalid email address"),
  password: z.string().min(8, "Password must be at least 8 characters"),
  roleIds: z.array(z.string()).min(1, "Select at least one role"),
});

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, permissions }: { roles: RoleOption[]; permissions: string[] }) {
  const users = useQuery(trpc.users.list.queryOptions());
  const allowedPermissions = new Set(permissions);
  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),
  });
  const removeUser = useMutation({
    mutationFn: async (id: string) => trpcClient.users.delete.mutate({ id }),
    onSuccess: async () => {
      toast.success("User deleted");
      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="Users"
      description={
        allowedPermissions.has("users.assignRoles")
          ? "Grant one or more roles to each user. Changes are saved immediately."
          : "Review application users and their current roles."
      }
      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} />,
        },
        ...(allowedPermissions.has("users.assignRoles")
          ? [
              {
                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>
                  );
                },
              },
            ]
          : []),
        ...(allowedPermissions.has("users.delete")
          ? [
              {
                key: "actions",
                header: "Actions",
                className: "w-20",
                render: (item: (typeof users.data)[number]) => (
                  <ConfirmDeleteIconButton
                    itemLabel={`user ${item.email}`}
                    disabled={removeUser.isPending}
                    onConfirm={() => removeUser.mutate(item.id)}
                  />
                ),
              },
            ]
          : []),
      ]}
      items={users.data}
      emptyTitle="No users found"
      emptyDescription="Users appear here after they sign up or are seeded."
    />
  );
}

function CreateUserCard({ roles }: { roles: RoleOption[] }) {
  const createUser = useMutation({
    mutationFn: async (values: UserCreateFormValues) => trpcClient.users.create.mutate(values),
    onSuccess: async () => {
      toast.success("User created");
      form.reset();
      await queryClient.invalidateQueries();
    },
    onError: (error) => toast.error(error.message),
  });

  const form = useForm({
    defaultValues: {
      name: "",
      email: "",
      password: "",
      roleIds: [roles.find((role) => role.name === "Viewer")?.id ?? roles[0]?.id ?? ""].filter(Boolean),
    } satisfies UserCreateFormValues,
    onSubmit: async ({ value }) => {
      await createUser.mutateAsync(value);
    },
    validators: {
      onSubmit: userCreateSchema,
    },
  });

  return (
    <form
      className="grid gap-4"
      onSubmit={(event) => {
        event.preventDefault();
        event.stopPropagation();
        form.handleSubmit();
      }}
    >
      <FormCard
        title="Create user"
        description="Add a password user and assign an initial role. The account can sign in immediately."
        footer={
          <form.Subscribe selector={(state) => ({ canSubmit: state.canSubmit, isSubmitting: state.isSubmitting })}>
            {({ canSubmit, isSubmitting }) => (
              <Button type="submit" disabled={!canSubmit} loading={isSubmitting}>
                Create user
              </Button>
            )}
          </form.Subscribe>
        }
      >
        <div className="grid gap-4 md:grid-cols-3">
          <form.Field name="name">
            {(field) => (
              <TextField
                label="Name"
                name={field.name}
                autoComplete="name"
                value={field.state.value}
                onBlur={field.handleBlur}
                onChange={(event) => field.handleChange(event.target.value)}
                errors={field.state.meta.errors}
              />
            )}
          </form.Field>
          <form.Field name="email">
            {(field) => (
              <TextField
                label="Email"
                name={field.name}
                type="email"
                autoComplete="email"
                value={field.state.value}
                onBlur={field.handleBlur}
                onChange={(event) => field.handleChange(event.target.value)}
                errors={field.state.meta.errors}
              />
            )}
          </form.Field>
          <form.Field name="password">
            {(field) => (
              <TextField
                label="Password"
                name={field.name}
                type="password"
                autoComplete="new-password"
                value={field.state.value}
                onBlur={field.handleBlur}
                onChange={(event) => field.handleChange(event.target.value)}
                errors={field.state.meta.errors}
              />
            )}
          </form.Field>
        </div>
        <form.Field name="roleIds">
          {(field) => (
            <div className="grid gap-2">
              <p className="text-sm font-medium">Initial roles</p>
              <div className="grid gap-2 sm:grid-cols-3">
                {roles.map((role) => {
                  const checked = field.state.value.includes(role.id);
                  const nextRoleIds = checked
                    ? field.state.value.filter((roleId) => roleId !== role.id)
                    : [...field.state.value, role.id];

                  return (
                    <label key={role.id} className="flex items-center gap-2 text-sm">
                      <Checkbox
                        checked={checked}
                        disabled={checked && field.state.value.length === 1}
                        onCheckedChange={() => field.handleChange(nextRoleIds)}
                      />
                      <span>{role.name}</span>
                    </label>
                  );
                })}
              </div>
              {field.state.meta.errors.length ? (
                <p className="text-xs text-destructive" role="alert">
                  {field.state.meta.errors.map((error) => (typeof error === "string" ? error : error?.message)).filter(Boolean).join(", ")}
                </p>
              ) : null}
            </div>
          )}
        </form.Field>
      </FormCard>
    </form>
  );
}

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

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

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

  const allowedPermissions = new Set(me.data.permissions);

  return (
    <PageSection title="Users and roles" description="Assign Admin, Editor, or Viewer access to application users.">
      <div className="grid gap-4">
        {allowedPermissions.has("users.create") ? <CreateUserCard roles={roles.data} /> : null}
        <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} permissions={me.data.permissions} />
      </div>
    </PageSection>
  );
}
