"use client";

import { useId, useState, type ReactNode } from "react";
import { FoxFormLabel } from "../fox-form-label";
import { cx } from "../../utils";
import { FoxButton } from "../fox-button";
import { FoxInput } from "../fox-input";
import { FoxSelect, type FoxSelectItem } from "../fox-select";

/** 드롭다운의 "직접입력" 항목이 쓰는 값. 실제 도메인과 겹치지 않게 잡았다. */
const CUSTOM_DOMAIN = "__fox-email-custom__";

export interface FoxEmailProps {
  /** `아이디@도메인` 전체 값. 둘 다 비어 있으면 빈 문자열이다. */
  value?: string;
  defaultValue?: string;
  onChange?: (value: string) => void;
  /** 도메인 드롭다운 목록. "직접입력" 항목은 컴포넌트가 맨 아래에 더한다. */
  domainOptions?: string[];
  customDomainLabel?: string;

  /**
   * 주면 확인 버튼이 생기고, 넘기지 않으면 버튼 자체가 렌더되지 않는다.
   * resolve하면 성공, throw·reject하면 실패로 본다.
   */
  onVerify?: (email: string) => Promise<unknown>;
  onVerifySuccess?: () => void;
  onVerifyFail?: (error: unknown) => void;
  verifyLabel?: string;

  label?: string;
  /** 헬퍼 메시지. 오류일 때 색이 바뀐다. */
  message?: string;
  messageIcon?: ReactNode;
  localPlaceholder?: string;
  domainPlaceholder?: string;
  customPlaceholder?: string;

  invalid?: boolean;
  disabled?: boolean;
  /** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
  hidden?: boolean;
  /** 배치 조정용. */
  className?: string;
}

type Split = { local: string; domain: string; custom: boolean };

/**
 * `아이디@도메인`을 칸으로 나눈다. `@`는 마지막 것을 기준으로 자른다.
 *
 * 나눈 도메인이 목록에 없으면 직접 적은 것으로 보고 드롭다운을 "직접입력"에 맞춘다.
 */
function splitEmail(value: string, domainOptions: string[]): Split {
  const at = (value ?? "").lastIndexOf("@");
  if (at < 0) {
    return { local: value ?? "", domain: "", custom: false };
  }
  const domain = value.slice(at + 1);
  return {
    local: value.slice(0, at),
    domain,
    custom: domain !== "" && !domainOptions.includes(domain),
  };
}

function joinEmail(local: string, domain: string): string {
  return local || domain ? `${local}@${domain}` : "";
}

/**
 * @fox 이메일 입력. 아이디와 도메인을 나눠 받되 값은 합쳐진 문자열 하나다.
 *
 * 도메인은 드롭다운에서 고르면 옆 칸에 그 값이 채워지고 읽기 전용이 된다. "직접입력"을
 * 고르면 그 칸이 풀려 손으로 적을 수 있다.
 *
 * ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
 * (또는 개별 파티셜)로 한 번 불러와야 한다.
 */
export function FoxEmail({
  value,
  defaultValue,
  onChange,
  domainOptions = [],
  customDomainLabel = "직접입력",
  onVerify,
  onVerifySuccess,
  onVerifyFail,
  verifyLabel = "확인",
  label,
  message,
  messageIcon,
  localPlaceholder = "아이디",
  domainPlaceholder = "도메인 선택",
  customPlaceholder = "직접입력",
  invalid = false,
  disabled = false,
  hidden = false,
  className,
}: FoxEmailProps) {
  const autoId = useId();
  const [parts, setParts] = useState<Split>(() =>
    splitEmail(value ?? defaultValue ?? "", domainOptions)
  );
  const [seenValue, setSeenValue] = useState(value);
  const [verifying, setVerifying] = useState(false);

  // 밖에서 값을 바꿨을 때만 다시 나눈다. 우리가 방금 올려보낸 값이면 그대로 둔다.
  if (value !== undefined && value !== seenValue) {
    setSeenValue(value);
    if (value !== joinEmail(parts.local, parts.domain)) {
      setParts(splitEmail(value, domainOptions));
    }
  }

  if (hidden) {
    return null;
  }

  const messageId = `${autoId}-message`;
  const email = joinEmail(parts.local, parts.domain);

  const commit = (next: Split) => {
    setParts(next);
    const joined = joinEmail(next.local, next.domain);
    setSeenValue(joined);
    onChange?.(joined);
  };

  const domainItems: FoxSelectItem[] = [
    ...domainOptions.map((option) => ({ value: option, label: option })),
    { value: CUSTOM_DOMAIN, label: customDomainLabel },
  ];

  // 목록에서 고르면 옆 칸을 그 값으로 덮고 잠근다. "직접입력"이면 잠금만 푼다.
  const handleDomainPick = (picked: string) => {
    if (picked === CUSTOM_DOMAIN) {
      commit({ ...parts, custom: true });
      return;
    }
    commit({ ...parts, domain: picked, custom: false });
  };

  const handleVerify = async () => {
    if (!onVerify || verifying) {
      return;
    }
    setVerifying(true);
    try {
      await onVerify(email);
      onVerifySuccess?.();
    } catch (error) {
      onVerifyFail?.(error);
    } finally {
      setVerifying(false);
    }
  };

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

      <div className="fox-email__row">
        <div className="fox-email__part">
          <FoxInput
            size="md"
            aria-label="이메일 아이디"
            aria-describedby={message ? messageId : undefined}
            value={parts.local}
            onChange={(next) => commit({ ...parts, local: next })}
            placeholder={localPlaceholder}
            invalid={invalid}
            disabled={disabled}
          />
        </div>

        <span className="fox-email__at" aria-hidden="true">
          @
        </span>

        <div className="fox-email__part">
          <FoxSelect
            size="md"
            options={domainItems}
            value={parts.custom ? CUSTOM_DOMAIN : parts.domain}
            onValueChange={handleDomainPick}
            placeholder={domainPlaceholder}
            error={invalid}
            disabled={disabled}
          />
        </div>

        {/* 목록에서 고른 값이 여기에 채워지고 잠긴다. "직접입력"일 때만 풀린다. */}
        <div className="fox-email__part">
          <FoxInput
            size="md"
            aria-label="이메일 도메인"
            value={parts.domain}
            onChange={(next) => commit({ ...parts, domain: next })}
            placeholder={customPlaceholder}
            readOnly={!parts.custom}
            invalid={invalid}
            disabled={disabled}
          />
        </div>

        {onVerify && (
          <FoxButton
            size="md"
            type="primary"
            label={verifyLabel}
            loading={verifying}
            disabled={disabled}
            onAction={handleVerify}
          />
        )}
      </div>

      {message && (
        <p className="fox-email__message" id={messageId}>
          {messageIcon && (
            <span className="fox-email__message-icon" aria-hidden="true">
              {messageIcon}
            </span>
          )}
          {message}
        </p>
      )}
    </div>
  );
}
