"use client";

import { FormEvent, useMemo, useState } from "react";
import { Pencil, Plus, Power } from "lucide-react";
import { useRouter } from "next/navigation";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { Dialog } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Select } from "@/components/ui/select";
import type { ApiResult } from "@/modules/core/server/api";
import type { Permission, RoleDefinition } from "@/modules/core/types";

type PermissionDefinition = {
  id: Permission;
  label: string;
  category: string;
  description: string;
};

type RoleManagementClientProps = {
  permissions: PermissionDefinition[];
};

type PermissionGroup = {
  category: string;
  permissions: PermissionDefinition[];
};

type PermissionTreeProps = {
  permissions: PermissionDefinition[];
  selectedPermissions: Permission[];
  onChange: (permissions: Permission[]) => void;
};

function groupPermissions(permissions: PermissionDefinition[]): PermissionGroup[] {
  const groups = new Map<string, PermissionDefinition[]>();

  for (const permission of permissions) {
    const currentGroup = groups.get(permission.category) ?? [];
    currentGroup.push(permission);
    groups.set(permission.category, currentGroup);
  }

  return Array.from(groups.entries()).map(([category, groupPermissions]) => ({
    category,
    permissions: groupPermissions,
  }));
}

function orderSelectedPermissions(permissions: PermissionDefinition[], selectedSet: ReadonlySet<Permission>): Permission[] {
  return permissions.filter((permission) => selectedSet.has(permission.id)).map((permission) => permission.id);
}

function PermissionTree({
  permissions,
  selectedPermissions,
  onChange,
}: PermissionTreeProps): React.ReactElement {
  const groups = useMemo(() => groupPermissions(permissions), [permissions]);
  const selectedSet = useMemo(() => new Set(selectedPermissions), [selectedPermissions]);

  function togglePermission(permissionId: Permission, isChecked: boolean): void {
    const nextSelected = new Set(selectedSet);
    if (isChecked) {
      nextSelected.add(permissionId);
    } else {
      nextSelected.delete(permissionId);
    }

    onChange(orderSelectedPermissions(permissions, nextSelected));
  }

  function toggleCategory(groupPermissions: PermissionDefinition[], isChecked: boolean): void {
    const nextSelected = new Set(selectedSet);
    for (const permission of groupPermissions) {
      if (isChecked) {
        nextSelected.add(permission.id);
      } else {
        nextSelected.delete(permission.id);
      }
    }

    onChange(orderSelectedPermissions(permissions, nextSelected));
  }

  return (
    <div className="grid gap-3">
      <div className="flex items-center justify-between gap-3 rounded-md border bg-secondary/35 px-3 py-2 text-sm">
        <span className="font-medium">权限树</span>
        <Badge variant="secondary">
          {selectedPermissions.length}/{permissions.length}
        </Badge>
      </div>
      <div className="grid gap-3">
        {groups.map((group) => {
          const selectedCount = group.permissions.filter((permission) => selectedSet.has(permission.id)).length;
          const isAllSelected = selectedCount === group.permissions.length && group.permissions.length > 0;

          return (
            <div className="rounded-md border bg-card" key={group.category}>
              <div className="flex items-center justify-between gap-3 border-b px-3 py-3 text-sm">
                <span className="flex min-w-0 items-center gap-3">
                  <Checkbox
                    checked={isAllSelected}
                    label={
                      <span className="min-w-0">
                        <span className="block font-medium">{group.category}</span>
                        <span className="block text-xs text-muted-foreground">
                          已选 {selectedCount} / {group.permissions.length}
                        </span>
                      </span>
                    }
                    onCheckedChange={(checked) => toggleCategory(group.permissions, checked)}
                  />
                </span>
                <Badge variant={selectedCount > 0 ? "success" : "secondary"}>{selectedCount}</Badge>
              </div>
              <div className="grid gap-2 bg-secondary/15 p-3 md:grid-cols-2">
                {group.permissions.map((permission) => (
                  <div
                    className="flex min-h-16 items-start gap-3 rounded-md border bg-background p-3 text-sm transition-colors hover:bg-secondary/50"
                    key={permission.id}
                  >
                    <Checkbox
                      checked={selectedSet.has(permission.id)}
                      label={
                        <span className="min-w-0">
                          <span className="block font-medium">{permission.label}</span>
                          <span className="block break-all text-xs text-muted-foreground">{permission.id}</span>
                          <span className="mt-1 block text-xs text-muted-foreground">{permission.description}</span>
                        </span>
                      }
                      onCheckedChange={(checked) => togglePermission(permission.id, checked)}
                    />
                  </div>
                ))}
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

export function RoleManagementClient({ permissions }: RoleManagementClientProps): React.ReactElement {
  const router = useRouter();
  const [isCreateOpen, setIsCreateOpen] = useState(false);
  const [selectedPermissions, setSelectedPermissions] = useState<Permission[]>([]);
  const [message, setMessage] = useState("");
  const [isPending, setIsPending] = useState(false);

  function openCreateDialog(): void {
    setMessage("");
    setSelectedPermissions([]);
    setIsCreateOpen(true);
  }

  function closeCreateDialog(): void {
    if (isPending) {
      return;
    }

    setIsCreateOpen(false);
  }

  async function handleSubmit(event: FormEvent<HTMLFormElement>): Promise<void> {
    event.preventDefault();
    setMessage("");
    setIsPending(true);
    const formData = new FormData(event.currentTarget);
    const response = await fetch("/api/settings/roles", {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({
        label: String(formData.get("label") ?? ""),
        key: String(formData.get("key") ?? ""),
        description: String(formData.get("description") ?? ""),
        permissions: selectedPermissions,
      }),
    });
    const result = (await response.json()) as ApiResult<{ role: RoleDefinition }>;
    setIsPending(false);

    if (!result.success) {
      setMessage(result.reason);
      return;
    }

    setMessage(result.reason);
    setSelectedPermissions([]);
    setIsCreateOpen(false);
    event.currentTarget.reset();
    router.refresh();
  }

  return (
    <>
      <Button onClick={openCreateDialog} type="button">
        <Plus className="size-4" aria-hidden="true" />
        新增角色
      </Button>
      <Dialog
        description="角色由系统注册权限点组合而成。"
        onClose={closeCreateDialog}
        open={isCreateOpen}
        title="新增角色"
        width="wide"
      >
        <form className="grid gap-4" onSubmit={handleSubmit}>
          <div className="grid gap-3 md:grid-cols-3">
            <Input label="角色名称" name="label" placeholder="角色名称" required />
            <Input label="角色标识" name="key" placeholder="可留空" />
            <Input label="角色说明" name="description" placeholder="角色说明" />
          </div>
          <PermissionTree
            onChange={setSelectedPermissions}
            permissions={permissions}
            selectedPermissions={selectedPermissions}
          />
          {message ? (
            <p className="rounded-md bg-secondary px-3 py-2 text-sm text-secondary-foreground" role="status">
              {message}
            </p>
          ) : null}
          <div className="flex flex-col-reverse gap-2 pt-1 sm:flex-row sm:justify-end">
            <Button disabled={isPending} onClick={closeCreateDialog} type="button" variant="outline">
              取消
            </Button>
            <Button disabled={isPending} type="submit">
              <Plus className="size-4" aria-hidden="true" />
              新增角色
            </Button>
          </div>
        </form>
      </Dialog>
    </>
  );
}

type RoleRowActionsProps = {
  permissions: PermissionDefinition[];
  role: RoleDefinition;
};

export function RoleRowActions({ permissions, role }: RoleRowActionsProps): React.ReactElement {
  const router = useRouter();
  const [isEditOpen, setIsEditOpen] = useState(false);
  const [selectedPermissions, setSelectedPermissions] = useState<Permission[]>(role.permissions);
  const [message, setMessage] = useState("");
  const [isPending, setIsPending] = useState(false);

  function openEditDialog(): void {
    setMessage("");
    setSelectedPermissions(role.permissions);
    setIsEditOpen(true);
  }

  function closeEditDialog(): void {
    if (isPending) {
      return;
    }

    setIsEditOpen(false);
  }

  async function updateRole(payload: Record<string, string | boolean | string[]>): Promise<void> {
    setMessage("");
    setIsPending(true);
    const response = await fetch(`/api/settings/roles/${role.id}`, {
      method: "PATCH",
      headers: { "content-type": "application/json" },
      body: JSON.stringify(payload),
    });
    const result = (await response.json()) as ApiResult<{ role: RoleDefinition }>;
    setIsPending(false);

    if (!result.success) {
      setMessage(result.reason);
      return;
    }

    setMessage(result.reason);
    setIsEditOpen(false);
    router.refresh();
  }

  async function disableRole(): Promise<void> {
    setMessage("");
    setIsPending(true);
    const response = await fetch(`/api/settings/roles/${role.id}`, {
      method: "DELETE",
    });
    const result = (await response.json()) as ApiResult<{ role: RoleDefinition }>;
    setIsPending(false);

    if (!result.success) {
      setMessage(result.reason);
      return;
    }

    router.refresh();
  }

  async function handleSubmit(event: FormEvent<HTMLFormElement>): Promise<void> {
    event.preventDefault();
    const formData = new FormData(event.currentTarget);
    await updateRole({
      label: String(formData.get("label") ?? ""),
      key: String(formData.get("key") ?? ""),
      description: String(formData.get("description") ?? ""),
      isEnabled: String(formData.get("isEnabled") ?? "") === "true",
      permissions: selectedPermissions,
    });
  }

  if (role.isSystem) {
    return <span className="block text-right text-xs text-muted-foreground">内置只读</span>;
  }

  return (
    <div className="flex items-center justify-end gap-2">
      <Button onClick={openEditDialog} size="sm" type="button" variant="outline">
        <Pencil className="size-4" aria-hidden="true" />
        编辑
      </Button>
      {role.isEnabled ? (
        <Button disabled={isPending} onClick={() => void disableRole()} size="sm" type="button" variant="destructive">
          <Power className="size-4" aria-hidden="true" />
          停用
        </Button>
      ) : (
        <Button
          disabled={isPending}
          onClick={() => void updateRole({ isEnabled: true })}
          size="sm"
          type="button"
          variant="outline"
        >
          <Power className="size-4" aria-hidden="true" />
          启用
        </Button>
      )}
      <Dialog
        description="编辑自定义角色的名称、说明、状态和权限点。"
        onClose={closeEditDialog}
        open={isEditOpen}
        title="编辑角色"
        width="wide"
      >
        <form className="grid gap-4" onSubmit={handleSubmit}>
          <div className="grid gap-3 md:grid-cols-2">
            <Input defaultValue={role.label} label="角色名称" name="label" required />
            <Input defaultValue={role.key} label="角色标识" name="key" required />
          </div>
          <Input defaultValue={role.description} label="角色说明" name="description" />
          <Select
            defaultValue={String(role.isEnabled)}
            label="状态"
            name="isEnabled"
            options={[
              { value: "true", label: "启用" },
              { value: "false", label: "停用" },
            ]}
          />
          <PermissionTree
            onChange={setSelectedPermissions}
            permissions={permissions}
            selectedPermissions={selectedPermissions}
          />
          {message ? (
            <p className="rounded-md bg-secondary px-3 py-2 text-sm text-secondary-foreground" role="status">
              {message}
            </p>
          ) : null}
          <div className="flex justify-end gap-2">
            <Button onClick={closeEditDialog} type="button" variant="outline">
              取消
            </Button>
            <Button disabled={isPending} type="submit">
              保存
            </Button>
          </div>
        </form>
      </Dialog>
    </div>
  );
}
