"use client";

import type { ManagedUser, Role } from "@domainops/shared-types";
import { App, Checkbox, Divider, Modal, Select, Switch, Typography } from "antd";
import { CrownFilled } from "@ant-design/icons";
import { useEffect, useState } from "react";
import { setUserCrown, updateRoleAccess } from "@/lib/api/users";
import { ApiError } from "@/lib/api/client";
import { titleCase } from "@/lib/utils/text";

const PERMISSION_GROUPS: { title: string; permissions: string[] }[] = [
  {
    title: "Domains & Redirects",
    permissions: ["domain.add", "domain.edit", "domain.archive", "domain.deploy", "domain.provision", "domain.seo", "redirect.create", "redirect.revert"],
  },
  {
    title: "WordPress",
    permissions: ["wp.manage", "wp.reveal"],
  },
  {
    title: "Projects & Regions",
    permissions: ["brand.add", "brand.edit", "brand.archive", "region.add", "region.edit", "region.archive"],
  },
  {
    title: "Servers & Cloudflare",
    permissions: ["server_group.add", "server_group.edit", "server_account.add", "cloudflare.add", "domain.open_cpanel"],
  },
  {
    title: "Registrars & Acquisition",
    permissions: [
      "registrar.add",
      "registrar.delete",
      "acquire.access",
      "acquire.purchase",
      "auction.access",
      "auction.add",
      "auction.bid",
    ],
  },
  { title: "Assistant", permissions: ["assistant.chat"] },
  { title: "Settings", permissions: ["settings.global.edit", "settings.theme.edit"] },
  {
    title: "Users & Access",
    permissions: ["users.manage", "user.password_reset", "user.2fa_reset", "user.access_manage", "user.force_logout"],
  },
];

const ALL_PERMISSIONS = PERMISSION_GROUPS.flatMap((g) => g.permissions);

/**
 * A role's default_action_permissions can be the literal ["*"] (e.g. a Crowned Admin,
 * seeded with full access) rather than an explicit grant list - canDo()
 * treats that as "everything", but the Checkbox.Group below only matches
 * exact permission strings, so "*" would show every box unchecked and
 * unchecking one wouldn't actually remove "*" itself. Expand it to every
 * known permission up front; saving after that crystallizes "*" into an
 * explicit list from then on.
 */
function resolvedPermissions(role?: Role): string[] {
  const current = role?.default_action_permissions ?? [];
  return current.includes("*") ? ALL_PERMISSIONS : current;
}

/**
 * Super-admin only - see RoleController::update()'s docblock. Edits a
 * role's default_action_permissions directly rather than per-user grants;
 * every user on that role picks up the change immediately (canDo() falls
 * back to the role's defaults whenever a user has no explicit override).
 */
export function ManageRolesModal({
  open,
  roles,
  users,
  onClose,
  onSaved,
  onCrownChanged,
}: {
  open: boolean;
  roles: Role[];
  users: ManagedUser[];
  onClose: () => void;
  onSaved: () => void;
  onCrownChanged: () => void;
}) {
  const { message } = App.useApp();
  const [selectedRoleId, setSelectedRoleId] = useState<number | undefined>();
  const [checked, setChecked] = useState<string[]>([]);
  const [isSaving, setIsSaving] = useState(false);
  const [crowningId, setCrowningId] = useState<number | null>(null);

  // The protected (super admin) role is always '*' and can never be edited
  // (RoleController::update() rejects it with a 422) - excluded here so
  // picking it can't lead to a save that's guaranteed to fail.
  const editableRoles = roles.filter((r) => !r.is_protected);

  useEffect(() => {
    if (!open) return;
    const firstRole = editableRoles[0];
    // Resets the modal's local selection to the first role every time it
    // opens - a one-time hydration from props, not a state-sync bug.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setSelectedRoleId(firstRole?.id);
    setChecked(resolvedPermissions(firstRole));
    // editableRoles is derived fresh from `roles` every render - depending
    // on `roles` alone avoids re-running this on every render.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open, roles]);

  function handleRoleChange(roleId: number) {
    setSelectedRoleId(roleId);
    setChecked(resolvedPermissions(editableRoles.find((r) => r.id === roleId)));
  }

  async function handleSave() {
    if (!selectedRoleId) return;
    setIsSaving(true);
    try {
      await updateRoleAccess(selectedRoleId, checked);
      message.success("Feature access updated.");
      onSaved();
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't update feature access.");
    } finally {
      setIsSaving(false);
    }
  }

  async function handleToggleCrown(user: ManagedUser, isCrowned: boolean) {
    setCrowningId(user.id);
    try {
      await setUserCrown(user.id, isCrowned);
      message.success(isCrowned ? `${user.name} crowned.` : `${user.name}'s crown removed.`);
      onCrownChanged();
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't update that.");
    } finally {
      setCrowningId(null);
    }
  }

  return (
    <Modal
      title="Manage Feature Access"
      open={open}
      onCancel={onClose}
      onOk={handleSave}
      confirmLoading={isSaving}
      okText="Save"
      width={640}
      destroyOnHidden
      className="dms_manage_roles_modal"
    >
      <div className="flex flex-col gap-4">
        <Select
          value={selectedRoleId}
          options={editableRoles.map((r) => ({ value: r.id, label: titleCase(r.label) }))}
          onChange={handleRoleChange}
          className="w-full"
        />

        <Checkbox.Group value={checked} onChange={(values) => setChecked(values as string[])} className="w-full">
          <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
            {PERMISSION_GROUPS.map((group) => (
              <div key={group.title}>
                <Typography.Text strong className="block mb-1">
                  {group.title}
                </Typography.Text>
                <div className="flex flex-col gap-1">
                  {group.permissions.map((permission) => (
                    <Checkbox key={permission} value={permission}>
                      {permission}
                    </Checkbox>
                  ))}
                </div>
              </div>
            ))}
          </div>
        </Checkbox.Group>

        <Divider style={{ margin: "4px 0" }} />

        <div>
          <Typography.Text strong className="block mb-1">
            Crowned Admin (Super Admin Only)
          </Typography.Text>
          <Typography.Paragraph type="secondary" style={{ marginBottom: 8 }}>
            The crown turns an Admin into a Crowned Admin: full access to every page and action, but never able to
            touch a Super Admin. Only Admins can be crowned.
          </Typography.Paragraph>
          <div className="flex flex-col gap-2 max-h-48 overflow-y-auto">
            {users.filter((u) => u.role?.label === "admin").map((u) => (
              <div key={u.id} className="flex items-center justify-between gap-3">
                <span className="flex items-center gap-1.5">
                  {u.is_crowned && <CrownFilled className="text-amber-500" />}
                  {u.name}
                </span>
                <Switch
                  checked={u.is_crowned}
                  disabled={!!u.is_fixed}
                  loading={crowningId === u.id}
                  onChange={(checked) => void handleToggleCrown(u, checked)}
                />
              </div>
            ))}
          </div>
        </div>
      </div>
    </Modal>
  );
}
