'use client';

import {
  useActionState,
  useEffect,
  useRef,
  useState,
  useTransition,
} from 'react';
import { FoxButton } from '@fox/core/components/fox-button';
import { FoxButtonGroup } from '@fox/core/components/fox-button-group';
import { FoxEmail } from '@fox/core/components/fox-email';
import { FoxInput } from '@fox/core/components/fox-input';
import { FoxModal } from '@fox/core/components/fox-modal';
import {
  foxPasswordValidator,
  foxValidationMessage,
  foxValidators,
} from '@fox/core/validation';
import { FoxChatCenteredDotsIcon } from '@fox/core/icons';
import { useFeedback } from '@/app/_hooks/use-feedback';
import { DEFAULT_ADMIN_ROLE_CODE } from '@/lib/domain/admin-member';
import { DEFAULT_MOBILE_PHONE_PREFIX } from '@/lib/domain/phone-number';
import {
  ADMIN_EMAIL_MESSAGES,
  ADMIN_LOGIN_ID_HELP_TEXT,
  ADMIN_LOGIN_ID_MESSAGES,
  ADMIN_LOGIN_ID_VALIDATORS,
  ADMIN_PASSWORD_ERROR_TEXT,
  ADMIN_PASSWORD_HELP_TEXT,
  ADMIN_PASSWORD_POLICY,
  INITIAL_ADMIN_MEMBER_FORM_STATE,
  adminEmailValidators,
} from '@/lib/domain/admin-member-form';
import {
  checkAdminLoginId,
  createAdminMemberAction,
  type LoginIdCheckResult,
} from '../_actions';
import { AdminMemberFormFields } from './admin-member-form-fields';
import styles from './admin-member-modal.module.scss';
import { submitFormAction } from '@/app/_hooks/submit-form-action';

interface AdminMemberCreateModalProps {
  onClose: () => void;
}

/** 시안의 도메인 목록. 맨 아래 "직접입력" 항목은 `FoxEmail`이 스스로 더한다. */
const EMAIL_DOMAIN_OPTIONS = [
  'naver.com',
  'gmail.com',
  'daum.net',
  'hanmail.net',
  'nate.com',
];

/**
 * 관리자 등록 팝업 — 시안: 관리자페이지(QsFVFFUKGH68xAPVkF3SJ9) ADM_ADM_102_p (5227:2035)
 *
 * **ID는 중복 확인을 통과해야 저장할 수 있다**(시안 ①). 확인은 등록 폼과 별개의 서버 호출인데,
 * 폼 안에 폼을 중첩할 수 없어 `useActionState` 대신 값을 직접 넘기는 Server Action
 * (`checkAdminLoginId`)을 `useTransition`으로 호출한다. 확인 후 사용자가 ID를 고칠 수 있으므로
 * "확인에 성공한 ID"와 "지금 입력창의 값"이 같을 때만 통과로 본다 — 확인만 받아 두고 다른 ID로
 * 바꿔 저장하는 경로를 막기 위해서다. 그래도 최종 방어선은 Server Action의 재확인이다.
 *
 * 저장 버튼은 `FoxModal`의 actions 슬롯에 있어 폼 **바깥**에 그려진다. `FoxButton`에는 네이티브
 * `form` 속성이 없으므로 폼을 ref로 잡아 `requestSubmit()`을 부른다 — `useActionState`의 폼
 * 액션이 그대로 타는 정식 제출이라 검증·상태 흐름이 동일하다.
 *
 * @fox의 제어 위젯(전화번호·이메일·역할·메뉴)은 값이 FormData에 실리지 않아 이 컴포넌트가 값을
 * 들고 hidden input으로 제출한다(`AdminMemberFormFields` 주석 참조).
 *
 * 비밀번호는 형식만 검증하고 백엔드가 SHA-256으로 암호화해 저장한다(`MngrAdminServiceImpl`).
 */
export function AdminMemberCreateModal({
  onClose,
}: AdminMemberCreateModalProps) {
  const { showToast } = useFeedback();
  const [state, formAction, isPending] = useActionState(
    createAdminMemberAction,
    INITIAL_ADMIN_MEMBER_FORM_STATE
  );
  const formRef = useRef<HTMLFormElement>(null);

  const [loginId, setLoginId] = useState('');
  const [checkResult, setCheckResult] = useState<LoginIdCheckResult>({
    status: 'idle',
  });
  const [isChecking, startChecking] = useTransition();

  // 앞자리는 고르지 않아도 되도록 기본값으로 시작한다(상수 주석 참조).
  const [phoneNumber, setPhoneNumber] = useState(DEFAULT_MOBILE_PHONE_PREFIX);
  // `FoxEmail`이 아이디·도메인을 스스로 나눠 들고 합친 값 하나를 내보낸다.
  // 도메인은 시안처럼 첫 항목이 골라진 채로 시작한다(아이디만 적으면 되게).
  const [emailValue, setEmailValue] = useState(`@${EMAIL_DOMAIN_OPTIONS[0]}`);
  // 이메일은 칸이 둘로 나뉘어 있어 검증 대상이 **합친 값**이다 — FoxInput 한 칸이 스스로
  // 판정할 수 없어 여기서 돌리고, `touched`도 직접 든다(FoxInput의 blur 규칙과 같은 시점).
  const [isEmailTouched, setIsEmailTouched] = useState(false);
  const [roleCode, setRoleCode] = useState<string>(DEFAULT_ADMIN_ROLE_CODE);
  const [menuCodes, setMenuCodes] = useState<string[]>([]);

  useEffect(() => {
    if (state.status === 'success') {
      showToast({ variant: 'success', message: '관리자를 등록했습니다.' });
      onClose();
    }
  }, [state, showToast, onClose]);

  const isLoginIdVerified =
    checkResult.status === 'available' && checkResult.loginId === loginId.trim();

  const errors = state.status === 'error' ? (state.errors ?? {}) : {};

  // 둘 중 하나라도 비면 **빈 이메일**로 본다 — 도메인만 고른 상태(`@naver.com`)를 값으로 보내면
  // "형식이 올바르지 않습니다"가 뜨지만, 사용자가 한 일은 아무것도 입력하지 않은 것이다.
  // (`FoxEmail`은 한쪽만 채워도 `@`를 붙여 내보내므로 여기서 다시 가른다.)
  const [emailLocal = '', emailDomain = ''] = emailValue.split('@');
  const email = emailLocal && emailDomain ? emailValue : '';

  const emailMessage =
    errors.email ??
    (isEmailTouched
      ? foxValidationMessage(
          foxValidators.compose(adminEmailValidators(true))(email),
          ADMIN_EMAIL_MESSAGES
        )
      : undefined);

  // 중복 확인 결과는 화면 규칙보다 먼저 보여준다 — 방금 누른 버튼의 답이기 때문이다.
  // 값이 나쁜 것(unavailable)과 확인을 못 한 것(failed)은 사유가 다르므로 둘 다 문구로 낸다.
  const loginIdFailure =
    checkResult.status === 'unavailable' || checkResult.status === 'failed'
      ? checkResult.message
      : undefined;
  // **서버 오류는 성공 문구를 이긴다** — 확인을 통과한 뒤에도 저장이 ID 때문에 거부될 수 있는데
  // (확인과 저장 사이의 선점, 형식 규칙 변경), '사용할 수 있는 ID입니다.'가 계속 떠 있으면
  // 거부된 사실이 화면에서 사라진다.
  const loginIdMessage =
    loginIdFailure ??
    errors.loginId ??
    (isLoginIdVerified ? '사용할 수 있는 ID입니다.' : ADMIN_LOGIN_ID_HELP_TEXT);

  return (
    <FoxModal
      open
      onClose={onClose}
      size="sm"
      title="관리자 등록"
      actions={
        <FoxButtonGroup size="md">
          <FoxButton type="default" size="md" label="취소" onAction={onClose} />
          <FoxButton
            type="primary"
            size="md"
            label="저장"
            loading={isPending}
            disabled={!isLoginIdVerified}
            onAction={() => formRef.current?.requestSubmit()}
          />
        </FoxButtonGroup>
      }
    >
      <form ref={formRef} onSubmit={(event) => submitFormAction(event, formAction)} className={styles.formList}>
        <FoxInput
          size="md"
          name="name"
          label="이름"
          requirement="required"
          placeholder="이름을 입력하세요."
          message={errors.name}
          invalid={Boolean(errors.name)}
        />

        <div className={styles.fieldWithAction}>
          <FoxInput
            size="md"
            name="loginId"
            label="ID"
            requirement="required"
            placeholder="관리자 ID를 입력하세요."
            autoComplete="off"
            value={loginId}
            onChange={setLoginId}
            message={loginIdMessage}
            messageIcon={<FoxChatCenteredDotsIcon />}
            // 중복 확인 결과·서버 오류는 화면 규칙이 알 수 없는 사유라 이쪽이 이긴다.
            invalid={Boolean(loginIdFailure) || Boolean(errors.loginId)}
            validators={ADMIN_LOGIN_ID_VALIDATORS}
            validationMessages={ADMIN_LOGIN_ID_MESSAGES}
          />
          <FoxButton
            type="secondary"
            size="md"
            fullWidth
            label="중복 확인"
            loading={isChecking}
            disabled={loginId.trim().length === 0}
            onAction={() =>
              startChecking(async () => {
                try {
                  setCheckResult(await checkAdminLoginId(loginId));
                } catch {
                  // Server Action 호출 자체가 깨진 경우(네트워크 등). 조용히 끝나면 버튼만
                  // 원복되고 아무 말도 남지 않아, 사유를 알 수 없어도 실패는 알린다.
                  setCheckResult({
                    status: 'failed',
                    message: '중복 확인에 실패했습니다. 잠시 후 다시 시도해 주세요.',
                  });
                }
              })
            }
          />
        </div>

        <FoxInput
          size="md"
          type="password"
          name="password"
          label="비밀번호"
          requirement="required"
          placeholder="비밀번호를 입력하세요."
          autoComplete="new-password"
          message={errors.password ?? ADMIN_PASSWORD_HELP_TEXT}
          messageIcon={<FoxChatCenteredDotsIcon />}
          invalid={Boolean(errors.password)}
          // 규칙은 도메인이 한 벌로 갖는다 — 저장 직전 서버 검증이 같은 값을 본다.
          validators={[foxPasswordValidator({ ...ADMIN_PASSWORD_POLICY, required: true })]}
          validationMessages={{ minlength: ADMIN_PASSWORD_ERROR_TEXT }}
        />

        <AdminMemberFormFields
          phoneNumber={phoneNumber}
          onPhoneNumberChange={setPhoneNumber}
          roleCode={roleCode}
          onRoleCodeChange={setRoleCode}
          menuCodes={menuCodes}
          onMenuCodesChange={setMenuCodes}
          errors={errors}
          emailField={
            <>
              <FoxEmail
                label="이메일"
                requirement="required"
                domainOptions={[...EMAIL_DOMAIN_OPTIONS]}
                localPlaceholder="ID"
                value={emailValue}
                onChange={(next) => {
                  setEmailValue(next);
                  setIsEmailTouched(true);
                }}
                message={emailMessage}
                invalid={Boolean(emailMessage)}
              />
              <input type="hidden" name="email" value={email} />
            </>
          }
        />

        {state.status === 'error' && state.message && (
          <p role="alert">{state.message}</p>
        )}
      </form>
    </FoxModal>
  );
}
