"use client";

import { useCallback, useEffect, useMemo, useState } from "react";
import { ChevronDown } from "lucide-react";
import { useConfirmAction } from "@/hooks/use-confirm-action";
import { useExportLabels } from "@/hooks/use-export-labels";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";

import {
  deleteAdminRole,
  getAdminRoles,
  getAdminUserPermissionCatalog,
  postAdminRole,
  putAdminRole,
  putAdminRolePermissions,
} from "@/api/admin-users";
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
import { Badge } from "@/components/ui/badge";
import { resolveRoleStatusTone } from "@/lib/admin-status-tone";
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { adminHasAnyPermission } from "@/lib/admin-permissions";
import { PRD_ADMIN_ROLE_MANAGE } from "@/lib/admin-prd";
import { cn } from "@/lib/utils";
import { useAdminProfile } from "@/stores/admin-session";
import type { AdminPermissionCatalogData, AdminRoleRow } from "@/types/api/index";
import { LotteryApiBizError } from "@/types/api/errors";

function permissionGroupLabel(key: string, fallback: string, t: (key: string) => string): string {
  const translated = t(`permissionGroups.${key}`);
  return translated === `permissionGroups.${key}` ? fallback : translated;
}

function permissionLabel(slug: string, fallback: string, t: (key: string) => string): string {
  const translated = t(`permissionNames.${slug}`);
  return translated === `permissionNames.${slug}` ? fallback : translated;
}

export function AdminRolesConsole(): React.ReactElement {
  const { t } = useTranslation(["adminUsers", "common"]);
  const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
  const profile = useAdminProfile();
  const canManageRoles = adminHasAnyPermission(profile?.permissions, [PRD_ADMIN_ROLE_MANAGE]);
  const exportLabels = useExportLabels("adminRoles");
  const [catalog, setCatalog] = useState<AdminPermissionCatalogData | null>(null);
  const [roles, setRoles] = useState<AdminRoleRow[]>([]);
  const [loading, setLoading] = useState(true);
  const [err, setErr] = useState<string | null>(null);
  const [directMenuExpanded, setDirectMenuExpanded] = useState<Record<string, boolean>>({});

  const [rolePermissionOpen, setRolePermissionOpen] = useState(false);
  const [selectedRoleId, setSelectedRoleId] = useState<number | null>(null);
  const [draftRolePermissions, setDraftRolePermissions] = useState<string[]>([]);
  const [roleSaving, setRoleSaving] = useState(false);

  const [roleDialogOpen, setRoleDialogOpen] = useState(false);
  const [roleMode, setRoleMode] = useState<"create" | "edit">("create");
  const [editingRoleId, setEditingRoleId] = useState<number | null>(null);
  const [roleSlug, setRoleSlug] = useState("");
  const [roleName, setRoleName] = useState("");
  const [roleDescription, setRoleDescription] = useState("");
  const [roleStatus, setRoleStatus] = useState(1);
  const [roleFormSaving, setRoleFormSaving] = useState(false);

  const [roleDeleteTarget, setRoleDeleteTarget] = useState<AdminRoleRow | null>(null);
  const [roleDeleteBusy, setRoleDeleteBusy] = useState(false);

  const selectedRole = useMemo(
    () => roles.find((role) => role.id === selectedRoleId) ?? null,
    [roles, selectedRoleId],
  );
  const selectedPermissionSet = useMemo(
    () => new Set(draftRolePermissions),
    [draftRolePermissions],
  );

  const directPermissionGroups = useMemo(() => {
    const groups = catalog?.permission_menu_groups;
    if (groups && groups.length > 0) {
      return groups;
    }
    const flatPermissions = catalog?.permissions ?? [];
    if (flatPermissions.length > 0) {
      return [{ key: "all", label: t("allPermissions"), permissions: flatPermissions }];
    }
    return [];
  }, [catalog, t]);

  const load = useCallback(async () => {
    setLoading(true);
    setErr(null);
    try {
      const [catalogData, roleData] = await Promise.all([
        getAdminUserPermissionCatalog(),
        getAdminRoles(),
      ]);
      setCatalog(catalogData);
      setRoles(roleData.items);
    } catch (e) {
      const msg = e instanceof LotteryApiBizError ? e.message : t("roleLoadFailed");
      setErr(msg);
      setRoles([]);
    } finally {
      setLoading(false);
    }
  }, [t]);

  useEffect(() => {
    queueMicrotask(() => {
      void load();
    });
  }, [load]);

  function isDirectGroupOpen(key: string): boolean {
    return directMenuExpanded[key] === true;
  }

  function toggleDirectGroup(key: string): void {
    setDirectMenuExpanded((prev) => {
      const wasOpen = prev[key] === true;
      return { ...prev, [key]: !wasOpen };
    });
  }

  function toggleRolePermission(slug: string, checked: boolean): void {
    setDraftRolePermissions((prev) => {
      if (checked) {
        return Array.from(new Set([...prev, slug])).sort();
      }
      return prev.filter((value) => value !== slug);
    });
  }

  function toggleGroupPermissions(slugs: string[], checked: boolean): void {
    setDraftRolePermissions((prev) => {
      if (checked) {
        return Array.from(new Set([...prev, ...slugs])).sort();
      }
      const remove = new Set(slugs);
      return prev.filter((value) => !remove.has(value));
    });
  }

  function openCreateRole(): void {
    setRoleMode("create");
    setEditingRoleId(null);
    setRoleSlug("");
    setRoleName("");
    setRoleDescription("");
    setRoleStatus(1);
    setRoleDialogOpen(true);
  }

  function openEditRole(role: AdminRoleRow): void {
    setRoleMode("edit");
    setEditingRoleId(role.id);
    setRoleSlug(role.slug);
    setRoleName(role.name);
    setRoleDescription(role.description ?? "");
    setRoleStatus(role.status);
    setRoleDialogOpen(true);
  }

  function openRolePermissionEditor(role: AdminRoleRow): void {
    setSelectedRoleId(role.id);
    setDraftRolePermissions([...role.permission_slugs].sort());
    setDirectMenuExpanded({});
    setRolePermissionOpen(true);
  }

  function handleRoleDialogOpenChange(open: boolean): void {
    setRoleDialogOpen(open);
    if (!open) {
      setEditingRoleId(null);
    }
  }

  function handleRolePermissionDialogOpenChange(open: boolean): void {
    setRolePermissionOpen(open);
    if (!open) {
      setSelectedRoleId(null);
    }
  }

  function getGroupSelectionState(slugs: string[]): boolean | "indeterminate" {
    if (slugs.length === 0) {
      return false;
    }
    const selectedCount = slugs.filter((slug) => selectedPermissionSet.has(slug)).length;
    if (selectedCount === 0) {
      return false;
    }
    if (selectedCount === slugs.length) {
      return true;
    }
    return "indeterminate";
  }

  async function saveRolePermissions(): Promise<void> {
    if (!selectedRole) {
      return;
    }
    setRoleSaving(true);
    try {
      const result = await putAdminRolePermissions(selectedRole.id, draftRolePermissions);
      setDraftRolePermissions([...result.permission_slugs].sort());
      setRoles((prev) => prev.map((role) => (role.id === result.id ? result : role)));
      setCatalog((prev) =>
        prev ? { ...prev, roles: prev.roles.map((role) => (role.id === result.id ? result : role)) } : prev,
      );
      toast.success(t("rolePermissionSaveSuccess"));
    } catch (e) {
      const msg = e instanceof LotteryApiBizError ? e.message : t("rolePermissionSaveFailed");
      toast.error(msg);
    } finally {
      setRoleSaving(false);
    }
  }

  async function submitRole(): Promise<void> {
    const name = roleName.trim();
    const slug = roleSlug.trim().toLowerCase();
    if (name === "" || slug === "") {
      toast.error(t("roleFormRequired"));
      return;
    }

    setRoleFormSaving(true);
    try {
      if (roleMode === "create") {
        const created = await postAdminRole({
          slug,
          name,
          description: roleDescription.trim() === "" ? null : roleDescription.trim(),
          status: roleStatus,
        });
        setRoles((prev) => [...prev, created]);
        setCatalog((prev) => (prev ? { ...prev, roles: [...prev.roles, created] } : prev));
        toast.success(t("roleCreateSuccess", { name: created.name }));
      } else {
        if (editingRoleId === null) {
          return;
        }
        const updated = await putAdminRole(editingRoleId, {
          slug,
          name,
          description: roleDescription.trim() === "" ? null : roleDescription.trim(),
          status: roleStatus,
        });
        setRoles((prev) => prev.map((role) => (role.id === updated.id ? updated : role)));
        setCatalog((prev) =>
          prev ? { ...prev, roles: prev.roles.map((role) => (role.id === updated.id ? updated : role)) } : prev,
        );
        toast.success(t("roleUpdateSuccess", { name: updated.name }));
      }
      handleRoleDialogOpenChange(false);
    } catch (e) {
      const msg = e instanceof LotteryApiBizError ? e.message : t("roleSaveFailed");
      toast.error(msg);
    } finally {
      setRoleFormSaving(false);
    }
  }

  async function confirmRoleDelete(): Promise<void> {
    if (!roleDeleteTarget) {
      return;
    }
    setRoleDeleteBusy(true);
    try {
      await deleteAdminRole(roleDeleteTarget.id);
      setRoles((prev) => prev.filter((role) => role.id !== roleDeleteTarget.id));
      setCatalog((prev) =>
        prev ? { ...prev, roles: prev.roles.filter((role) => role.id !== roleDeleteTarget.id) } : prev,
      );
      toast.success(t("roleDeleteSuccess", { name: roleDeleteTarget.name }));
      setRoleDeleteTarget(null);
    } catch (e) {
      const msg = e instanceof LotteryApiBizError ? e.message : t("roleDeleteFailed");
      toast.error(msg);
    } finally {
      setRoleDeleteBusy(false);
    }
  }

  return (
    <div className="flex w-full max-w-none flex-col gap-6">
      <Card>
        <CardHeader className="flex flex-row flex-wrap items-end justify-between gap-4">
          <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
            <CardTitle>{t("roleListTitle")}</CardTitle>
            {canManageRoles ? (
              <Button type="button" size="sm" onClick={() => openCreateRole()}>
                {t("createRole")}
              </Button>
            ) : null}
          </div>
          <div className="admin-list-actions">
            <AdminTableExportButton
              tableId="admin-roles-table"
              filename={exportLabels.filename}
              sheetName={exportLabels.sheetName}
            />
            <Button type="button" variant="secondary" onClick={() => void load()}>
              {t("actions.refresh", { ns: "common" })}
            </Button>
          </div>
        </CardHeader>
        <CardContent className="space-y-4">
          {err ? <p className="text-sm text-red-600 dark:text-red-400">{err}</p> : null}
          {loading && roles.length === 0 ? (
            <p className="text-sm text-muted-foreground">{t("states.loading", { ns: "common" })}</p>
          ) : null}
          <div className="rounded-md border">
            <Table id="admin-roles-table">
              <TableHeader>
                <TableRow>
                  <TableHead className="w-16">{t("table.id", { ns: "common" })}</TableHead>
                  <TableHead>{t("roleTable.name")}</TableHead>
                  <TableHead>{t("roleTable.slug")}</TableHead>
                  <TableHead>{t("roleTable.type")}</TableHead>
                  <TableHead>{t("roleTable.status")}</TableHead>
                  <TableHead>{t("roleTable.users")}</TableHead>
                  <TableHead>{t("roleTable.permissions")}</TableHead>
                  <TableHead>{t("roleTable.actions")}</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {roles.length === 0 ? (
                  <TableRow>
                    <TableCell colSpan={8} className="text-muted-foreground">
                      {t("states.noData", { ns: "common" })}
                    </TableCell>
                  </TableRow>
                ) : (
                  roles.map((role) => (
                    <TableRow key={role.id}>
                      <TableCell>{role.id}</TableCell>
                      <TableCell>
                        <div className="flex flex-col">
                          <span className="font-medium">{role.name}</span>
                          <span className="text-xs text-muted-foreground">{role.description ?? ""}</span>
                        </div>
                      </TableCell>
                      <TableCell>{role.slug}</TableCell>
                      <TableCell>
                        {role.is_system ? (
                          <Badge variant="secondary">{t("roleType.system")}</Badge>
                        ) : (
                          <Badge variant="outline">{t("roleType.custom")}</Badge>
                        )}
                      </TableCell>
                      <TableCell>
                        <AdminStatusBadge status={role.status} tone={resolveRoleStatusTone(role.status)}>
                          {role.status === 1 ? t("status.enabled") : t("status.disabled")}
                        </AdminStatusBadge>
                      </TableCell>
                      <TableCell className="tabular-nums">{role.user_count}</TableCell>
                      <TableCell className="tabular-nums">{role.permission_slugs.length}</TableCell>
                      <TableCell>
                        {canManageRoles ? (
                          <div className="flex flex-wrap gap-1">
                            <Button type="button" size="sm" variant="outline" onClick={() => openRolePermissionEditor(role)}>
                              {t("roleActions.permissions")}
                            </Button>
                            <Button type="button" size="sm" variant="outline" onClick={() => openEditRole(role)}>
                              {t("actions.edit")}
                            </Button>
                            <Button
                              type="button"
                              size="sm"
                              variant="destructive"
                              disabled={role.is_system || role.user_count > 0}
                              onClick={() => setRoleDeleteTarget(role)}
                            >
                              {t("actions.delete")}
                            </Button>
                          </div>
                        ) : (
                          <span className="text-xs text-muted-foreground">—</span>
                        )}
                      </TableCell>
                    </TableRow>
                  ))
                )}
              </TableBody>
            </Table>
          </div>
        </CardContent>
      </Card>

      <Dialog open={rolePermissionOpen} onOpenChange={handleRolePermissionDialogOpenChange}>
        <DialogContent
          showCloseButton
          className="flex h-[min(84vh,760px)] !max-w-[min(720px,calc(100vw-2rem))] flex-col gap-0 overflow-hidden rounded-2xl border bg-background p-0 shadow-2xl"
        >
          <DialogHeader className="shrink-0 space-y-1 border-b bg-background px-5 py-4 pr-12">
            <DialogTitle className="text-[15px] font-semibold tracking-tight text-foreground">
              {t("rolePermissionDialog.title")}
            </DialogTitle>
            <DialogDescription className="text-sm text-muted-foreground">
              {selectedRole ? selectedRole.name : null}
            </DialogDescription>
          </DialogHeader>
          <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain bg-muted/15 px-5 py-4">
            <div className="overflow-hidden rounded-xl border border-border/70 bg-background">
              {directPermissionGroups.map((group) => {
                const isOpen = isDirectGroupOpen(group.key);
                const groupSlugs = group.permissions.map((permission) => permission.slug);
                const selectedCount = group.permissions.filter((permission) =>
                  selectedPermissionSet.has(permission.slug),
                ).length;
                const checkedState = getGroupSelectionState(groupSlugs);

                return (
                  <div key={group.key} className={cn("border-b border-border/60 last:border-b-0", isOpen && "bg-muted/10")}>
                    <div className="flex items-center gap-3 px-4 py-3 text-sm transition-colors hover:bg-muted/20">
                      <button
                        type="button"
                        className="flex size-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted"
                        onClick={() => toggleDirectGroup(group.key)}
                        aria-label={isOpen ? t("aria.collapse", { ns: "common" }) : t("aria.expand", { ns: "common" })}
                      >
                        <ChevronDown
                          aria-hidden
                          className={cn("size-4 transition-transform", isOpen && "rotate-180")}
                        />
                      </button>
                      <Checkbox
                        checked={checkedState === true}
                        indeterminate={checkedState === "indeterminate"}
                        onCheckedChange={(value) => toggleGroupPermissions(groupSlugs, value === true)}
                      />
                      <button
                        type="button"
                        className="min-w-0 flex-1 text-left"
                        onClick={() => toggleDirectGroup(group.key)}
                      >
                        <div className="flex min-w-0 items-center gap-2">
                          <span className="min-w-0 truncate text-[15px] font-medium leading-6 text-foreground">
                            {permissionGroupLabel(group.key, group.label, t)}
                          </span>
                          {group.permissions.length > 0 ? (
                            <span className="shrink-0 rounded bg-muted px-1.5 py-0.5 text-[11px] text-muted-foreground">
                              {group.permissions.length}
                            </span>
                          ) : null}
                        </div>
                      </button>
                      <span className="shrink-0 tabular-nums text-xs text-muted-foreground">
                        {selectedCount}/{group.permissions.length}
                      </span>
                    </div>
                    {isOpen ? (
                      <div className="pb-2">
                        {group.permissions.map((permission, index) => (
                          <label
                            key={permission.slug}
                            className={cn(
                              "flex cursor-pointer items-start gap-3 px-4 py-2.5 text-sm transition-colors hover:bg-muted/20",
                              index === 0 && "border-t border-border/50",
                              selectedPermissionSet.has(permission.slug) && "bg-muted/20",
                            )}
                          >
                            <span className="mt-1 flex h-4 w-8 shrink-0 items-center">
                              <span className="h-px w-full bg-border/70" />
                            </span>
                            <Checkbox
                              className="mt-0.5"
                              checked={selectedPermissionSet.has(permission.slug)}
                              onCheckedChange={(value) =>
                                toggleRolePermission(permission.slug, value === true)
                              }
                            />
                            <span className="min-w-0 whitespace-normal break-words leading-6 text-foreground">
                              {permissionLabel(permission.slug, permission.name, t)}
                            </span>
                          </label>
                        ))}
                      </div>
                    ) : null}
                  </div>
                );
              })}
            </div>
          </div>
          <div className="flex shrink-0 justify-end gap-2 border-t bg-background px-5 py-4">
            <Button type="button" variant="outline" onClick={() => handleRolePermissionDialogOpenChange(false)}>
              {t("actions.cancel")}
            </Button>
            <Button
              type="button"
              disabled={!selectedRole || roleSaving}
              onClick={() =>
                selectedRole &&
                requestConfirm({
                  title: t("confirmSaveRolePermissionsTitle"),
                  description: t("confirmSaveRolePermissionsDescription", { name: selectedRole.name }),
                  confirmLabel: t("confirm.confirmSave", { ns: "common" }),
                  confirmVariant: "destructive",
                  onConfirm: () => saveRolePermissions(),
                })
              }
            >
              {roleSaving ? t("saving") : t("actions.save")}
            </Button>
          </div>
        </DialogContent>
      </Dialog>

      <Dialog open={roleDialogOpen} onOpenChange={handleRoleDialogOpenChange}>
        <DialogContent showCloseButton className="max-w-lg gap-4">
          <DialogHeader>
            <DialogTitle>
              {roleMode === "create" ? t("roleDialog.createTitle") : t("roleDialog.editTitle")}
            </DialogTitle>
            <DialogDescription>{t("roleDialog.description")}</DialogDescription>
          </DialogHeader>
          <div className="space-y-3">
            <div className="space-y-1.5">
              <div className="text-sm font-medium leading-none">{t("roleDialog.slug")}</div>
              <Input value={roleSlug} onChange={(e) => setRoleSlug(e.target.value)} disabled={roleMode === "edit"} />
            </div>
            <div className="space-y-1.5">
              <div className="text-sm font-medium leading-none">{t("roleDialog.name")}</div>
              <Input value={roleName} onChange={(e) => setRoleName(e.target.value)} />
            </div>
            <div className="space-y-1.5">
              <div className="text-sm font-medium leading-none">{t("roleDialog.descriptionLabel")}</div>
              <Input value={roleDescription} onChange={(e) => setRoleDescription(e.target.value)} />
            </div>
            <div className="flex items-center justify-between rounded-xl border border-border/70 p-3">
              <div className="space-y-1">
                <p className="text-sm font-medium">{t("roleDialog.status")}</p>
                <p className="text-xs text-muted-foreground">
                  {roleStatus === 1 ? t("status.enabled") : t("status.disabled")}
                </p>
              </div>
              <Switch
                checked={roleStatus === 1}
                disabled={roleFormSaving}
                aria-label={t("roleDialog.status")}
                onCheckedChange={(checked) => setRoleStatus(checked ? 1 : 0)}
              />
            </div>
          </div>
          <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
            <Button type="button" variant="outline" onClick={() => handleRoleDialogOpenChange(false)}>
              {t("actions.cancel")}
            </Button>
            <Button
              type="button"
              disabled={roleFormSaving}
              onClick={() =>
                requestConfirm({
                  title: t("confirmSaveRoleTitle"),
                  description:
                    roleMode === "create"
                      ? t("confirmSaveRoleCreateDescription", { name: roleName || roleSlug || "—" })
                      : t("confirmSaveRoleEditDescription", { name: roleName || "—" }),
                  confirmLabel: t("confirm.confirmSave", { ns: "common" }),
                  onConfirm: () => submitRole(),
                })
              }
            >
              {roleFormSaving ? t("saving") : t("actions.save")}
            </Button>
          </div>
        </DialogContent>
      </Dialog>

      <Dialog open={roleDeleteTarget !== null} onOpenChange={(open) => !open && setRoleDeleteTarget(null)}>
        <DialogContent showCloseButton className="max-w-md gap-4">
          <DialogHeader>
            <DialogTitle>{t("roleDelete.confirmTitle")}</DialogTitle>
            <DialogDescription>
              {roleDeleteTarget ? t("roleDelete.confirmDescription", { name: roleDeleteTarget.name }) : null}
            </DialogDescription>
          </DialogHeader>
          <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
            <Button type="button" variant="outline" disabled={roleDeleteBusy} onClick={() => setRoleDeleteTarget(null)}>
              {t("actions.cancel")}
            </Button>
            <Button type="button" variant="destructive" disabled={roleDeleteBusy} onClick={() => void confirmRoleDelete()}>
              {roleDeleteBusy ? t("deleting") : t("actions.delete")}
            </Button>
          </div>
        </DialogContent>
      </Dialog>
      <ConfirmDialog />
    </div>
  );
}
