/**
 * ReservationForm Component
 * Description: Three-step seat reservation, posted through /api/training-register
 * Dependencies: window.AppData.TRAINING
 *
 * Answers to choice fields are stored as option INDEXES, not label strings,
 * so switching VN/EN mid-form keeps every choice the user already made.
 * They are converted back to readable labels only when submitting, so the
 * inbox shows "Kiến trúc sư" rather than "role: 2".
 *
 * `setLang` is optional and drives the VN/EN toggle rendered in the modal
 * head - without it that mid-form language switch is unreachable, because
 * the backdrop covers the page header and the focus trap excludes it.
 */

window.ReservationForm = ({ lang, setLang, onClose }) => {
    const { useState, useEffect, useRef } = React;
    const DATA = window.AppData.TRAINING;
    const fm = DATA[lang].form;

    const [step, setStep] = useState(0);
    const [values, setValues] = useState({});
    const [showErr, setShowErr] = useState(false);
    const [status, setStatus] = useState('idle');   // idle | sending | done | error
    const [errCode, setErrCode] = useState('');     // server error code, if any

    const boxRef = useRef(null);

    // Queried live at call time (never cached) - the field set changes
    // between steps, and a stale list would trap focus onto elements
    // that no longer exist.
    const FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), input:not([disabled]), ' +
        'textarea:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
    const getFocusable = () => (boxRef.current
        ? Array.from(boxRef.current.querySelectorAll(FOCUSABLE_SELECTOR)).filter((el) => el.offsetParent !== null)
        : []);

    // "Ô đầu tiên" is the first form field, not merely the first tabbable
    // control (which in DOM order is the ✕ close button in the header).
    const focusFirstField = () => {
        if (!boxRef.current) return;
        const field = boxRef.current.querySelector(
            '.tr-modal-body input:not([disabled]), .tr-modal-body textarea:not([disabled])'
        );
        const target = field || getFocusable()[0];
        if (target) target.focus();
    };

    useEffect(() => {
        document.body.style.overflow = 'hidden';
        const onKey = (e) => {
            if (e.key === 'Escape') { onClose(); return; }
            if (e.key !== 'Tab') return;
            const list = getFocusable();
            if (!list.length) return;
            const first = list[0];
            const last = list[list.length - 1];
            const activeInBox = boxRef.current && boxRef.current.contains(document.activeElement);
            if (e.shiftKey) {
                if (!activeInBox || document.activeElement === first) {
                    e.preventDefault();
                    last.focus();
                }
            } else if (!activeInBox || document.activeElement === last) {
                e.preventDefault();
                first.focus();
            }
        };
        document.addEventListener('keydown', onKey);
        return () => {
            document.body.style.overflow = '';
            document.removeEventListener('keydown', onKey);
        };
    }, [onClose]);

    // Focus the first field on mount, and again whenever the step (or the
    // done/error outcome) swaps the box's content for a different set of
    // controls - otherwise focus is left on a control that may no longer exist.
    useEffect(() => {
        focusFirstField();
    }, [step, status]);

    const setValue = (key, val) => setValues((prev) => {
        const next = {};
        Object.keys(prev).forEach((k) => { next[k] = prev[k]; });
        next[key] = val;
        return next;
    });

    const toggleCheck = (key, i) => {
        const cur = Array.isArray(values[key]) ? values[key] : [];
        setValue(key, cur.indexOf(i) > -1 ? cur.filter((x) => x !== i) : cur.concat([i]));
    };

    // undefined/null/blank-string/empty-array read as unanswered - importantly,
    // 0 does NOT (a choice field's first option is stored as index 0, and
    // 0 is a valid answer, not a missing one).
    const isEmpty = (v) => v === undefined || v === null
        || (Array.isArray(v) ? v.length === 0 : String(v).trim() === '');

    // Shape only, never deliverability - there is no <form> in the modal, so
    // type="email" never triggers native constraint validation and "asdf"
    // would otherwise reach the inbox as the only way back to the lead.
    const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    const isBadEmail = (f) => f.t === 'email' && !isEmpty(values[f.k])
        && !EMAIL_RE.test(String(values[f.k]).trim());

    const current = fm.steps[step];
    const isLast = step === fm.steps.length - 1;
    const missing = current.fields.some((f) => f.req && isEmpty(values[f.k]));
    const badEmail = current.fields.some(isBadEmail);

    // Indexes become labels here so the inbox is readable. The result is an
    // ORDERED ARRAY of {k, l, v} - the stable key stays separate from the
    // display label, so the server can allowlist on `k` and never has to turn
    // a client-supplied string into a payload key.
    const buildAnswers = () => {
        const out = [];
        fm.steps.forEach((s) => s.fields.forEach((f) => {
            const v = values[f.k];
            if (v === undefined || v === '') return;
            if (f.t === 'check') {
                const picked = (Array.isArray(v) ? v : []).map((i) => f.o[i]).join(', ');
                if (picked) out.push({ k: f.k, l: f.l, v: picked });
            } else if (f.t === 'radio') {
                if (f.o[v] !== undefined) out.push({ k: f.k, l: f.l, v: f.o[v] });
            } else {
                out.push({ k: f.k, l: f.l, v: String(v) });
            }
        }));
        return out;
    };

    const submit = async () => {
        setStatus('sending');
        try {
            const response = await fetch('/api/training-register', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ language: lang, answers: buildAnswers() })
            });
            const data = await response.json().catch(() => ({}));
            if (response.ok && data.success) { setErrCode(''); setStatus('done'); return; }
            setErrCode(typeof data.error === 'string' ? data.error : '');
            setStatus('error');
        } catch (err) {
            setErrCode('network');
            setStatus('error');
        }
    };

    const advance = () => {
        if (missing || badEmail) { setShowErr(true); return; }
        setShowErr(false);
        if (isLast) submit(); else setStep(step + 1);
    };

    const renderField = (f) => {
        if (f.t === 'radio' || f.t === 'check') {
            const multi = f.t === 'check';
            const sel = multi ? (Array.isArray(values[f.k]) ? values[f.k] : []) : values[f.k];
            return (
                <div className="tr-fld" key={f.k}>
                    <label>{f.l}</label>
                    <div className="tr-opts">
                        {f.o.map((o, i) => (
                            <label key={o}>
                                <input type={multi ? 'checkbox' : 'radio'} name={f.k}
                                       checked={multi ? sel.indexOf(i) > -1 : sel === i}
                                       onChange={() => (multi ? toggleCheck(f.k, i) : setValue(f.k, i))} />
                                {o}
                            </label>
                        ))}
                    </div>
                </div>
            );
        }
        if (f.t === 'area') {
            return (
                <div className="tr-fld" key={f.k}>
                    <label>{f.l} <span style={{ opacity: .55 }}>({fm.optional})</span></label>
                    <textarea name={f.k} placeholder={f.ph || ''} value={values[f.k] || ''}
                              maxLength={2000}
                              onChange={(e) => setValue(f.k, e.target.value)} />
                </div>
            );
        }
        const bad = showErr && ((f.req && isEmpty(values[f.k])) || isBadEmail(f));
        return (
            <div className={'tr-fld' + (bad ? ' bad' : '')} key={f.k}>
                <label>{f.l}{f.req ? <b> *</b> : null}</label>
                <input type={f.t === 'email' ? 'email' : 'text'} name={f.k}
                       value={values[f.k] || ''}
                       onChange={(e) => setValue(f.k, e.target.value)} />
                {f.hint ? <span className="tr-modal-note">{f.hint}</span> : null}
            </div>
        );
    };

    // The endpoint returns distinct codes; a rate-limited user told to "check
    // your connection" would just retry and fail again.
    const errorText = errCode === 'rate_limit' ? fm.errRate
        : (errCode === 'unconfigured' ? fm.errConfig : fm.errSend);
    const hintText = showErr ? (missing ? fm.err : fm.errEmail) : fm.note;

    const langToggle = typeof setLang === 'function' ? (
        <div className="tr-lang">
            <button type="button" className={lang === 'vi' ? 'on' : ''}
                    onClick={() => setLang('vi')}>VN</button>
            <button type="button" className={lang === 'en' ? 'on' : ''}
                    onClick={() => setLang('en')}>EN</button>
        </div>
    ) : null;

    return (
        <div className="tr-modal" role="dialog" aria-modal="true" aria-label={fm.title}>
            <div className="tr-modal-back" onClick={onClose}></div>
            <div className="tr-modal-box" ref={boxRef}>
                {status === 'done' ? (
                    <React.Fragment>
                        <div className="tr-modal-head">
                            <h2 className="tr-modal-title">{fm.doneTitle}</h2>
                            <button type="button" className="tr-modal-x" onClick={onClose}
                                    aria-label={fm.close}>&#10005;</button>
                        </div>
                        <div className="tr-done">
                            <div className="tr-done-mark">&#10003;</div>
                            <p>{fm.doneBody}</p>
                            <p className="tr-modal-note">{fm.doneNote}</p>
                            <button type="button" className="tr-btn" onClick={onClose}>{fm.close}</button>
                        </div>
                    </React.Fragment>
                ) : (
                    <React.Fragment>
                        <div className="tr-modal-head">
                            <div>
                                <h2 className="tr-modal-title">{fm.title}</h2>
                                <p className="tr-modal-sub">
                                    {current.h} &middot; {step + 1} / {fm.steps.length}
                                </p>
                            </div>
                            <div className="tr-modal-tools">
                                {langToggle}
                                <button type="button" className="tr-modal-x" onClick={onClose}
                                        aria-label={fm.close}>&#10005;</button>
                            </div>
                        </div>
                        <div className="tr-modal-steps">
                            {fm.steps.map((s, i) => (
                                <i key={s.h} className={i <= step ? 'on' : ''}></i>
                            ))}
                        </div>
                        <div className="tr-modal-body">
                            {current.fields.map(renderField)}
                            <p className="tr-modal-note"
                               style={showErr || status === 'error' ? { color: '#EB4A2E' } : null}>
                                {status === 'error' ? errorText : hintText}
                            </p>
                        </div>
                        <div className="tr-modal-foot">
                            {step > 0 ? (
                                <button type="button" className="tr-btn link"
                                        onClick={() => { setShowErr(false); setStep(step - 1); }}>
                                    &larr; {fm.back}
                                </button>
                            ) : <span></span>}
                            <button type="button" className="tr-btn" onClick={advance}
                                    disabled={status === 'sending'}>
                                {status === 'sending' ? '…' : (isLast ? fm.submit : fm.next)}
                            </button>
                        </div>
                    </React.Fragment>
                )}
            </div>
        </div>
    );
};
