// AKC Course Booking — Registration screen + sticky fee panel
// Fields locked to AKC's official Learner Registration Form spec.

const { useState: useStateR, useMemo: useMemoR, useEffect: useEffectR, useRef: useRefR } = React;

// ───────────────────────────────────────────────────────────────────────────
// Defaults — match exactly the fields on the paper Learner Registration Form
// ───────────────────────────────────────────────────────────────────────────

const blankLearner = () => ({
  learner_name: '',
  dob: '',
  email: '',
  mobile: '',
  id_number: '',
  id_type: '',
  nationality: '',     // e.g. "SINGAPORE CITIZEN | SG" — drives funding eligibility
  race: '',            // e.g. "CHINESE | CN"
});

// Picklists come from reference-data.jsx (window.AKC_NATIONALITIES / window.AKC_RACES)
// Format matches the MOM batch trainee upload spec exactly: "LABEL | CODE".
const ID_TYPES = ['NRIC', 'FIN', 'Passport', 'Work Permit'];
const DOB_MAX_AGE = 100;

function ageFromDob(dob) {
  if (!dob) return null;
  const d = new Date(dob);
  if (isNaN(d)) return null;
  const today = new Date();
  let age = today.getFullYear() - d.getFullYear();
  const m = today.getMonth() - d.getMonth();
  if (m < 0 || (m === 0 && today.getDate() < d.getDate())) age--;
  return age;
}

function localDateIso(d = new Date()) {
  const y = d.getFullYear();
  const m = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}

function dobMinIso() {
  const d = new Date();
  d.setFullYear(d.getFullYear() - DOB_MAX_AGE);
  return localDateIso(d);
}

function validateDobValue(dob) {
  if (!dob) return 'Required';
  if (!/^\d{4}-\d{2}-\d{2}$/.test(dob)) return 'Enter a valid birth year';

  const parsed = new Date(dob + 'T00:00:00');
  if (isNaN(parsed)) return 'Enter a valid date';
  if (dob < dobMinIso()) return 'Learner must be 100 years old or younger';
  if (dob > localDateIso()) return 'Date of birth cannot be in the future';
  return '';
}

function normalizeSingaporePhone(value) {
  const raw = (value || '').trim();
  if (!raw) return '';

  const compact = raw.replace(/[\s().-]+/g, '');
  if (/^\+\d{8,15}$/.test(compact)) return compact;

  const digits = raw.replace(/\D+/g, '');
  if (/^[689]\d{7}$/.test(digits)) return `+65${digits}`;
  if (/^65[689]\d{7}$/.test(digits)) return `+${digits}`;
  return '';
}

function normalizeIdNumber(value, idType) {
  const compact = String(value || '').replace(/[\s-]+/g, '');
  if (['NRIC', 'FIN', 'Work Permit'].includes(idType)) {
    return compact.toUpperCase().replace(/[^A-Z0-9]/g, '');
  }
  return compact.trim();
}

function validateIdNumber(idType, value) {
  const id = normalizeIdNumber(value, idType);
  if (!id) return 'Required';
  if (idType === 'NRIC') {
    return /^[ST]\d{7}[A-Z]$/.test(id) ? '' : 'Enter valid NRIC, e.g. S1234567A';
  }
  if (idType === 'FIN') {
    return /^[FGM]\d{7}[A-Z]$/.test(id) ? '' : 'Enter valid FIN, e.g. F1234567A';
  }
  if (idType === 'Work Permit') {
    return /^[A-Z0-9]{8,12}$/.test(id) ? '' : 'Enter valid Work Permit number';
  }
  if (idType === 'Passport') {
    return /^[A-Z0-9]{6,20}$/i.test(id) ? '' : 'Enter valid passport number';
  }
  return '';
}

function validateLearner(l) {
  const e = {};
  if (!l.learner_name.trim()) e.learner_name = 'Required';
  const dobError = validateDobValue(l.dob);
  if (dobError) e.dob = dobError;
  if (!l.email.trim()) e.email = 'Required';
  else if (!l.email.includes('@')) e.email = 'Looks invalid';
  if (!l.mobile.trim()) e.mobile = 'Required';
  else if (!normalizeSingaporePhone(l.mobile)) e.mobile = 'Enter 8-digit SG mobile or +65 number';
  if (!l.id_type) e.id_type = 'Required';
  const idError = validateIdNumber(l.id_type, l.id_number);
  if (idError) e.id_number = idError;
  if (!l.nationality) e.nationality = 'Required';
  else if (window.AKC_NATIONALITIES && !window.AKC_NATIONALITIES.includes(l.nationality))
    e.nationality = 'Pick from the list';
  if (!l.race) e.race = 'Required';
  else if (window.AKC_RACES && !window.AKC_RACES.includes(l.race))
    e.race = 'Pick from the list';
  return e;
}

function isWshCourse(course, intake) {
  const raw = intake?.backend || {};
  const text = [
    course?.main_category,
    course?.category,
    course?.sub_category,
    course?.name,
    course?.code,
    raw.course_category,
    raw.web_course_category,
    raw.web_main_course,
    raw.course_code,
    raw.class_no,
  ].map(value => String(value || '').toLowerCase()).join(' ');
  return text.includes('wsh')
    || text.includes('worker courses')
    || text.includes('supervisor courses')
    || text.includes('work at height')
    || text.includes('forklift')
    || text.includes('mewp')
    || text.includes('bizsafe')
    || text.includes('csoc')
    || text.includes('msoc')
    || text.includes('ssic')
    || text.includes('aws');
}

// ───────────────────────────────────────────────────────────────────────────
// Searchable Combobox — text input with a filtered dropdown list.
// Value stored is one of the strings in `options` (strict).
// ───────────────────────────────────────────────────────────────────────────

function Combobox({ value, options, onChange, placeholder, invalid }) {
  const [query, setQuery] = useStateR(value || '');
  const [open, setOpen]   = useStateR(false);
  const [activeIdx, setActiveIdx] = useStateR(0);
  const wrapRef = useRefR(null);

  // Keep input synced when value changes externally
  useEffectR(() => { setQuery(value || ''); }, [value]);

  // Filter — case-insensitive substring match against the label part (before the " | ")
  const filtered = useMemoR(() => {
    const q = query.trim().toLowerCase();
    if (!q || q === (value || '').toLowerCase()) return options.slice(0, 100);
    return options.filter(o => o.toLowerCase().includes(q)).slice(0, 100);
  }, [query, options, value]);

  // Close on click outside
  useEffectR(() => {
    if (!open) return;
    function onDocClick(e) {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    }
    document.addEventListener('mousedown', onDocClick);
    return () => document.removeEventListener('mousedown', onDocClick);
  }, [open]);

  function commit(opt) {
    onChange(opt);
    setQuery(opt);
    setOpen(false);
  }

  function handleKey(e) {
    if (e.key === 'ArrowDown') {
      e.preventDefault();
      if (!open) setOpen(true);
      setActiveIdx(i => Math.min(filtered.length - 1, i + 1));
    } else if (e.key === 'ArrowUp') {
      e.preventDefault();
      setActiveIdx(i => Math.max(0, i - 1));
    } else if (e.key === 'Enter') {
      if (open && filtered[activeIdx]) {
        e.preventDefault();
        commit(filtered[activeIdx]);
      }
    } else if (e.key === 'Escape') {
      setOpen(false);
      setQuery(value || '');
    }
  }

  return (
    <div ref={wrapRef} style={{ position: 'relative' }}>
      <input
        className={'akc-input' + (invalid ? ' akc-input--invalid' : '')}
        type="text"
        value={query}
        placeholder={placeholder || 'Type to search…'}
        onFocus={() => { setOpen(true); setActiveIdx(0); }}
        onChange={(e) => { setQuery(e.target.value); setOpen(true); setActiveIdx(0); }}
        onKeyDown={handleKey}
        autoComplete="off"
        spellCheck={false}
      />
      {open && filtered.length > 0 && (
        <ul
          role="listbox"
          style={{
            position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 50,
            margin: '4px 0 0', padding: 4, listStyle: 'none',
            background: 'var(--surface)',
            border: '1px solid var(--line)',
            borderRadius: 8,
            boxShadow: '0 8px 24px -8px rgba(0,0,0,0.18)',
            maxHeight: 260, overflowY: 'auto',
            fontSize: 13,
          }}
        >
          {filtered.map((opt, i) => (
            <li
              key={opt}
              role="option"
              aria-selected={i === activeIdx}
              onMouseDown={(e) => { e.preventDefault(); commit(opt); }}
              onMouseEnter={() => setActiveIdx(i)}
              style={{
                padding: '7px 10px', borderRadius: 6, cursor: 'pointer',
                background: i === activeIdx ? 'var(--primary-100, #e8f0fc)' : 'transparent',
                color: i === activeIdx ? 'var(--primary-700, #0d3f80)' : 'var(--ink)',
                fontFamily: "inherit",
              }}
            >
              {opt}
            </li>
          ))}
        </ul>
      )}
      {open && filtered.length === 0 && (
        <div style={{
          position: 'absolute', top: '100%', left: 0, right: 0, zIndex: 50,
          margin: '4px 0 0', padding: '10px 12px',
          background: 'var(--surface)', border: '1px solid var(--line)',
          borderRadius: 8, fontSize: 12, color: 'var(--muted)',
        }}>
          No matches. Only values from the MOM list are accepted.
        </div>
      )}
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────────────
// Field atom
// ───────────────────────────────────────────────────────────────────────────

function Field({ label, hint, error, children, span }) {
  return (
    <div className="akc-field" style={span ? { gridColumn: `span ${span}` } : null}>
      {label && <label className="akc-field-label">{label}</label>}
      {children}
      {error ? <div className="akc-error">{error}</div> : hint ? <div className="akc-field-hint">{hint}</div> : null}
    </div>
  );
}

function ReadOnlyField({ label, value }) {
  return (
    <div className="akc-field">
      <label className="akc-field-label">{label}</label>
      <div className="akc-input" style={{
        background: 'var(--surface-2)', color: 'var(--ink)', fontWeight: 600,
        cursor: 'default', display: 'flex', alignItems: 'center', gap: 8,
      }}>
        <LockIcon /> <span style={{ flex: 1 }}>{value}</span>
      </div>
    </div>
  );
}

function LockIcon() {
  return (
    <svg width="12" height="12" viewBox="0 0 12 12" fill="none" style={{ color: 'var(--muted)', flex: '0 0 auto' }}>
      <rect x="2.5" y="5.5" width="7" height="5" rx="1" stroke="currentColor" strokeWidth="1.2" />
      <path d="M4 5.5V4a2 2 0 014 0v1.5" stroke="currentColor" strokeWidth="1.2" />
    </svg>
  );
}

// ───────────────────────────────────────────────────────────────────────────
// RegistrationScreen
// ───────────────────────────────────────────────────────────────────────────

function RegistrationScreen({ device, course, intake, showGrant, onBack, onSubmit }) {
  const isMobile = device === 'mobile';

  // Individual-only path: exactly one learner, no company.
  const [learners, setLearners] = useStateR([blankLearner()]);
  const [useSkillsFutureAnswer, setUseSkillsFutureAnswer] = useStateR('');
  const [preferredContact, setPreferredContact] = useStateR(''); // 'email' | 'whatsapp'
  const [agreedTerms, setAgreedTerms] = useStateR(false);
  const [agreedPdpa, setAgreedPdpa] = useStateR(false);
  const [agreedWshTrs, setAgreedWshTrs] = useStateR(false);
  const [agreedMarketing, setAgreedMarketing] = useStateR(false);
  const [captchaToken, setCaptchaToken] = useStateR('');
  const captchaContainerRef = useRefR(null);
  const captchaWidgetIdRef = useRefR(null);
  const [submitted, setSubmitted] = useStateR(false);
  const [fees, setFees] = useStateR(() => window.calcFee({ ...course, full_fee: intake.full_fee || course.full_fee }, { registrantType: 'individual' }, 1));
  const [isSubmitting, setIsSubmitting] = useStateR(false);
  const [submitError, setSubmitError] = useStateR('');
  const requiresWshTrsConsent = isWshCourse(course, intake);

  const updateLearner = (i, patch) => {
    setLearners(prev => prev.map((l, idx) => idx === i ? { ...l, ...patch } : l));
  };

  // funding profile — derived from the (single) learner
  const profile = useMemoR(() => ({
    registrantType: 'individual',
    nationality: learners[0]?.nationality || '',
    age: ageFromDob(learners[0]?.dob) ?? 35,
  }), [learners]);

  useEffectR(() => {
    let alive = true;
    const fallback = window.calcFee({ ...course, full_fee: intake.full_fee || course.full_fee }, profile, learners.length);

    // Only reset to the local fallback when there is no backend estimate yet
    // (first load). After that, keep the current fees visible while the new
    // backend call is in-flight so the panel doesn't flicker back to $0 funding
    // on every keystroke.
    setFees(prev => prev.backendEstimate ? prev : fallback);

    const timer = setTimeout(async () => {
      try {
        const nextFees = await window.calculateBackendFee({
          course,
          intake,
          registrantType: 'individual',
          learners,
          company: {},
        });
        if (alive) setFees(nextFees);
      } catch (err) {
        if (alive) setFees(fallback);
      }
    }, 250);

    return () => {
      alive = false;
      clearTimeout(timer);
    };
  }, [course, intake, learners, profile]);

  // validation
  const learnerErrors = learners.map(validateLearner);
  const totalLearnerErrors = learnerErrors.reduce((s, e) => s + Object.keys(e).length, 0);
  const canSubmit = totalLearnerErrors === 0
    && !!useSkillsFutureAnswer
    && !!preferredContact
    && agreedTerms
    && agreedPdpa
    && (!requiresWshTrsConsent || agreedWshTrs)
    && !!captchaToken;

  // ── hCaptcha widget render ───────────────────────────────────────────────
  // Re-renders into the container ref as soon as the hCaptcha script is loaded.
  useEffectR(() => {
    let polled = false;
    function tryRender() {
      const siteKey = (window.AKCBookingConfig && window.AKCBookingConfig.hcaptchaSiteKey) || '';
      if (!siteKey) return;            // no key → silently skip (dev mode)
      if (!window.hcaptcha || !captchaContainerRef.current) {
        polled = true;
        setTimeout(tryRender, 100);
        return;
      }
      if (captchaWidgetIdRef.current != null) return; // already rendered
      try {
        captchaWidgetIdRef.current = window.hcaptcha.render(captchaContainerRef.current, {
          sitekey: siteKey,
          theme:   'light',
          size:    'normal',
          callback:           (t) => setCaptchaToken(t),
          'expired-callback': ()  => setCaptchaToken(''),
          'error-callback':   ()  => setCaptchaToken(''),
        });
      } catch (_) { /* probably already rendered */ }
    }
    tryRender();
    return () => { polled = false; };
  }, []);

  // If a submission fails, reset the captcha so the user gets a fresh challenge
  function resetCaptcha() {
    setCaptchaToken('');
    if (window.hcaptcha && captchaWidgetIdRef.current != null) {
      try { window.hcaptcha.reset(captchaWidgetIdRef.current); } catch (_) {}
    }
  }

  const handleSubmit = async () => {
    setSubmitted(true);
    setSubmitError('');
    if (!canSubmit || isSubmitting) return;

    setIsSubmitting(true);
    try {
      const backendRegistration = await window.submitBackendRegistration({
        course,
        intake,
        learners,
        company: {},
        fees,
        registrantType: 'individual',
        useSkillsFuture: useSkillsFutureAnswer === 'yes',
        preferredContact,
        pdpaAcknowledged: agreedPdpa,
        termsAcknowledged: agreedTerms,
        wshTrsConsentAcknowledged: requiresWshTrsConsent ? agreedWshTrs : false,
        marketingConsentAcknowledged: agreedMarketing,
        captchaToken,
      });
      onSubmit({ course, intake, learners, company: {}, fees, registrantType: 'individual', backendRegistration });
    } catch (err) {
      setSubmitError(err.message || 'Unable to submit registration. Please try again.');
      resetCaptcha();
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
    <div style={{ padding: isMobile ? '16px 16px 24px' : '24px 32px 40px' }}>
      <div style={{ display: 'grid', gridTemplateColumns: isMobile ? '1fr' : 'minmax(0, 1fr) 360px', gap: isMobile ? 16 : 28, alignItems: 'start' }}>
        {/* — Form column — */}
        <div className="akc-stack-lg" style={{ gap: 18, minWidth: 0 }}>
          <button className="akc-btn akc-btn--link" onClick={onBack} style={{ alignSelf: 'flex-start', padding: 0 }}>
            ← Back to intakes
          </button>

          <header className="akc-stack" style={{ gap: 6 }}>
            <div className="akc-eyebrow">Step 2 of 3 · Learner Registration Form</div>
            <h1 className="akc-h1">Who's attending?</h1>
            <p style={{ margin: 0, color: 'var(--ink-2)' }}>
              All fields are required. Course date, language and venue are locked from your selected intake.
            </p>
          </header>

          {/* Learner card (individual — single learner only) */}
          <section className="akc-stack" style={{ gap: 12 }}>
            <h3 className="akc-h3" style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <span style={{ width: 4, height: 16, background: 'var(--primary)', borderRadius: 2 }} />
              Your Details
            </h3>
            <LearnerCard
              idx={0}
              showIndex={false}
              learner={learners[0]}
              errors={submitted ? learnerErrors[0] : {}}
              canRemove={false}
              onChange={(patch) => updateLearner(0, patch)}
              onRemove={() => {}}
              isMobile={isMobile}
            />
          </section>

          {/* Preferences */}
          <section className="akc-card akc-stack" style={{ gap: 16 }}>
            <h3 className="akc-h3" style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <span style={{ width: 4, height: 16, background: 'var(--primary)', borderRadius: 2 }} />
              Preferences
            </h3>

            {/* SkillsFuture credit */}
            <div className="akc-stack" style={{ gap: 8 }}>
              <label className="akc-field-label" style={{ margin: 0 }}>
                Use SkillsFuture Credit? <span style={{ color: 'var(--danger)' }}>*</span>
              </label>
              <p style={{ margin: 0, fontSize: 12, color: 'var(--muted)' }}>
                Eligible Singapore Citizens aged 25+ may offset eligible course fees with their SkillsFuture Credit balance.
              </p>
              <div style={{ display: 'flex', gap: 10 }}>
                {[
                  { v: 'yes', label: 'Yes' },
                  { v: 'no',  label: 'No' },
                ].map(opt => (
                  <button
                    key={opt.v}
                    type="button"
                    onClick={() => setUseSkillsFutureAnswer(opt.v)}
                    style={{
                      flex: 1,
                      padding: '10px 14px',
                      borderRadius: 8,
                      border: useSkillsFutureAnswer === opt.v
                        ? '2px solid var(--primary)'
                        : '1.5px solid var(--line)',
                      background: useSkillsFutureAnswer === opt.v
                        ? 'var(--primary-050, #f0f5ff)'
                        : 'var(--surface)',
                      color: useSkillsFutureAnswer === opt.v ? 'var(--primary-700)' : 'var(--ink)',
                      fontWeight: useSkillsFutureAnswer === opt.v ? 700 : 500,
                      fontSize: 13,
                      cursor: 'pointer',
                      textAlign: 'center',
                      transition: 'all 0.15s ease',
                    }}
                  >
                    {opt.label}
                  </button>
                ))}
              </div>
              {submitted && !useSkillsFutureAnswer && (
                <div className="akc-error">Please select whether you want to use SkillsFuture Credit.</div>
              )}
            </div>

            {/* Preferred contact method */}
            <div className="akc-stack" style={{ gap: 8 }}>
              <label className="akc-field-label" style={{ margin: 0 }}>
                Preferred contact method <span style={{ color: 'var(--danger)' }}>*</span>
              </label>
              <p style={{ margin: 0, fontSize: 12, color: 'var(--muted)' }}>
                How would you like our customer service team to reach you?
              </p>
              <div style={{ display: 'flex', gap: 10 }}>
                {[
                  { v: 'email',    label: '✉ Email',    desc: 'We will email you at the address provided above.' },
                  { v: 'whatsapp', label: '💬 WhatsApp', desc: 'We will message you on the mobile number provided above.' },
                ].map(opt => (
                  <button
                    key={opt.v}
                    type="button"
                    onClick={() => setPreferredContact(opt.v)}
                    style={{
                      flex: 1,
                      padding: '10px 14px',
                      borderRadius: 8,
                      border: preferredContact === opt.v
                        ? '2px solid var(--primary)'
                        : '1.5px solid var(--line)',
                      background: preferredContact === opt.v
                        ? 'var(--primary-050, #f0f5ff)'
                        : 'var(--surface)',
                      color: preferredContact === opt.v ? 'var(--primary-700)' : 'var(--ink)',
                      fontWeight: preferredContact === opt.v ? 700 : 500,
                      fontSize: 13,
                      cursor: 'pointer',
                      textAlign: 'center',
                      transition: 'all 0.15s ease',
                    }}
                  >
                    {opt.label}
                  </button>
                ))}
              </div>
              {submitted && !preferredContact && (
                <div className="akc-error">Please select a preferred contact method.</div>
              )}
            </div>
          </section>

          {/* Consent */}
          <section className="akc-card akc-stack akc-consent-section">
            <div className="akc-consent-head">
              <div>
                <div className="akc-kicker">Required acknowledgements</div>
                <h3>Before you submit</h3>
              </div>
              <span>Required</span>
            </div>

            <label className="akc-check">
              <input type="checkbox" checked={agreedPdpa} onChange={(e) => setAgreedPdpa(e.target.checked)} />
              <span>I consent to AKC collecting and using my personal data for course administration, in accordance with the <a className="akc-link" href="https://www.sg-akc.com/privacy-policy/" target="_blank" rel="noopener noreferrer">PDPA Notice</a>.</span>
            </label>
            <label className="akc-check">
              <input type="checkbox" checked={agreedTerms} onChange={(e) => setAgreedTerms(e.target.checked)} />
              <span><strong>Refund Policy:</strong> Requests for refunds, changes, or cancellation of course registrations must be submitted in writing and are subject to our terms and conditions. Please review our <a className="akc-link" href="https://www.sg-akc.com/terms-and-condition/" target="_blank" rel="noopener noreferrer">Terms & Conditions</a> before registering for the course.</span>
            </label>
            <label className="akc-check">
              <input type="checkbox" checked={agreedMarketing} onChange={(e) => setAgreedMarketing(e.target.checked)} />
              <span>Yes, I agree to receive course updates and promotional emails from Absolute Kinetics Consultancy.</span>
            </label>
            {requiresWshTrsConsent && (
              <label className="akc-check">
                <input type="checkbox" checked={agreedWshTrs} onChange={(e) => setAgreedWshTrs(e.target.checked)} />
                <span>
                  I understand that individuals using the Check Workers' WSH Training Records eService on the MOM website can verify an applicant's training records, including identification number, name, eligibility for 4 years of Safety Orientation Course (CSOC/SSIC/MSOC) certification where applicable, course title, training provider, date of assessment, certificate expiry date, and result of assessment. I consent to Absolute Kinetics Consultancy Pte Ltd using and disclosing my personal data and training records to companies that access MOM's Training Record System (TRS).
                </span>
              </label>
            )}

            {/* hCaptcha — blocks bots without making real users click traffic lights */}
            <div style={{ marginTop: 12 }}>
              <div ref={captchaContainerRef} className="h-captcha"></div>
            </div>

            {submitted && !canSubmit && (
              <div className="akc-error" style={{ marginTop: 6 }}>
                {totalLearnerErrors > 0
                  ? `Please complete ${totalLearnerErrors} field${totalLearnerErrors > 1 ? 's' : ''} above.`
                  : !useSkillsFutureAnswer
                  ? 'Please select whether you want to use SkillsFuture Credit.'
                  : !preferredContact
                  ? 'Please select a preferred contact method.'
                  : !agreedPdpa || !agreedTerms
                  ? 'Please accept both the PDPA and Terms before submitting.'
                  : requiresWshTrsConsent && !agreedWshTrs
                  ? 'Please accept the MOM Training Record System consent before submitting.'
                  : !captchaToken
                  ? 'Please complete the human-check above.'
                  : ''}
              </div>
            )}
            {submitError && (
              <div className="akc-error" style={{ marginTop: 6 }}>
                {submitError}
              </div>
            )}
          </section>

          {/* Mobile: inline fee panel + submit */}
          {isMobile && (
            <FeePanel device="mobile" course={course} intake={intake} fees={fees} learnerCount={learners.length} showGrant={showGrant} onSubmit={handleSubmit} isSubmitting={isSubmitting} />
          )}

          {!isMobile && (
            <div style={{ display: 'flex', gap: 12, justifyContent: 'flex-end' }}>
              <button className="akc-btn akc-btn--ghost" onClick={onBack}>Back</button>
              <button className="akc-btn akc-btn--primary akc-btn--lg" onClick={handleSubmit} disabled={isSubmitting}>
                {isSubmitting ? 'Submitting...' : 'Submit registration →'}
              </button>
            </div>
          )}
        </div>

        {/* Desktop: sticky fee panel sidebar */}
        {!isMobile && (
          <aside style={{ position: 'sticky', top: 16 }}>
            <FeePanel device="desktop" course={course} intake={intake} fees={fees} learnerCount={learners.length} showGrant={showGrant} onSubmit={handleSubmit} isSubmitting={isSubmitting} />
          </aside>
        )}
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────────────
// Learner card — 8 fields, 2-col grid
// ───────────────────────────────────────────────────────────────────────────

function LearnerCard({ idx, showIndex, learner, errors, canRemove, onChange, onRemove, isMobile }) {
  return (
    <div className="akc-learner">
      {(showIndex || canRemove) && (
        <div className="akc-learner-head">
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <span style={{
              width: 26, height: 26, borderRadius: 8, background: 'var(--primary-100)', color: 'var(--primary)',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontSize: 12,
            }}>L{idx + 1}</span>
            <span style={{ fontWeight: 700 }}>Learner {idx + 1}</span>
          </div>
          {canRemove && (
            <button className="akc-btn akc-btn--link" onClick={onRemove} style={{ color: 'var(--danger)' }}>
              Remove
            </button>
          )}
        </div>
      )}

      <div style={{
        display: 'grid', gridTemplateColumns: isMobile ? '1fr' : '1fr 1fr', gap: 12,
      }}>
        <Field label="Learner Name *" error={errors.learner_name}>
          <input className={'akc-input' + (errors.learner_name ? ' akc-input--invalid' : '')}
                 value={learner.learner_name} onChange={(e) => onChange({ learner_name: e.target.value })}
                 placeholder="As per NRIC / Passport" required />
        </Field>
        <Field label="Date of Birth *" error={errors.dob}>
          <input type="date" className={'akc-input akc-tabular' + (errors.dob ? ' akc-input--invalid' : '')}
                 value={learner.dob} onChange={(e) => onChange({ dob: e.target.value })}
                 min={dobMinIso()} max={localDateIso()} required />
        </Field>

        <Field label="Learner Email *" error={errors.email}>
          <input type="email" className={'akc-input' + (errors.email ? ' akc-input--invalid' : '')}
                 value={learner.email} onChange={(e) => onChange({ email: e.target.value })}
                 placeholder="learner@email.com" required />
        </Field>
        <Field label="Learner mobile no. *" error={errors.mobile}>
          <input type="tel" className={'akc-input akc-tabular' + (errors.mobile ? ' akc-input--invalid' : '')}
                 value={learner.mobile} onChange={(e) => onChange({ mobile: e.target.value })}
                 onBlur={() => {
                   const normalized = normalizeSingaporePhone(learner.mobile);
                   if (normalized) onChange({ mobile: normalized });
                 }}
                 placeholder="+65 9123 4567" required />
        </Field>

        <Field label="ID Number *" error={errors.id_number} hint="Masked in admin views.">
          <input className={'akc-input akc-mono' + (errors.id_number ? ' akc-input--invalid' : '')} required
                 value={learner.id_number}
                 onChange={(e) => onChange({ id_number: normalizeIdNumber(e.target.value, learner.id_type) })}
                 onBlur={() => onChange({ id_number: normalizeIdNumber(learner.id_number, learner.id_type) })}
                 autoComplete="new-password"
                 autoCorrect="off"
                 spellCheck={false}
                 maxLength={learner.id_type === 'Passport' ? 20 : 12}
                 placeholder="S1234567A" />
        </Field>
        <Field label="ID Type *" error={errors.id_type}>
          <select className={'akc-select' + (errors.id_type ? ' akc-input--invalid' : '')} value={learner.id_type}
                  onChange={(e) => onChange({ id_type: e.target.value, id_number: normalizeIdNumber(learner.id_number, e.target.value) })} required>
            <option value="">Select...</option>
            {ID_TYPES.map(t => <option key={t} value={t}>{t}</option>)}
          </select>
        </Field>

        <Field label="Nationality *" hint="Start typing, e.g. SINGAPORE" error={errors.nationality}>
          <Combobox
            value={learner.nationality}
            options={window.AKC_NATIONALITIES || []}
            placeholder="e.g. SINGAPORE CITIZEN | SG"
            invalid={!!errors.nationality}
            onChange={(v) => onChange({ nationality: v })}
          />
        </Field>
        <Field label="Race *" hint="Start typing, e.g. CHINESE" error={errors.race}>
          <Combobox
            value={learner.race}
            options={window.AKC_RACES || []}
            placeholder="e.g. CHINESE | CN"
            invalid={!!errors.race}
            onChange={(v) => onChange({ race: v })}
          />
        </Field>
      </div>
    </div>
  );
}

// ───────────────────────────────────────────────────────────────────────────
// Fee panel — unchanged but uses new profile + adds "SME rate assumed" note
// ───────────────────────────────────────────────────────────────────────────

function FeePanel({ device, course, intake, fees, learnerCount, showGrant, onSubmit, isSubmitting }) {
  const isMobile = device === 'mobile';
  const selectedFullFee = Number(intake.full_fee || course.full_fee || 0);
  const grantEligible = fees.fundingEligible == null ? showGrant : fees.fundingEligible;
  return (
    <div className="akc-card" style={{ padding: 0, overflow: 'hidden' }}>
      <div style={{ padding: 18, borderBottom: '1px solid var(--line)' }}>
        <div className="akc-eyebrow" style={{ marginBottom: 6 }}>Your selected class</div>
        <div style={{ fontWeight: 700, color: 'var(--display-ink)', fontSize: 15, marginBottom: 8, lineHeight: 1.3 }}>
          {course.name}
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: 12, color: 'var(--ink-2)' }}>
          <div style={{ display: 'flex', gap: 8 }}><span style={{ color: 'var(--muted)', minWidth: 64 }}>Dates</span><span style={{ fontWeight: 600, color: 'var(--ink)' }}>{window.fmtDateRange(intake.start, intake.end)}</span></div>
          <div style={{ display: 'flex', gap: 8 }}><span style={{ color: 'var(--muted)', minWidth: 64 }}>Time</span><span>{intake.start_time}–{intake.end_time}</span></div>
          <div style={{ display: 'flex', gap: 8 }}><span style={{ color: 'var(--muted)', minWidth: 64 }}>Venue</span><span>{intake.venue}</span></div>
          <div style={{ display: 'flex', gap: 8 }}><span style={{ color: 'var(--muted)', minWidth: 64 }}>Language</span><span>{intake.language}</span></div>
          {intake.backend?.mom_reference_no && (
            <div style={{ display: 'flex', gap: 8 }}><span style={{ color: 'var(--muted)', minWidth: 64 }}>TGS Code</span><span>{intake.backend.mom_reference_no}</span></div>
          )}
        </div>
      </div>

      {grantEligible ? (
        <div style={{ padding: 18 }}>
          <div className="akc-eyebrow" style={{ marginBottom: 8 }}>Estimated fees · per learner</div>
          <div className="akc-fee-row"><span className="lbl">Course fee (before GST)</span><span className="val akc-tabular">{window.money(fees.feeBeforeGst)}</span></div>
          <div className="akc-fee-row akc-fee-row--funding">
            <span className="lbl">SSG funding ({Math.round(fees.rate * 100)}%)</span>
            <span className="val akc-tabular">−{window.money(fees.funding)}</span>
          </div>
          <div className="akc-fee-row"><span className="lbl">Nett fee</span><span className="val akc-tabular">{window.money(fees.nett)}</span></div>
          <div className="akc-fee-row"><span className="lbl">GST (9%)</span><span className="val akc-tabular">{window.money(fees.gst)}</span></div>
          {learnerCount > 1 && (
            <div className="akc-fee-row" style={{ paddingTop: 12, borderTop: '1px solid var(--line)' }}>
              <span className="lbl">× {learnerCount} learners</span>
              <span className="val akc-tabular">{window.money(fees.perLearner)} ea</span>
            </div>
          )}
          <div className="akc-fee-row akc-fee-row--total">
            <span className="lbl">Estimated total</span>
            <span className="val akc-tabular">{window.money(fees.total)}</span>
          </div>
          {fees.rate > 0 && (
            <div style={{
              marginTop: 10, padding: '8px 10px', background: 'var(--good-bg)', color: 'var(--good)',
              fontSize: 11, fontWeight: 600, borderRadius: 8,
            }}>
              You save {window.money(fees.totalFundingValue)} with SSG funding
            </div>
          )}
          <div style={{ marginTop: 10, fontSize: 10.5, color: 'var(--muted)', lineHeight: 1.5 }}>
            Funding estimates are based on your eligibility profile. GST is calculated on the original course fee before subsidy. Final eligibility is subject to verification and approval.
          </div>
        </div>
      ) : (
        <div style={{ padding: 18 }}>
          <div className="akc-eyebrow" style={{ marginBottom: 8 }}>Course fee · no SSG Training Grant</div>
          <div className="akc-fee-row"><span className="lbl">Per learner (before GST)</span><span className="val akc-tabular">{window.money(fees.feeBeforeGst)}</span></div>
          <div className="akc-fee-row"><span className="lbl">GST (9%)</span><span className="val akc-tabular">{window.money(fees.gst)}</span></div>
          <div className="akc-fee-row akc-fee-row--total">
            <span className="lbl">Total × {learnerCount}</span>
            <span className="val akc-tabular">{window.money(fees.total)}</span>
          </div>
          <div style={{ marginTop: 10, fontSize: 10.5, color: 'var(--muted)', lineHeight: 1.5 }}>
            This course does not currently receive an SSG Training Grant. Eligible Singapore Citizens may still use SkillsFuture Credit, subject to their available balance.
          </div>
        </div>
      )}

      <div style={{ padding: 16, borderTop: '1px solid var(--line)', background: 'var(--surface)' }}>
        <button className="akc-btn akc-btn--primary akc-btn--block akc-btn--lg" onClick={onSubmit} disabled={isSubmitting}>
          {isSubmitting ? 'Submitting...' : 'Submit registration →'}
        </button>
        <div style={{ textAlign: 'center', marginTop: 8, fontSize: 11, color: 'var(--muted)' }}>
          No payment due yet. Payment will be made after customer service team sends you a confirmation email.
        </div>
      </div>
    </div>
  );
}

function Tick() {
  return (
    <svg width="13" height="13" viewBox="0 0 13 13" fill="none" style={{ flex: '0 0 auto' }}>
      <circle cx="6.5" cy="6.5" r="6.5" fill="var(--good)" />
      <path d="M3.5 6.8l2.2 2.2L9.5 4.5" stroke="white" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" fill="none"/>
    </svg>
  );
}

// ───────────────────────────────────────────────────────────────────────────
// Confirmation
// ───────────────────────────────────────────────────────────────────────────

function ConfirmationScreen({ device, course, intake, fees, learnerCount, registrationId, status, onReset }) {
  const isMobile = device === 'mobile';
  const ref = registrationId || 'AKC-' + Date.now().toString(36).toUpperCase().slice(-6);
  return (
    <div style={{ padding: isMobile ? '16px' : '32px', maxWidth: 640, margin: '0 auto' }}>
      <div className="akc-card akc-success">
        <div className="tick">✓</div>
        <div className="akc-eyebrow">Step 3 of 3 · Confirmed</div>
        <h1 className="akc-h1">Registration submitted.</h1>
        <p style={{ margin: 0, color: 'var(--ink-2)', maxWidth: 420 }}>
          We've emailed your registration confirmation and proforma invoice to you.
          Please quote reference no. <strong className="akc-mono">{ref}</strong> when contacting us.
        </p>

        <div style={{ width: '100%', marginTop: 12, padding: 16, background: 'var(--surface-2)', borderRadius: 10, textAlign: 'left' }}>
          <div className="akc-eyebrow" style={{ marginBottom: 8 }}>What's confirmed</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: 13 }}>
            <div><strong>{course.name}</strong></div>
            <div style={{ color: 'var(--ink-2)' }}>{window.fmtDateRange(intake.start, intake.end)} · {intake.venue}</div>
            <div style={{ color: 'var(--ink-2)' }}>
              {learnerCount} learner{learnerCount > 1 ? 's' : ''} · estimated <strong>{window.money(fees.total)}</strong>
              {fees.fundingEligible === false ? ' total payable' : ' after funding'}
            </div>
            {status && <div style={{ color: 'var(--ink-2)' }}>Status · <strong>{status}</strong></div>}
          </div>
        </div>

        <div style={{ display: 'flex', gap: 10, marginTop: 8, flexWrap: 'wrap' }}>
          <button className="akc-btn akc-btn--ghost" onClick={onReset}>Back to courses</button>
          <a
            href={'/booking/track/' + ref}
            className="akc-btn akc-btn--primary"
            style={{ textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 6 }}
          >
            Track your registration →
          </a>
        </div>

        <div style={{ marginTop: 16, fontSize: 11, color: 'var(--muted)', maxWidth: 420 }}>
          {fees.fundingEligible === false
            ? 'No SSG Training Grant is available for this course. SkillsFuture Credit may still be used if eligible.'
            : "Final SSG funding eligibility is subject to verification and approval. We'll confirm your funded fee within 2 working days."}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { RegistrationScreen, ConfirmationScreen, FeePanel });
