/* Family interest intake is deliberately separate from scholarship applications. */
const campInterestMaxCampers = 16;
const campInterestValidName = (value) => typeof value === 'string' && value.trim().length > 0 && value.trim().length <= 60 && !/\p{Cc}/u.test(value);
const campInterestValidPhone = (value) => {
  if (typeof value !== 'string' || /\p{Cc}/u.test(value)) return false;
  const phone = value.trim();
  if (!phone) return true;
  const digits = phone.replace(/\D/g, '').length;
  return phone.length <= 40 && /^\+?[0-9(). -]+$/.test(phone) && digits >= 7 && digits <= 15;
};

function campInterestReadAttempt(storageKey) {
  try {
    const saved = JSON.parse(window.sessionStorage.getItem(storageKey));
    if (!saved || typeof saved !== 'object' || Array.isArray(saved) || typeof saved.submissionKey !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(saved.submissionKey)) return null;
    if (typeof saved.parentEmail !== 'string' || saved.parentEmail.length > 254 || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(saved.parentEmail.trim()) || typeof saved.updatesOptIn !== 'boolean') return null;
    const legacy = Object.hasOwn(saved, 'parentName');
    const savedPhone = Object.hasOwn(saved, 'parentPhone') ? saved.parentPhone : '';
    const fields = new Set(['parentEmail', 'campers', 'updatesOptIn', 'submissionKey', ...(legacy ? ['parentName'] : ['parentFirstName', 'parentLastName', 'parentPhone'])]);
    if (Object.keys(saved).some((key) => !fields.has(key))) return null;
    if (legacy) {
      if (typeof saved.parentName !== 'string' || saved.parentName.length > 120 || Array.from(saved.parentName).some((character) => character.codePointAt(0) < 32 || character.codePointAt(0) === 127)) return null;
    } else if (!campInterestValidName(saved.parentFirstName) || !campInterestValidName(saved.parentLastName) || !campInterestValidPhone(savedPhone)) return null;
    if (!Array.isArray(saved.campers) || !saved.campers.length || saved.campers.length > campInterestMaxCampers || saved.campers.some((child) => !child || typeof child !== 'object' || Array.isArray(child) || Object.keys(child).some((key) => !['grade', 'interest'].includes(key)) || !Number.isInteger(child.grade) || child.grade < 5 || child.grade > 12 || !['strong', 'possible'].includes(child.interest))) return null;
    // Keep an older saved response intact so its UUID can confirm a receipt.
    // New responses always collect first and last names separately.
    return legacy ? saved : { ...saved, parentPhone: savedPhone };
  } catch { return null; }
}

function campInterestUUID() {
  if (window.crypto.randomUUID) return window.crypto.randomUUID();
  const bytes = window.crypto.getRandomValues(new Uint8Array(16));
  bytes[6] = (bytes[6] & 15) | 64;
  bytes[8] = (bytes[8] & 63) | 128;
  const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
  return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}

function CampInterestTurnstile({ siteKey, onToken, onProblem }) {
  const container = React.useRef(null);
  React.useEffect(() => {
    let timer;
    let widget;
    let stopped = false;
    let checks = 0;
    const source = 'https://challenges.cloudflare.com/turnstile/v0/api.js';
    if (!Array.from(document.scripts).some((script) => script.src.startsWith(source))) {
      const script = document.createElement('script');
      script.src = `${source}?render=explicit`;
      script.async = true;
      script.defer = true;
      document.head.appendChild(script);
    }
    const render = () => {
      if (stopped || !container.current) return;
      if (!window.turnstile) {
        if (++checks > 75) { onProblem('The verification check could not load. Reload this page and try again.'); return; }
        timer = setTimeout(render, 200);
        return;
      }
      try {
        widget = window.turnstile.render(container.current, {
          sitekey: siteKey,
          action: 'camp-interest',
          size: 'flexible',
          callback: (token) => { onToken(token); onProblem(''); },
          'expired-callback': () => onToken(''),
          'error-callback': () => { onToken(''); onProblem('The verification check could not finish. Please try again.'); },
        });
      } catch { onProblem('The verification check could not load. Reload this page and try again.'); }
    };
    render();
    return () => {
      stopped = true;
      clearTimeout(timer);
      if (widget !== undefined && window.turnstile) window.turnstile.remove(widget);
    };
  }, [siteKey, onToken, onProblem]);
  return <div className="wk-camp-interest-verification" ref={container} />;
}

function CampInterestForm() {
  const config = window.WKYHB.camp || {};
  const year = config.year || 2028;
  const storageKey = `wkyhb:camp-interest:${year}:pending`;
  const initialAttempt = React.useRef(campInterestReadAttempt(storageKey));
  const [parentEmail, setParentEmail] = React.useState(initialAttempt.current?.parentEmail || '');
  const [parentFirstName, setParentFirstName] = React.useState(initialAttempt.current?.parentFirstName || '');
  const [parentLastName, setParentLastName] = React.useState(initialAttempt.current?.parentLastName || '');
  const [parentPhone, setParentPhone] = React.useState(initialAttempt.current?.parentPhone || '');
  const [campers, setCampers] = React.useState(initialAttempt.current?.campers || [{ grade: '', interest: '' }]);
  const [updatesOptIn, setUpdatesOptIn] = React.useState(initialAttempt.current?.updatesOptIn || false);
  const [attempt, setAttempt] = React.useState(initialAttempt.current);
  const attemptRef = React.useRef(initialAttempt.current);
  const [busy, setBusy] = React.useState(false);
  const busyRef = React.useRef(false);
  const [errors, setErrors] = React.useState({});
  const [notice, setNotice] = React.useState(initialAttempt.current ? 'A response from this browser is awaiting confirmation. Try again to check safely without counting your family twice.' : '');
  const [duplicate, setDuplicate] = React.useState(false);
  const [receipt, setReceipt] = React.useState(null);
  const [token, setToken] = React.useState('');
  const [verificationProblem, setVerificationProblem] = React.useState('');
  const [verificationVersion, setVerificationVersion] = React.useState(0);
  const noticeRef = React.useRef(null);
  const successRef = React.useRef(null);
  const onToken = React.useCallback((value) => setToken(value), []);
  const onVerificationProblem = React.useCallback((value) => setVerificationProblem(value), []);
  const open = config.interestOpen === true && typeof config.turnstileSiteKey === 'string' && /^0x4[A-Za-z0-9_-]{17,97}$/.test(config.turnstileSiteKey);
  const frozen = busy || !!attempt;
  const legacyAttempt = attempt && Object.hasOwn(attempt, 'parentName');

  React.useEffect(() => { if (notice && !Object.keys(errors).length) noticeRef.current?.focus({ preventScroll: true }); }, [notice]);
  React.useEffect(() => { if (receipt) successRef.current?.focus(); }, [receipt]);

  const changeCamper = (index, name, value) => {
    setCampers((current) => current.map((child, i) => i === index ? { ...child, [name]: value } : child));
    setErrors({});
  };
  const addCamper = () => {
    const index = campers.length;
    setCampers([...campers, { grade: '', interest: '' }]);
    requestAnimationFrame(() => document.getElementById(`camp-interest-grade-${index}`)?.focus());
  };
  const removeCamper = (index) => {
    setCampers(campers.filter((child, i) => i !== index));
    setErrors({});
    requestAnimationFrame(() => document.getElementById(`camp-interest-grade-${Math.max(0, index - 1)}`)?.focus());
  };
  const submit = async (event) => {
    event.preventDefault();
    if (busyRef.current || (!open && !attemptRef.current) || duplicate) return;
    const nextErrors = {};
    if (!attemptRef.current) {
      if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(parentEmail.trim()) || parentEmail.trim().length > 254) nextErrors.email = 'Enter a valid parent or guardian email.';
      if (!campInterestValidName(parentFirstName)) nextErrors['first-name'] = 'Enter your first name.';
      if (!campInterestValidName(parentLastName)) nextErrors['last-name'] = 'Enter your last name.';
      if (!campInterestValidPhone(parentPhone)) nextErrors.phone = 'Enter a phone number with 7 to 15 digits, or leave it blank.';
      campers.forEach((child, index) => {
        if (!Number.isInteger(Number(child.grade)) || Number(child.grade) < 5 || Number(child.grade) > 12) nextErrors[`grade-${index}`] = 'Select a grade from 5th through 12th.';
        if (!['strong', 'possible'].includes(child.interest)) nextErrors[`interest-${index}`] = 'Choose an interest level for this child.';
      });
    }
    setErrors(nextErrors);
    if (Object.keys(nextErrors).length) {
      setNotice('Please complete the highlighted fields.');
      requestAnimationFrame(() => document.getElementById(`camp-interest-${Object.keys(nextErrors)[0]}`)?.focus());
      return;
    }
    if (!token && !attemptRef.current) { setNotice('Please complete the verification check before sharing your interest.'); return; }
    let pending = attemptRef.current;
    if (!pending) {
      try {
        pending = { parentEmail: parentEmail.trim().toLowerCase(), parentFirstName: parentFirstName.trim(), parentLastName: parentLastName.trim(), parentPhone: parentPhone.trim(), campers: campers.map((child) => ({ grade: Number(child.grade), interest: child.interest })), updatesOptIn, submissionKey: campInterestUUID() };
        window.sessionStorage.setItem(storageKey, JSON.stringify(pending));
      } catch { setNotice('Your browser could not save this response for a safe retry. Allow browser storage, then try again.'); return; }
      attemptRef.current = pending;
      setAttempt(pending);
    }
    busyRef.current = true;
    setBusy(true);
    setNotice('');
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 30000);
    try {
      const response = await fetch('/api/camp-interest', { method: 'POST', credentials: 'same-origin', cache: 'no-store', signal: controller.signal, headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify({ ...pending, turnstileToken: token }) });
      const result = await response.json().catch(() => ({}));
      if (response.ok && result.success && result.receipt) {
        try { window.sessionStorage.removeItem(storageKey); } catch { /* Receipt still confirms success. */ }
        setReceipt({ ...result.receipt, updatesOptIn: pending.updatesOptIn });
        attemptRef.current = null;
        setAttempt(null);
        return;
      }
      if (response.status === 409) {
        setDuplicate(true);
        setNotice('This saved response was already received with different answers. Contact the chapter for help confirming your response.');
      } else if (response.status === 400) {
        try { window.sessionStorage.removeItem(storageKey); } catch { /* In-memory correction remains available. */ }
        attemptRef.current = null;
        setAttempt(null);
        setNotice(result.message || 'Please check your answers and verification, then try again.');
      } else {
        setNotice('We couldn’t confirm whether your response was received. Your answers are saved here; try again to check safely without counting your family twice.');
      }
    } catch {
      setNotice('We couldn’t confirm whether your response was received. Your answers are saved here; try again to check safely without counting your family twice.');
    } finally {
      clearTimeout(timeout);
      busyRef.current = false;
      setBusy(false);
      setToken('');
      setVerificationVersion((value) => value + 1);
    }
  };

  const startUpdate = () => {
    attemptRef.current = null;
    setAttempt(null);
    setReceipt(null);
    setDuplicate(false);
    setErrors({});
    setNotice('');
    setToken('');
    setVerificationVersion((value) => value + 1);
    requestAnimationFrame(() => document.getElementById('camp-interest-email')?.focus());
  };

  const fieldError = (key) => errors[key] ? <span className="wk-camp-interest-field-error" id={`camp-interest-error-${key}`}>{errors[key]}</span> : null;
  return <section id="camp-interest" className="wk-camp-interest" aria-labelledby="camp-interest-heading">
    <div className="wk-container wk-camp-interest-inner">
      {!receipt && <div className="wk-camp-interest-intro">
        <p className="wk-camp-kicker">Help us plan</p>
        <h2 id="camp-interest-heading" tabIndex={-1}>Camp interest &amp; updates</h2>
        <p>Tell us how many children might attend FC summer camp in {year}, and choose whether to receive camp updates by email.</p>
        <p>For children who will have finished 5th through 12th grade by summer {year}. Dates, location, and cost are still being determined. Interest only—no registration or commitment.</p>
      </div>}
      {receipt ? <div className="wk-camp-interest-card wk-camp-interest-success" role="status">
        <h3 id="camp-interest-heading" ref={successRef} tabIndex={-1}>Thanks for expressing interest!</h3>
        <p>This is not a registration.</p>
        {receipt.updatesOptIn && <p>You’re signed up for camp updates.</p>}
        <p>Need to make changes? Submit again using the same email.</p>
        {open && <button type="button" className="wk-camp-interest-secondary" onClick={startUpdate}>Update my response</button>}
        {!open && <p>The survey is now closed.</p>}
        <a href={`mailto:${window.WKYHB.org.email}`}>Contact us with questions</a>
      </div> : !open && !attempt ? <div className="wk-camp-interest-card">
        <h3>The interest survey is not accepting responses yet.</h3>
        <p>Check back for updates.</p>
        <button type="button" className="wk-camp-button wk-camp-button-crimson" disabled>Share my family’s interest</button>
      </div> : <form className="wk-camp-interest-card" onSubmit={submit} noValidate aria-busy={busy}>
        {!open && attempt && <p>The survey is now closed. You can still confirm the response already saved in this browser.</p>}
        {notice && <div className="wk-camp-interest-notice" role="alert" ref={noticeRef} tabIndex={-1}>
          <p>{notice}</p>{duplicate && <a href={`mailto:${window.WKYHB.org.email}`}>Contact the chapter</a>}
        </div>}
        <fieldset disabled={frozen} className="wk-camp-interest-fields">
          <legend className="wk-camp-interest-sr">Your family’s interest</legend>
          <div className="wk-camp-interest-field">
            <label htmlFor="camp-interest-email">Parent/guardian email</label>
            <input id="camp-interest-email" type="email" autoComplete="email" required maxLength={254} value={parentEmail} onChange={(event) => { setParentEmail(event.target.value); setErrors({}); }} aria-invalid={!!errors.email} aria-describedby={`camp-interest-email-help${errors.email ? ' camp-interest-error-email' : ''}`} />
            <p className="wk-camp-interest-help" id="camp-interest-email-help">Use the same email if you resubmit. Include all children in your updated response. Contact details are for chapter leadership only.</p>
            {fieldError('email')}
          </div>
          {legacyAttempt ? <div className="wk-camp-interest-field">
            <label htmlFor="camp-interest-saved-name">Parent/guardian name (saved response)</label>
            <input id="camp-interest-saved-name" type="text" readOnly value={attempt.parentName} />
          </div> : <React.Fragment>
            <div className="wk-camp-interest-field">
              <label htmlFor="camp-interest-first-name">Parent/guardian first name</label>
              <input id="camp-interest-first-name" type="text" autoComplete="given-name" required maxLength={60} value={parentFirstName} onChange={(event) => { setParentFirstName(event.target.value); setErrors({}); }} aria-invalid={!!errors['first-name']} aria-describedby={errors['first-name'] ? 'camp-interest-error-first-name' : undefined} />
              {fieldError('first-name')}
            </div>
            <div className="wk-camp-interest-field">
              <label htmlFor="camp-interest-last-name">Parent/guardian last name</label>
              <input id="camp-interest-last-name" type="text" autoComplete="family-name" required maxLength={60} value={parentLastName} onChange={(event) => { setParentLastName(event.target.value); setErrors({}); }} aria-invalid={!!errors['last-name']} aria-describedby={errors['last-name'] ? 'camp-interest-error-last-name' : undefined} />
              {fieldError('last-name')}
            </div>
          </React.Fragment>}
          {!legacyAttempt && <div className="wk-camp-interest-field">
            <label htmlFor="camp-interest-phone">Parent/guardian phone (optional)</label>
            <input id="camp-interest-phone" type="tel" autoComplete="tel" maxLength={40} value={parentPhone} onChange={(event) => { setParentPhone(event.target.value); setErrors({}); }} aria-invalid={!!errors.phone} aria-describedby={errors.phone ? 'camp-interest-error-phone' : undefined} />
            {fieldError('phone')}
          </div>}
          <div className="wk-camp-interest-children">
            {campers.map((child, index) => <fieldset className="wk-camp-interest-child" key={index}>
              <legend>Potential camper {index + 1}</legend>
              <div className="wk-camp-interest-child-fields">
                <div className="wk-camp-interest-field">
                  <label htmlFor={`camp-interest-grade-${index}`}>Grade completed by summer {year}</label>
                  <select id={`camp-interest-grade-${index}`} required value={child.grade} onChange={(event) => changeCamper(index, 'grade', event.target.value)} aria-invalid={!!errors[`grade-${index}`]} aria-describedby={errors[`grade-${index}`] ? `camp-interest-error-grade-${index}` : undefined}>
                    <option value="">Choose a grade</option>
                    {[5, 6, 7, 8, 9, 10, 11, 12].map((grade) => <option key={grade} value={grade}>{grade}th grade</option>)}
                  </select>{fieldError(`grade-${index}`)}
                </div>
                <div className="wk-camp-interest-field">
                  <label htmlFor={`camp-interest-interest-${index}`}>Interest level</label>
                  <select id={`camp-interest-interest-${index}`} required value={child.interest} onChange={(event) => changeCamper(index, 'interest', event.target.value)} aria-invalid={!!errors[`interest-${index}`]} aria-describedby={errors[`interest-${index}`] ? `camp-interest-error-interest-${index}` : undefined}>
                    <option value="">Choose an interest level</option>
                    <option value="strong">Strong interest—if the details work for our family</option>
                    <option value="possible">Possible interest—we need more information</option>
                  </select>{fieldError(`interest-${index}`)}
                </div>
              </div>
              {campers.length > 1 && <button type="button" className="wk-camp-interest-text-button" onClick={() => removeCamper(index)} aria-label={`Remove potential camper ${index + 1}`}>Remove this child</button>}
            </fieldset>)}
          </div>
          <button className="wk-camp-interest-secondary" type="button" onClick={addCamper} disabled={campers.length >= campInterestMaxCampers}>Add another child</button>
          <label className="wk-camp-interest-checkbox"><input type="checkbox" checked={updatesOptIn} onChange={(event) => setUpdatesOptIn(event.target.checked)} /><span>Email me camp updates</span></label>
        </fieldset>
        {!duplicate && <React.Fragment>
          {open && <CampInterestTurnstile key={verificationVersion} siteKey={config.turnstileSiteKey} onToken={onToken} onProblem={onVerificationProblem} />}
          {verificationProblem && <p className="wk-camp-interest-field-error" role="alert">{verificationProblem}</p>}
          <p className="wk-camp-interest-help">Submitting shares your family’s interest only. It does not reserve a place.</p>
          <button className="wk-camp-button wk-camp-button-crimson" type="submit" disabled={busy}>{busy ? 'Sharing your interest…' : attempt ? 'Try again' : updatesOptIn ? 'Share interest & sign up for updates' : 'Share my family’s interest'}</button>
        </React.Fragment>}
      </form>}
    </div>
  </section>;
}

window.CampInterestForm = CampInterestForm;
