"use client";

import { useEffect, useId, useRef, useState, type ChangeEvent } from "react";
import { FoxFormLabel } from "../fox-form-label";
import { cx } from "../../utils";
import { FoxButton } from "../fox-button";
import { FoxPhoneNumber } from "../fox-phone-number";

export interface FoxCertifiedProps {
  /** 전화번호. 숫자만 담긴 문자열이다 — `FoxPhoneNumber`와 같다. */
  phone?: string;
  defaultPhone?: string;
  onPhoneChange?: (phone: string) => void;
  /** 전화번호 앞자리 목록. 밖에서 받은 값을 칸으로 나눌 때도 쓴다. */
  prefixOptions?: string[];

  /**
   * 인증번호 받기. resolve하면 성공으로 보고 인증번호 칸과 확인 버튼을 연다.
   * throw하거나 reject하면 실패로 보고 `onSendFail`을 부른다.
   */
  onSend: (phone: string) => Promise<unknown>;
  /** 인증번호 확인. resolve하면 성공, throw·reject하면 실패다. */
  onVerify: (phone: string, code: string) => Promise<unknown>;

  onSendSuccess?: () => void;
  onSendFail?: (error: unknown) => void;
  onVerifySuccess?: () => void;
  onVerifyFail?: (error: unknown) => void;
  /** 남은 시간이 0이 됐을 때 한 번 부른다. */
  onExpire?: () => void;

  /**
   * 제한 시간(초). 주면 인증번호를 보낸 시점부터 컴포넌트가 직접 센다.
   * `remainingSeconds`를 함께 주면 그쪽이 이긴다.
   */
  expiresIn?: number;
  /** 남은 시간(초)을 직접 셀 때. 주면 컴포넌트는 세지 않고 이 값만 그린다. */
  remainingSeconds?: number;

  label?: string;
  sendLabel?: string;
  verifyLabel?: string;
  codePlaceholder?: string;
  codeMaxLength?: number;
  /** 전체 잠금. 인증에 성공하면 이 값과 무관하게 잠긴다. */
  disabled?: boolean;
  /** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
  hidden?: boolean;
  /** 배치 조정용. */
  className?: string;
}

/** 시안의 `24:16` 표기. 분이 두 자리를 넘어가면 그대로 늘어난다. */
function formatTimer(seconds: number): string {
  const safe = Math.max(0, Math.floor(seconds));
  const minutes = Math.floor(safe / 60);
  const rest = safe % 60;
  return `${String(minutes).padStart(2, "0")}:${String(rest).padStart(2, "0")}`;
}

/** 시안의 ico/ClockCountdown 자리. @fox에 아이콘 세트가 없어 같은 모양을 직접 그린다. */
function TimerIcon() {
  return <i className="fox-ico fox-ico-ClockCountdown" aria-hidden="true" />;
}

/**
 * @fox 휴대폰 인증. 전화번호를 받고, 인증번호를 보내고, 확인한다.
 *
 * 보내기·확인은 호출부가 `Promise`로 넘긴다 — resolve면 성공, throw·reject면 실패다.
 * 성공·실패는 모두 이벤트로 올라오고, 화면은 최소한만 반응한다(보내기 성공이면 인증번호
 * 칸과 확인 버튼이 열리고, 인증 성공이면 전부 잠긴다). 실패 문구는 호출부가 보여준다.
 *
 * ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
 * (또는 개별 파티셜)로 한 번 불러와야 한다.
 */
export function FoxCertified({
  phone,
  defaultPhone,
  onPhoneChange,
  prefixOptions = [],
  onSend,
  onVerify,
  onSendSuccess,
  onSendFail,
  onVerifySuccess,
  onVerifyFail,
  onExpire,
  expiresIn,
  remainingSeconds,
  label,
  sendLabel = "인증번호 받기",
  verifyLabel = "인증번호 확인",
  codePlaceholder = "인증번호 입력",
  codeMaxLength,
  disabled = false,
  hidden = false,
  className,
}: FoxCertifiedProps) {
  const autoId = useId();
  const [innerPhone, setInnerPhone] = useState(defaultPhone ?? "");
  const [code, setCode] = useState("");
  const [sent, setSent] = useState(false);
  const [verified, setVerified] = useState(false);
  const [sending, setSending] = useState(false);
  const [verifying, setVerifying] = useState(false);
  const [ticking, setTicking] = useState<number | null>(null);
  const expiredRef = useRef(false);

  const selfTimed = remainingSeconds === undefined && expiresIn !== undefined;
  const left = remainingSeconds ?? ticking;

  // 스스로 셀 때만 1초마다 줄인다. 호출부가 값을 주면 그리기만 한다.
  useEffect(() => {
    if (!selfTimed || !sent) {
      return;
    }
    const id = setInterval(() => {
      setTicking((prev) => (prev === null || prev <= 0 ? prev : prev - 1));
    }, 1000);
    return () => clearInterval(id);
  }, [selfTimed, sent]);

  // 0에 닿는 순간 한 번만 알린다. 다시 보내 시간이 살아나면 잠금을 푼다.
  useEffect(() => {
    if (left === null || left === undefined) {
      return;
    }
    if (left <= 0 && !expiredRef.current) {
      expiredRef.current = true;
      onExpire?.();
    }
    if (left > 0) {
      expiredRef.current = false;
    }
  }, [left, onExpire]);

  if (hidden) {
    return null;
  }

  const phoneValue = phone ?? innerPhone;
  const locked = disabled || verified;
  const codeId = `${autoId}-code`;

  const handlePhoneChange = (next: string) => {
    if (phone === undefined) {
      setInnerPhone(next);
    }
    onPhoneChange?.(next);
  };

  const handleSend = async () => {
    if (sending) {
      return;
    }
    setSending(true);
    try {
      await onSend(phoneValue);
      setSent(true);
      expiredRef.current = false;
      if (selfTimed) {
        setTicking(expiresIn ?? null);
      }
      onSendSuccess?.();
    } catch (error) {
      onSendFail?.(error);
    } finally {
      setSending(false);
    }
  };

  const handleVerify = async () => {
    if (verifying) {
      return;
    }
    setVerifying(true);
    try {
      await onVerify(phoneValue, code);
      setVerified(true);
      onVerifySuccess?.();
    } catch (error) {
      onVerifyFail?.(error);
    } finally {
      setVerifying(false);
    }
  };

  return (
    <div
      className={cx("fox-certified", className)}
      role="group"
      aria-labelledby={label ? `${autoId}-label` : undefined}
    >
      {label && (
        <FoxFormLabel as="span" id={`${autoId}-label`}>
          {label}
        </FoxFormLabel>
      )}

      <div className="fox-certified__row">
        <div className="fox-certified__phone">
          <FoxPhoneNumber
            type="combine"
            prefixOptions={prefixOptions}
            value={phoneValue}
            onChange={handlePhoneChange}
            disabled={locked}
          />
        </div>

        <FoxButton
          size="md"
          type="secondary"
          label={sendLabel}
          loading={sending}
          disabled={locked}
          onAction={handleSend}
        />
      </div>

      {/* 보내기가 성공해야 열린다. */}
      {sent && (
        <>
          <div className="fox-certified__box">
            <input
              id={codeId}
              className="fox-certified__field"
              inputMode="numeric"
              autoComplete="one-time-code"
              aria-label={codePlaceholder}
              placeholder={codePlaceholder}
              value={code}
              maxLength={codeMaxLength}
              disabled={locked}
              onChange={(event: ChangeEvent<HTMLInputElement>) => setCode(event.target.value)}
            />

            {left !== null && left !== undefined && (
              <span className="fox-certified__timer">
                <span className="fox-certified__timer-icon" aria-hidden="true">
                  <TimerIcon />
                </span>
                <span className="fox-certified__timer-text">{formatTimer(left)}</span>
              </span>
            )}
          </div>

          <FoxButton
            size="md"
            type="primary"
            label={verifyLabel}
            fullWidth
            loading={verifying}
            disabled={locked}
            onAction={handleVerify}
          />
        </>
      )}
    </div>
  );
}
