/* ============================================================================
 * Wildy — 4-step 회원가입 폼 (네이버 스타일, Phase 1B 후속)
 * ----------------------------------------------------------------------------
 *   각 단계 = 한 가지에 집중 (UX 원칙)
 *     Step 1: 가입 방법 선택 (소셜 즉시 OAuth / 이메일은 step 2 진행)
 *     Step 2: 약관 동의 (5개 — TOS, Privacy, Age14, Marketing, Location)
 *     Step 3: 본인인증 (한국 PortOne / 외국인 직접 입력)
 *     Step 4: 추가 정보 (이메일/PW/username/nickname/언어 + Turnstile)
 *
 *   양쪽 HTML 공유 — window.WildyComponents.SignupFlow 로 노출.
 *
 *   props:
 *     supabaseClient   — supabase v2 client (없으면 데모 모드)
 *     onAuthenticated  — (user) => void  가입 성공 콜백
 *     onSwitchToLogin  — () => void      "이미 회원" 클릭
 *     mobile           — boolean         true면 480 패딩, false면 데스크톱 카드
 *
 *   ⚠ location_agreed_at 컬럼은 아직 DB 미정의 — 현재는 클라 state만 유지.
 *      Phase 1D에서 컬럼 추가 + RPC 시그니처 확장 예정.
 * ========================================================================= */
(function (global) {
  'use strict';

  if (typeof React === 'undefined') {
    console.error('[signup-form.jsx] React not loaded');
    return;
  }
  const { useState, useEffect, useRef, useMemo } = React;

  function StepIndicator({ step }) {
    const labels = ['방법', '약관', '본인인증', '정보'];
    return (
      <div className="mb-7 flex items-center gap-1">
        {[1, 2, 3, 4].map(n => (
          <React.Fragment key={n}>
            <div className="flex flex-col items-center" style={{ minWidth: 36 }}>
              <div className={`flex h-7 w-7 items-center justify-center rounded-full text-xs font-bold transition ${
                step > n ? 'bg-emerald-500 text-white'
                : step === n ? 'bg-wildy-ink text-white'
                : 'bg-slate-100 text-slate-400'
              }`}>
                {step > n ? '✓' : n}
              </div>
              <div className={`mt-1 text-[10px] font-bold ${step >= n ? 'text-wildy-ink' : 'text-slate-400'}`}>
                {labels[n - 1]}
              </div>
            </div>
            {n < 4 && <div className={`h-px flex-1 mt-3 ${step > n ? 'bg-emerald-500' : 'bg-slate-200'}`}></div>}
          </React.Fragment>
        ))}
      </div>
    );
  }

  function StepHeading({ title, subtitle }) {
    return (
      <div className="mb-5">
        <h2 className="font-display text-2xl font-black text-wildy-ink leading-tight" style={{ wordBreak: 'keep-all' }}>{title}</h2>
        {subtitle && <p className="mt-2 text-sm text-slate-500" style={{ wordBreak: 'keep-all' }}>{subtitle}</p>}
      </div>
    );
  }

  // ───────────────────────────────────────────────────────────
  // Step 1 — 가입 방법 선택
  // ───────────────────────────────────────────────────────────
  function Step1Method({ flags, supabaseClient, onPickEmail, onSwitchToLogin, setError }) {
    const [loadingProvider, setLoadingProvider] = useState('');
    const oauth = global.WILDY_OAUTH_PROVIDERS || {};
    const providers = [
      { key: 'google', authProvider: oauth.google || 'google', name: '구글',  icon: 'G', cls: 'border-slate-200 bg-white text-slate-700', enabled: flags.googleLogin !== false },
      { key: 'kakao',  authProvider: oauth.kakao  || 'kakao',  name: '카카오', icon: 'K', cls: 'border-yellow-300 bg-yellow-300 text-slate-900', enabled: flags.kakaoLogin !== false },
      { key: 'naver',  authProvider: oauth.naver  || 'custom:naver', name: '네이버', icon: 'N', cls: 'border-green-500 bg-green-500 text-white', enabled: flags.naverLogin !== false },
      { key: 'apple',  authProvider: oauth.apple  || 'apple',  name: 'Apple', icon: '', cls: 'border-slate-900 bg-slate-900 text-white', enabled: flags.apple === true },
    ].filter(p => p.enabled);

    const startOAuth = async (p) => {
      setError('');
      if (!supabaseClient) {
        setError('소셜 가입은 Supabase 키 설정 후 사용 가능해요. 이메일로 진행해주세요.');
        return;
      }
      setLoadingProvider(p.key);
      try {
        if (typeof location !== 'undefined' && location.protocol === 'file:') {
          throw new Error('Live Server나 localhost에서 실행해주세요.');
        }
        const redirectTo = global.location.origin + '/auth';
        const { error } = await supabaseClient.auth.signInWithOAuth({
          provider: p.authProvider,
          options: { redirectTo, queryParams: p.key === 'google' ? { prompt: 'select_account' } : undefined },
        });
        if (error) throw error;
      } catch (e) {
        setError(e?.message || '소셜 가입 연결 실패');
        setLoadingProvider('');
      }
    };

    return (
      <>
        <StepHeading title="가입 방법을 선택해주세요" subtitle="Wildy에 오신 걸 환영해요." />

        <div className="grid gap-2.5">
          {providers.map(p => (
            <button
              key={p.key}
              onClick={() => startOAuth(p)}
              disabled={Boolean(loadingProvider)}
              className={`flex w-full min-h-[52px] items-center justify-center gap-2.5 rounded-2xl border-2 px-4 py-3 text-sm font-bold transition disabled:opacity-60 ${p.cls}`}
            >
              {p.icon && (
                <span className="flex h-7 w-7 items-center justify-center rounded-full bg-white/90 text-xs font-black text-slate-800">{p.icon}</span>
              )}
              <span>{loadingProvider === p.key ? `${p.name} 연결 중...` : `${p.name}로 가입하기`}</span>
            </button>
          ))}
        </div>

        <div className="my-5 flex items-center gap-3 text-xs text-slate-400">
          <div className="h-px flex-1 bg-slate-200"></div>
          <span>또는 이메일로</span>
          <div className="h-px flex-1 bg-slate-200"></div>
        </div>

        <button
          onClick={onPickEmail}
          disabled={Boolean(loadingProvider)}
          className="w-full min-h-[52px] rounded-2xl border-2 border-wildy-ink bg-white py-3 text-sm font-bold text-wildy-ink hover:bg-wildy-cloud transition"
        >
          ✉️ 이메일로 가입하기
        </button>

        <div className="mt-6 text-center text-sm text-slate-500">
          이미 회원이신가요?{' '}
          <button onClick={onSwitchToLogin} className="font-bold text-wildy-deep underline-offset-2 hover:underline">로그인</button>
        </div>
      </>
    );
  }

  // ───────────────────────────────────────────────────────────
  // Step 2 — 약관 동의 (5개)
  // ───────────────────────────────────────────────────────────
  function Step2Terms({ agreements, setAgreements, onNext, onBack, setError }) {
    const items = [
      { key: 'tos',          label: '이용약관',                  href: '/terms',    required: true },
      { key: 'privacy',      label: '개인정보 처리방침',           href: '/privacy',  required: true },
      { key: 'age14',        label: '만 14세 이상이에요',           href: null,        required: true },
      { key: 'marketing',    label: '마케팅 정보 수신',            href: null,        required: false },
      { key: 'location',     label: '위치기반서비스 (Phase 2)',   href: null,        required: false },
      // Phase 1 §2.5 E-5 — 번역 데이터 활용 (선택, 기본 체크). 미체크 시 가입 후
      //   users.translation_data_collection_opted_out=true 로 설정.
      { key: 'translation',  label: '번역 데이터 활용 (게임 번역 품질 개선)', href: '/terms#ch9', required: false },
    ];

    const allChecked = items.every(i => agreements[i.key]);
    const allRequiredChecked = items.filter(i => i.required).every(i => agreements[i.key]);

    const toggleAll = () => {
      const next = !allChecked;
      const updated = {};
      items.forEach(i => { updated[i.key] = next; });
      setAgreements(updated);
    };
    const toggle = (k) => setAgreements({ ...agreements, [k]: !agreements[k] });

    const next = () => {
      if (!allRequiredChecked) { setError('필수 항목에 모두 동의해주세요'); return; }
      setError(''); onNext();
    };

    return (
      <>
        <StepHeading title="약관에 동의해주세요" subtitle="Wildy 서비스 이용을 위해 약관에 동의해주세요." />

        <label className="mb-3 flex items-center gap-3 rounded-2xl border-2 border-sky-100 px-4 py-3.5 cursor-pointer hover:border-sky-200">
          <input type="checkbox" checked={allChecked} onChange={toggleAll} className="h-5 w-5 accent-wildy-ink" />
          <span className="text-base font-bold text-wildy-ink">전체 동의하기</span>
        </label>

        <div className="rounded-2xl bg-slate-50/60 px-4 py-3 space-y-2.5">
          {items.map(i => (
            <label key={i.key} className="flex items-center gap-3 cursor-pointer">
              <input type="checkbox" checked={Boolean(agreements[i.key])} onChange={() => toggle(i.key)} className="h-4 w-4 accent-wildy-ink flex-shrink-0" />
              <span className="text-sm text-slate-700 flex-1">
                <span className={`mr-1 font-bold ${i.required ? 'text-rose-500' : 'text-slate-400'}`}>[{i.required ? '필수' : '선택'}]</span>
                {i.label}
              </span>
              {i.href && (
                <a href={i.href} target="_blank" rel="noreferrer" className="text-xs text-slate-500 underline" onClick={(e) => e.stopPropagation()}>보기</a>
              )}
            </label>
          ))}
        </div>

        <div className="mt-7 flex gap-2">
          <button onClick={onBack} className="rounded-full border-2 border-slate-200 px-5 py-3.5 text-sm font-bold text-slate-600">이전</button>
          <button
            onClick={next}
            disabled={!allRequiredChecked}
            className="flex-1 rounded-full bg-wildy-ink py-3.5 font-bold text-white disabled:bg-slate-300 disabled:cursor-not-allowed"
          >
            다음
          </button>
        </div>
      </>
    );
  }

  // ───────────────────────────────────────────────────────────
  // Step 3 — 본인인증
  // ───────────────────────────────────────────────────────────
  function Step3Identity({ flags, helpers, identity, setIdentity, isForeigner, setIsForeigner, onNext, onBack, setError }) {
    const [iform, setIform] = useState(identity || { name: '', birthDate: '', gender: 'unspecified', phone: '', countryCode: 'KR' });
    const [portoneLoading, setPortoneLoading] = useState(false);

    const startPortOne = () => {
      if (!flags.portOne) {
        setError('PortOne 키 도착 전 임시 모드입니다. "직접 입력"으로 진행해주세요.');
        return;
      }
      if (!global.IMP) { setError('PortOne SDK 로드 실패. 새로고침 후 다시 시도해주세요.'); return; }
      setPortoneLoading(true);
      const merchantUid = 'wildy_' + Date.now() + '_' + Math.random().toString(36).slice(2, 8);
      const cfg = global.WILDY_PORTONE || {};
      global.IMP.init(cfg.mid || 'imp00000000');
      global.IMP.certification({
        channelKey: cfg.channel || undefined,
        merchant_uid: merchantUid,
        popup: cfg.popup !== false,
      }, async (rsp) => {
        if (!rsp.success) {
          setPortoneLoading(false);
          setError(rsp.error_msg || '본인인증이 취소되었어요');
          return;
        }
        try {
          const r = await fetch('/api/auth/identity-verification', {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ impUid: rsp.imp_uid, merchantUid }),
          });
          const data = await r.json();
          if (data.ok && data.verified) {
            setIdentity({
              name: data.name, birthDate: data.birthDate, gender: data.gender, phone: data.phone,
              ci: data.ci, is_foreigner: false, country_code: 'KR',
            });
            onNext();
          } else if (data.age_blocked) {
            setError('만 14세 이상부터 가입할 수 있어요');
          } else if (data.mode === 'manual_required') {
            setError('통합본인인증이 준비 중이에요. "직접 입력"으로 진행해주세요.');
          } else {
            setError(data.message || '본인인증 검증 실패');
          }
        } catch {
          setError('서버 통신 오류');
        } finally { setPortoneLoading(false); }
      });
    };

    const submitManual = () => {
      if (!iform.name || iform.name.length < 2) { setError('이름을 입력해주세요'); return; }
      if (!iform.birthDate) { setError('생년월일을 입력해주세요'); return; }
      const age = helpers.computeAge ? helpers.computeAge(iform.birthDate) : 99;
      if (age < 14) { setError('만 14세 이상부터 가입할 수 있어요'); return; }
      let phoneOut;
      if (isForeigner) {
        const digits = (iform.phone || '').replace(/[^0-9+]/g, '');
        if (!digits.startsWith('+') || digits.length < 8) { setError('국제번호 형식: +국가코드 숫자 (예: +818012345678)'); return; }
        phoneOut = digits;
      } else {
        phoneOut = helpers.normalizePhone ? helpers.normalizePhone(iform.phone) : iform.phone;
        if (!phoneOut) { setError('휴대폰 번호 형식 확인 (010-XXXX-XXXX)'); return; }
      }
      setIdentity({
        name: iform.name, birthDate: iform.birthDate, gender: iform.gender, phone: phoneOut, ci: null,
        is_foreigner: isForeigner, country_code: isForeigner ? iform.countryCode : 'KR',
      });
      setError('');
      onNext();
    };

    const max14 = new Date(Date.now() - 14 * 365.25 * 24 * 3600 * 1000).toISOString().slice(0, 10);

    return (
      <>
        <StepHeading title="본인인증을 진행해요" subtitle="안전한 서비스 이용을 위해 본인인증이 필요해요." />

        {flags.foreignerSignup !== false && (
          <div className="mb-5 grid grid-cols-2 gap-2">
            <button onClick={() => setIsForeigner(false)} className={`rounded-2xl border-2 px-3 py-3.5 text-sm font-bold transition ${!isForeigner ? 'border-wildy-ink bg-wildy-ink text-white' : 'border-sky-100 bg-white text-slate-600'}`}>
              🇰🇷 한국 사용자
            </button>
            <button onClick={() => setIsForeigner(true)} className={`rounded-2xl border-2 px-3 py-3.5 text-sm font-bold transition ${isForeigner ? 'border-wildy-ink bg-wildy-ink text-white' : 'border-sky-100 bg-white text-slate-600'}`}>
              🌐 외국인
            </button>
          </div>
        )}

        {!isForeigner && flags.portOne && (
          <div className="mb-5">
            <p className="mb-3 text-sm text-slate-600">통합본인인증으로 빠르게 인증하세요.</p>
            <button onClick={startPortOne} disabled={portoneLoading} className="w-full rounded-full bg-wildy-ink py-3.5 font-bold text-white disabled:bg-slate-300">
              {portoneLoading ? '인증 진행 중...' : '📱 휴대폰 인증 시작하기'}
            </button>
            <p className="mt-3 text-center text-xs text-slate-400">또는 직접 입력으로 진행</p>
          </div>
        )}

        {!isForeigner && !flags.portOne && (
          <div className="mb-4 rounded-2xl bg-amber-50 px-4 py-3 text-xs text-amber-700">
            ⚠ PortOne 키 도착 전 임시 모드입니다. 아래에 직접 입력해주세요.
          </div>
        )}

        {isForeigner && (
          <div className="mb-4 rounded-2xl bg-sky-50 px-4 py-3 text-xs text-sky-700">
            외국인 사용자는 직접 입력으로 진행합니다. 휴대폰은 +국가코드 형식.
          </div>
        )}

        <div className="space-y-2.5">
          <input value={iform.name} onChange={(e) => setIform({ ...iform, name: e.target.value })} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300" placeholder={isForeigner ? 'Name (실명)' : '이름 (실명)'} />
          <input type="date" value={iform.birthDate} onChange={(e) => setIform({ ...iform, birthDate: e.target.value })} max={max14} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300" />
          <select value={iform.gender} onChange={(e) => setIform({ ...iform, gender: e.target.value })} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300">
            <option value="unspecified">성별 선택 안함</option>
            <option value="male">남성</option>
            <option value="female">여성</option>
          </select>
          {isForeigner && (
            <select value={iform.countryCode} onChange={(e) => setIform({ ...iform, countryCode: e.target.value })} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300">
              <option value="US">🇺🇸 United States (+1)</option>
              <option value="JP">🇯🇵 Japan (+81)</option>
              <option value="CN">🇨🇳 China (+86)</option>
              <option value="ID">🇮🇩 Indonesia (+62)</option>
              <option value="VN">🇻🇳 Vietnam (+84)</option>
              <option value="TH">🇹🇭 Thailand (+66)</option>
              <option value="PH">🇵🇭 Philippines (+63)</option>
              <option value="TW">🇹🇼 Taiwan (+886)</option>
              <option value="HK">🇭🇰 Hong Kong (+852)</option>
              <option value="SG">🇸🇬 Singapore (+65)</option>
              <option value="GB">🇬🇧 UK (+44)</option>
              <option value="DE">🇩🇪 Germany (+49)</option>
              <option value="FR">🇫🇷 France (+33)</option>
              <option value="OTHER">🌐 Other</option>
            </select>
          )}
          <input value={iform.phone} onChange={(e) => setIform({ ...iform, phone: e.target.value })} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300" placeholder={isForeigner ? '+1234567890' : '휴대폰 번호 (010-XXXX-XXXX)'} />
        </div>

        <div className="mt-7 flex gap-2">
          <button onClick={onBack} className="rounded-full border-2 border-slate-200 px-5 py-3.5 text-sm font-bold text-slate-600">이전</button>
          <button onClick={submitManual} className="flex-1 rounded-full bg-wildy-ink py-3.5 font-bold text-white">
            {isForeigner || !flags.portOne ? '다음' : '직접 입력으로 다음'}
          </button>
        </div>
      </>
    );
  }

  // ───────────────────────────────────────────────────────────
  // Step 4 — 추가 정보
  // ───────────────────────────────────────────────────────────
  function Step4Profile({ flags, helpers, disposable, supabaseClient, profile, setProfile, onSubmit, onBack, submitting, error, setError }) {
    const [emailStatus, setEmailStatus] = useState({ available: null, message: '' });
    const [usernameStatus, setUsernameStatus] = useState({ available: null, message: '' });
    const turnstileRef = useRef(null);
    const pwStrength = helpers.passwordStrength ? helpers.passwordStrength(profile.password || '') : { score: 0, label: '', ok: false };

    useEffect(() => {
      if (!profile.email) { setEmailStatus({ available: null, message: '' }); return; }
      const t = setTimeout(async () => {
        if (helpers.isValidEmail && !helpers.isValidEmail(profile.email)) { setEmailStatus({ available: false, message: '이메일 형식 확인' }); return; }
        if (disposable.isDisposable && disposable.isDisposable(profile.email)) { setEmailStatus({ available: false, message: '일회용 이메일은 사용 불가' }); return; }
        try {
          const r = await fetch('/api/auth/validate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'email', email: profile.email }) });
          const data = await r.json();
          setEmailStatus({ available: data.available, message: data.message || (data.available ? '✓ 사용 가능' : '사용 불가') });
        } catch { setEmailStatus({ available: null, message: '확인 실패' }); }
      }, 500);
      return () => clearTimeout(t);
    }, [profile.email]);

    useEffect(() => {
      if (!profile.username) { setUsernameStatus({ available: null, message: '' }); return; }
      const t = setTimeout(async () => {
        const local = helpers.validateUsername ? helpers.validateUsername(profile.username) : { ok: true };
        if (!local.ok) { setUsernameStatus({ available: false, message: local.reason }); return; }
        try {
          const r = await fetch('/api/auth/validate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'username', username: profile.username }) });
          const data = await r.json();
          setUsernameStatus({ available: data.available, message: data.message || (data.available ? '✓ 사용 가능' : '사용 불가') });
        } catch { setUsernameStatus({ available: null, message: '확인 실패' }); }
      }, 500);
      return () => clearTimeout(t);
    }, [profile.username]);

    useEffect(() => {
      if (!flags.turnstile || !turnstileRef.current || !global.turnstile) return;
      const id = global.turnstile.render(turnstileRef.current, {
        sitekey: global.WILDY_TURNSTILE_SITE_KEY,
        callback: (t) => setProfile(p => ({ ...p, turnstileToken: t })),
        'expired-callback': () => setProfile(p => ({ ...p, turnstileToken: '' })),
      });
      return () => { try { global.turnstile.remove(id); } catch {} };
    }, []);

    const suggestNickname = () => {
      const ns = global.WildyLib?.NicknameSuggester;
      if (ns) setProfile(p => ({ ...p, displayName: ns.suggest(p.lang || 'ko') }));
    };

    const submit = () => {
      if (!emailStatus.available) { setError('이메일을 확인해주세요'); return; }
      if (!usernameStatus.available) { setError('아이디를 확인해주세요'); return; }
      if (!pwStrength.ok) { setError('비밀번호를 더 강하게 설정해주세요'); return; }
      if (!profile.displayName?.trim()) { setError('닉네임을 입력해주세요'); return; }
      if (flags.turnstile && !profile.turnstileToken) { setError('보안 확인을 완료해주세요'); return; }
      setError('');
      onSubmit();
    };

    return (
      <>
        <StepHeading title="마지막 한 가지!" subtitle="Wildy에서 사용할 정보를 입력해주세요." />

        <div className="space-y-1">
          <input value={profile.email || ''} onChange={(e) => setProfile({ ...profile, email: e.target.value })} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300" placeholder="✉️ 이메일" />
          {emailStatus.message && (
            <div className={`px-2 pb-1 text-xs ${emailStatus.available === true ? 'text-emerald-600' : emailStatus.available === false ? 'text-rose-600' : 'text-slate-500'}`}>{emailStatus.message}</div>
          )}

          <input type="password" value={profile.password || ''} onChange={(e) => setProfile({ ...profile, password: e.target.value })} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300" placeholder="🔒 비밀번호 (8자 이상)" />
          {profile.password && (
            <div className={`px-2 pb-1 text-xs ${pwStrength.score >= 3 ? 'text-emerald-600' : pwStrength.score >= 2 ? 'text-amber-600' : 'text-rose-600'}`}>강도: {pwStrength.label}</div>
          )}

          <input value={profile.username || ''} onChange={(e) => setProfile({ ...profile, username: e.target.value.toLowerCase() })} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300" placeholder="👤 사용자명 (영문 소문자/숫자/_)" />
          {usernameStatus.message && (
            <div className={`px-2 pb-1 text-xs ${usernameStatus.available === true ? 'text-emerald-600' : usernameStatus.available === false ? 'text-rose-600' : 'text-slate-500'}`}>{usernameStatus.message}</div>
          )}

          <div className="flex gap-2 pt-1">
            <input value={profile.displayName || ''} onChange={(e) => setProfile({ ...profile, displayName: e.target.value })} className="flex-1 rounded-2xl border-2 border-sky-100 px-4 py-3 outline-none focus:border-sky-300" placeholder="🌟 닉네임" />
            <button type="button" onClick={suggestNickname} title="자동 추천" className="rounded-2xl border-2 border-sky-100 px-3 text-lg hover:bg-sky-50">🎲</button>
          </div>

          <select value={profile.lang || 'ko'} onChange={(e) => setProfile({ ...profile, lang: e.target.value })} className="w-full rounded-2xl border-2 border-sky-100 px-4 py-3 mt-1 outline-none focus:border-sky-300">
            <option value="ko">🇰🇷 한국어</option>
            <option value="en">🇺🇸 English</option>
            <option value="ja">🇯🇵 日本語</option>
            <option value="zh">🇨🇳 中文</option>
            <option value="id">🇮🇩 Bahasa Indonesia</option>
          </select>
        </div>

        {flags.turnstile && <div ref={turnstileRef} className="my-4"></div>}

        <div className="mt-6 flex gap-2">
          <button onClick={onBack} className="rounded-full border-2 border-slate-200 px-5 py-3.5 text-sm font-bold text-slate-600" disabled={submitting}>이전</button>
          <button onClick={submit} disabled={submitting} className="flex-1 rounded-full bg-wildy-ink py-3.5 font-bold text-white disabled:bg-slate-300">
            {submitting ? '가입 처리 중...' : 'Wildy 시작하기'}
          </button>
        </div>
      </>
    );
  }

  // ───────────────────────────────────────────────────────────
  // SignupFlow — 메인 컨테이너
  // ───────────────────────────────────────────────────────────
  function SignupFlow({ supabaseClient, onAuthenticated, onSwitchToLogin, mobile = false }) {
    const flags = global.WILDY_FLAGS || {};
    const helpers = (global.WildyLib && global.WildyLib.SupabaseHelpers) || {};
    const disposable = (global.WildyLib && global.WildyLib.DisposableEmails) || { isDisposable: () => false };

    const [step, setStep] = useState(1);
    const [error, setError] = useState('');
    const [submitting, setSubmitting] = useState(false);

    // translation: 기본 true (옵트인) — Phase 1 §2.5 E-5. 미체크 시 가입 후 opt-out 처리.
    const [agreements, setAgreements] = useState({ tos: false, privacy: false, age14: false, marketing: false, location: false, translation: true });
    const [isForeigner, setIsForeigner] = useState(false);
    const [identity, setIdentity] = useState(null);
    const [profile, setProfile] = useState({ email: '', password: '', username: '', displayName: '', lang: 'ko', turnstileToken: '' });

    const finalize = async () => {
      setError('');
      setSubmitting(true);
      try {
        // CAPTCHA 검증
        if (flags.turnstile && profile.turnstileToken) {
          const cap = await fetch('/api/auth/verify-captcha', {
            method: 'POST', headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ token: profile.turnstileToken }),
          });
          const capData = await cap.json();
          if (!capData.ok) { setError('보안 확인 실패'); setSubmitting(false); return; }
        }

        if (!supabaseClient) {
          // Demo mode
          onAuthenticated({ email: profile.email, nickname: profile.displayName, provider: 'email' });
          return;
        }

        const signUpCall = supabaseClient.auth.signUp({
          email: profile.email, password: profile.password,
          options: { data: { display_name: profile.displayName } },
        });
        const { data: au, error: ae } = await (helpers.withTimeout
          ? helpers.withTimeout(signUpCall, 15000)
          : signUpCall);
        if (ae) throw ae;
        const userId = au?.user?.id;
        if (!userId) throw new Error('가입 후 user_id를 받지 못했어요');

        const { error: re } = await supabaseClient.rpc('signup_with_profile_v2', {
          p_user_id: userId,
          p_username: profile.username.toLowerCase(),
          p_display_name: profile.displayName,
          p_email: profile.email,
          p_real_name: identity.name,
          p_birth_date: identity.birthDate,
          p_gender: identity.gender || 'unspecified',
          p_phone: identity.phone,
          p_ci: identity.ci,
          p_terms_agreed: agreements.tos && agreements.privacy && agreements.age14,
          p_marketing_agreed: Boolean(agreements.marketing),
          p_preferred_language: profile.lang || 'ko',
          p_is_foreigner: Boolean(identity.is_foreigner),
          p_country_code: identity.country_code || 'KR',
        });
        if (re) throw re;

        // 번역 데이터 옵트아웃 처리 (체크 안 한 경우만) — fire-and-forget
        //   기본 = 옵트인. 체크 해제 → opted_out=true 로 저장.
        if (!agreements.translation) {
          try {
            await supabaseClient.from('users')
              .update({ translation_data_collection_opted_out: true })
              .eq('id', userId);
          } catch (e) {
            console.warn('[SignupFlow] translation opt-out save failed', e.message);
          }
        }

        // 이메일 인증 메일 발송 (fire-and-forget)
        fetch('/api/auth/send-verification', {
          method: 'POST', headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ userId, email: profile.email }),
        }).catch(() => {});

        onAuthenticated({ id: userId, email: profile.email, nickname: profile.displayName, provider: 'email' });
      } catch (e) {
        console.error('[SignupFlow] finalize', e);
        setError(helpers.translateAuthError ? helpers.translateAuthError(e) : (e?.message || '가입 처리 중 문제가 발생했어요'));
      } finally {
        setSubmitting(false);
      }
    };

    // wrapper 책임은 부모 (index.html section / app.html 풀스크린)에 위임.
    // mobile prop은 padding 미세조정에만 사용.
    const inner = (
      <>
        <StepIndicator step={step} />

        {step === 1 && <Step1Method flags={flags} supabaseClient={supabaseClient} onPickEmail={() => { setError(''); setStep(2); }} onSwitchToLogin={onSwitchToLogin} setError={setError} />}
        {step === 2 && <Step2Terms agreements={agreements} setAgreements={setAgreements} onNext={() => setStep(3)} onBack={() => setStep(1)} setError={setError} />}
        {step === 3 && <Step3Identity flags={flags} helpers={helpers} identity={identity} setIdentity={setIdentity} isForeigner={isForeigner} setIsForeigner={setIsForeigner} onNext={() => setStep(4)} onBack={() => setStep(2)} setError={setError} />}
        {step === 4 && <Step4Profile flags={flags} helpers={helpers} disposable={disposable} supabaseClient={supabaseClient} profile={profile} setProfile={setProfile} onSubmit={finalize} onBack={() => setStep(3)} submitting={submitting} error={error} setError={setError} />}

        {error && <div className="mt-4 rounded-2xl bg-rose-50 px-4 py-3 text-sm text-rose-600" style={{ wordBreak: 'keep-all' }}>{error}</div>}
      </>
    );
    if (mobile) {
      return (
        <div className="min-h-screen w-full bg-white px-5 pt-8 pb-12" style={{ maxWidth: 480, margin: '0 auto' }}>
          {inner}
        </div>
      );
    }
    return <div>{inner}</div>;
  }

  global.WildyComponents = global.WildyComponents || {};
  global.WildyComponents.SignupFlow = SignupFlow;
})(typeof window !== 'undefined' ? window : globalThis);
