import { useEffect, useState } from "react";
import { ApiError } from "../../api/client";
import {
  ROLE_DEFAULT_PERMISSIONS,
  ROLES,
} from "../../api/types";
import type { Permission, Role } from "../../api/types";
import { Button } from "../../components/ui/Button";
import { Field, Input } from "../../components/ui/Input";
import { Modal } from "../../components/ui/Modal";
import { useToast } from "../../components/ui/toast-context";
import { roleLabel } from "../../components/ui/status";
import { useCreateUser } from "./hooks";
import { PasswordField } from "./PasswordField";
import { PermissionsField } from "./PermissionsField";
import { scorePassword } from "./password";

interface CreateUserModalProps {
  open: boolean;
  onClose: () => void;
}

const ADMIN_PERMS_NOTE =
  "Admins implicitly hold every permission, so there is nothing to choose here.";

/**
 * Create-user form. Collects username, optional email, an initial password
 * (typed or generated + copied once), a role from the REAL role set, and a
 * permission set seeded from the role's server-side defaults. Client-side
 * validation gates the submit, but the server is authoritative.
 *
 * Credential handling: the typed/generated password lives only in this
 * component's state and is wiped whenever the dialog closes (success, cancel,
 * or escape). It is never logged. The server never returns it.
 */
export function CreateUserModal({ open, onClose }: CreateUserModalProps) {
  const toast = useToast();
  const create = useCreateUser();

  const [username, setUsername] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [role, setRole] = useState<Role>("operator");
  // Permissions track the role's defaults until the admin touches them.
  const [permissions, setPermissions] = useState<Permission[]>(
    ROLE_DEFAULT_PERMISSIONS.operator,
  );
  const [permsTouched, setPermsTouched] = useState(false);
  const [submitted, setSubmitted] = useState(false);

  // Reset everything (including the password) every time the dialog opens or
  // closes so no credential lingers in state across uses.
  useEffect(() => {
    setUsername("");
    setEmail("");
    setPassword("");
    setRole("operator");
    setPermissions(ROLE_DEFAULT_PERMISSIONS.operator);
    setPermsTouched(false);
    setSubmitted(false);
    create.reset();
    // create is stable; resetting on open/close only.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  function handleRoleChange(next: Role) {
    setRole(next);
    // Until the admin explicitly edits permissions, mirror the new role's
    // defaults so the preview matches what the server would seed.
    if (!permsTouched) setPermissions(ROLE_DEFAULT_PERMISSIONS[next]);
  }

  function handlePermsChange(next: Permission[]) {
    setPermsTouched(true);
    setPermissions(next);
  }

  const usernameError =
    submitted && username.trim().length === 0
      ? "A username is required."
      : undefined;
  const passwordStrength = scorePassword(password);
  const passwordError =
    submitted && !passwordStrength.meetsMinimum
      ? password.length === 0
        ? "An initial password is required."
        : "Password must be at least 8 characters."
      : undefined;

  const canSubmit =
    username.trim().length > 0 && passwordStrength.meetsMinimum;

  function handleSubmit() {
    setSubmitted(true);
    if (username.trim().length === 0 || !passwordStrength.meetsMinimum) return;

    // For admin, permissions are implicit server-side — don't send a redundant
    // set. For other roles, send the explicit set only when the admin edited
    // it; otherwise let the server seed its own role defaults.
    const sendPermissions =
      role !== "admin" && permsTouched ? permissions : undefined;

    create.mutate(
      {
        username: username.trim(),
        password,
        email: email.trim() ? email.trim() : null,
        role,
        permissions: sendPermissions,
      },
      {
        onSuccess: (user) => {
          toast.success(
            "User created",
            `${user.username} can sign in with the password you set.`,
          );
          // Wipe the password from state immediately on success.
          setPassword("");
          onClose();
        },
        onError: (err) => {
          toast.error(
            "Could not create user",
            err instanceof ApiError ? err.message : "Unexpected error.",
          );
        },
      },
    );
  }

  if (!open) return null;

  const roleSelectId = "create-user-role";

  return (
    <Modal
      open={open}
      title="Add user"
      ariaLabel="Add user"
      onClose={create.isPending ? () => {} : onClose}
      dismissable={!create.isPending}
      wide
      footer={
        <>
          <Button
            variant="ghost"
            onClick={onClose}
            disabled={create.isPending}
          >
            Cancel
          </Button>
          <Button
            variant="primary"
            onClick={handleSubmit}
            loading={create.isPending}
            disabled={!canSubmit && submitted}
          >
            Create user
          </Button>
        </>
      }
    >
      <div className="userform">
        <Field label="Username" htmlFor="create-user-username">
          <Input
            id="create-user-username"
            value={username}
            onChange={(e) => setUsername(e.target.value)}
            disabled={create.isPending}
            autoComplete="off"
            spellCheck={false}
            aria-invalid={usernameError ? true : undefined}
            aria-describedby={
              usernameError ? "create-user-username-err" : undefined
            }
          />
          {usernameError && (
            <p
              className="userform__err"
              id="create-user-username-err"
              role="alert"
            >
              {usernameError}
            </p>
          )}
        </Field>

        <Field label="Email (optional)" htmlFor="create-user-email">
          <Input
            id="create-user-email"
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            disabled={create.isPending}
            autoComplete="off"
            spellCheck={false}
          />
        </Field>

        <PasswordField
          label="Initial password"
          value={password}
          onChange={setPassword}
          disabled={create.isPending}
          error={passwordError}
        />

        <div className="field">
          <label className="field__label" htmlFor={roleSelectId}>
            Role
          </label>
          <select
            id={roleSelectId}
            className="select"
            value={role}
            disabled={create.isPending}
            onChange={(e) => handleRoleChange(e.target.value as Role)}
          >
            {ROLES.map((r) => (
              <option key={r} value={r}>
                {roleLabel(r)}
              </option>
            ))}
          </select>
        </div>

        <PermissionsField
          value={permissions}
          onChange={handlePermsChange}
          disabled={create.isPending}
          lockedNote={role === "admin" ? ADMIN_PERMS_NOTE : undefined}
        />
      </div>
    </Modal>
  );
}
