// rsvp-form.jsx — inline RSVP form for the public invite page.
// Exposes <RsvpForm /> on the global window so invite.jsx can use it.

const { useEffect, useState, useRef, useMemo } = React;

// ─── tiny fetch helper ────────────────────────────────────────────────
async function apiPost(path, body) {
  const r = await fetch(path, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify(body),
  });
  const data = await r.json().catch(() => ({}));
  return { ok: r.ok, status: r.status, data };
}

// ─── presentational atoms ─────────────────────────────────────────────
function Field({ label, children, hint }) {
  return (
    <div className="field">
      <div className="field-label">{label}</div>
      {children}
      {hint && <div className="field-hint">{hint}</div>}
    </div>
  );
}

function Seg({ value, onChange, name }) {
  return (
    <div className="seg" role="radiogroup" aria-label={name}>
      <label>
        <input
          type="radio"
          name={name}
          checked={value === 1}
          onChange={() => onChange(1)}
        />
        <span>Joining</span>
      </label>
      <label>
        <input
          type="radio"
          name={name}
          checked={value === 0}
          onChange={() => onChange(0)}
        />
        <span>Can't make it</span>
      </label>
    </div>
  );
}

// Dietary options shown in the dropdown. "Other" reveals a free-text input.
const DIETARY_OPTIONS = [
  'No restrictions',
  'Vegetarian',
  'Vegan',
  'Pescatarian',
  'Gluten-free',
  'Dairy-free',
  'Nut allergy',
  'Halal',
  'Kosher',
  'Other',
];

function dietaryToSelection(value) {
  const v = (value || '').trim();
  if (!v) return { choice: '', other: '' };
  if (DIETARY_OPTIONS.includes(v) && v !== 'Other') return { choice: v, other: '' };
  return { choice: 'Other', other: v };
}

// ─── per-attendee block ───────────────────────────────────────────────
function AttendeeBlock({ a, onChange, locked }) {
  const editable = !locked;
  const { choice, other } = dietaryToSelection(a.dietary);

  const setDietary = (nextChoice, nextOther) => {
    if (!nextChoice) {
      onChange({ ...a, dietary: '' });
    } else if (nextChoice === 'Other') {
      onChange({ ...a, dietary: nextOther });
    } else {
      onChange({ ...a, dietary: nextChoice });
    }
  };

  return (
    <div className="attendee">
      {a.is_plus_one && a.id == null && editable ? (
        // Unsaved plus-one row: keep the input mounted while the user types so
        // we don't lose focus / drop characters on the first keystroke. The
        // input only goes away once the row is persisted (gets an id) or the
        // form is locked.
        <Field label="Plus-one name">
          <input
            className="input"
            type="text"
            value={a.display_name || ''}
            onChange={(e) => onChange({ ...a, display_name: e.target.value })}
            placeholder="Their name"
            maxLength={100}
          />
        </Field>
      ) : (
        <div className="attendee-name">
          {a.display_name || (a.is_plus_one ? 'Plus-one' : 'Attendee')}
        </div>
      )}

      <Field label={a.is_plus_one ? 'Are they joining?' : 'Are you joining?'}>
        <Seg
          name={`att-${a.id || 'new'}`}
          value={a.attending === 1 ? 1 : a.attending === 0 ? 0 : null}
          onChange={editable ? (v) => onChange({ ...a, attending: v }) : () => {}}
        />
      </Field>

      <Field label="Dietary requirements">
        <select
          className="input select"
          value={choice}
          disabled={!editable}
          onChange={(e) => setDietary(e.target.value, other)}
        >
          <option value="">Select an option…</option>
          {DIETARY_OPTIONS.map(opt => (
            <option key={opt} value={opt}>{opt}</option>
          ))}
        </select>
      </Field>

      {choice === 'Other' && (
        <Field label="Please specify">
          <input
            className="input"
            type="text"
            value={other}
            disabled={!editable}
            onChange={(e) => setDietary('Other', e.target.value)}
            placeholder="Tell us what to know"
          />
        </Field>
      )}

      <Field label="A note for us — optional">
        <textarea
          className="textarea"
          rows={2}
          value={a.message || ''}
          disabled={!editable}
          onChange={(e) => onChange({ ...a, message: e.target.value })}
          placeholder=""
        />
      </Field>
    </div>
  );
}

// ─── the form itself ──────────────────────────────────────────────────
function RsvpForm({ copy }) {
  const initialToken = useMemo(() => {
    const p = new URLSearchParams(window.location.search);
    return (p.get('t') || '').trim().slice(0, 10);
  }, []);

  // Always start idle so guests landing via /?t=… see the whole page first.
  const [phase, setPhase] = useState('idle');
  const [token, setToken] = useState(initialToken);
  const [surname, setSurname] = useState('');
  const [error, setError] = useState('');
  const [guest, setGuest] = useState(null);
  const [attendees, setAttendees] = useState([]);
  const [plusOne, setPlusOne] = useState(null); // virtual plus-one row
  const [email, setEmail] = useState('');
  const [editReason, setEditReason] = useState('');
  const surnameInputRef = useRef(null);
  const tokenFromUrl = !!initialToken;

  // Focus the surname field when the guest opens the form (but never on page load).
  useEffect(() => {
    if (phase === 'enter-token' && tokenFromUrl && surnameInputRef.current) {
      surnameInputRef.current.focus({ preventScroll: true });
    }
  }, [phase, tokenFromUrl]);

  // ── transitions
  const startReply = () => { setError(''); setPhase('enter-token'); };
  const cancelReply = () => { setError(''); setPhase('idle'); };

  const verify = async (e) => {
    e?.preventDefault?.();
    if (!token || token.length !== 10) { setError('Check the token — it should be 10 characters.'); return; }
    if (!surname.trim()) { setError("We'll need the surname too."); return; }
    setError(''); setPhase('verifying');
    const { ok, status, data } = await apiPost('/api/verify-surname', { token: token.trim(), surname: surname.trim() });
    if (status === 429) { setError("Too many tries — give it a few minutes and try again."); setPhase('enter-token'); return; }
    if (!ok || !data.ok) {
      if (status === 404 || data.error === 'token_not_found') setError("That token doesn't match anything we sent out. Double-check the link.");
      else if (data.error === 'surname_mismatch') setError("That surname doesn't match our records. Try the one we'd have written down.");
      else setError("Something went sideways. Try again in a moment.");
      setPhase('enter-token');
      return;
    }
    // success
    setGuest(data.guest);
    setAttendees(data.attendees || []);
    setEmail(data.guest?.email || '');
    // virtual plus-one if allowed and no real plus-one row yet
    if (data.guest.plus_one && !(data.attendees || []).some(a => a.is_plus_one)) {
      setPlusOne({ id: null, is_plus_one: 1, display_name: '', attending: null, dietary: '', song_request: '', message: '', needs_transport: 0 });
    } else {
      setPlusOne(null);
    }
    // strip ?t= from URL for screen-share safety
    if (window.location.search.includes('t=')) {
      window.history.replaceState(null, '', window.location.pathname);
    }
    setPhase(data.guest.locked ? 'review' : 'unlocked');
  };

  const updateAttendee = (idx, next) => {
    setAttendees(prev => prev.map((a, i) => i === idx ? next : a));
  };

  const updatePlusOne = (next) => setPlusOne(next);

  const submit = async () => {
    const trimmedEmail = email.trim();
    if (!trimmedEmail) {
      setError("We'll need a contact email so we can confirm.");
      return;
    }
    if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmedEmail)) {
      setError("That email doesn't look right — double-check it.");
      return;
    }
    if (!attendees.length && !(plusOne && plusOne.display_name && plusOne.attending != null)) {
      setError("Tell us at least one person's answer.");
      return;
    }
    setError(''); setPhase('submitting');
    const payload = {
      token: token.trim(),
      surname: surname.trim(),
      email: trimmedEmail,
      attendees: [...attendees],
    };
    if (plusOne && (plusOne.display_name || plusOne.attending != null)) {
      payload.attendees.push(plusOne);
    }
    const { ok, status, data } = await apiPost('/api/rsvp', payload);
    if (!ok || !data.ok) {
      if (status === 409 || data.error === 'locked') setError("Your RSVP is locked. Request an edit and we'll unlock it for you.");
      else if (data.error === 'email_required') setError("We'll need a contact email so we can confirm.");
      else if (data.error === 'email_invalid') setError("That email doesn't look right — double-check it.");
      else setError("Something went sideways. Try again in a moment.");
      setPhase('unlocked');
      return;
    }
    setGuest(data.guest);
    setAttendees(data.attendees || []);
    setPlusOne(null);
    setPhase('submitted');
  };

  const requestEdit = async () => {
    setError(''); setPhase('submitting');
    const { ok, data } = await apiPost('/api/rsvp/edit-request', {
      token: token.trim(),
      surname: surname.trim(),
      reason: editReason || undefined,
    });
    if (!ok || !data.ok) {
      setError("Couldn't send the request. Try again in a moment.");
      setPhase('review');
      return;
    }
    setPhase('edit-requested');
  };

  // ── render by phase ──────────────────────────────────────────────────
  if (phase === 'idle') {
    return (
      <button type="button" className="btn" onClick={startReply}>
        {copy.rsvpBtn}
        <span className="arrow"></span>
      </button>
    );
  }

  if (phase === 'enter-token' || phase === 'verifying') {
    return (
      <form className="rsvp-gate" onSubmit={verify}>
        {!tokenFromUrl && (
          <Field label="Your token">
            <input
              className="input mono"
              type="text"
              inputMode="text"
              autoComplete="off"
              spellCheck="false"
              value={token}
              onChange={(e) => setToken(e.target.value.trim().toLowerCase().slice(0, 10))}
              placeholder="ten characters"
              maxLength={10}
              disabled={phase === 'verifying'}
            />
          </Field>
        )}
        <Field label="Surname">
          <input
            ref={surnameInputRef}
            className="input"
            type="text"
            autoComplete="family-name"
            value={surname}
            onChange={(e) => setSurname(e.target.value)}
            placeholder="As we'd have written it down"
            disabled={phase === 'verifying'}
          />
        </Field>
        {error && <div className="form-error">{error}</div>}
        <div className="form-row">
          <button type="submit" className="btn" disabled={phase === 'verifying'}>
            {phase === 'verifying' ? 'Checking…' : 'Continue'}
            <span className="arrow"></span>
          </button>
          <button type="button" className="btn ghost" onClick={cancelReply} disabled={phase === 'verifying'}>
            Cancel
          </button>
        </div>
      </form>
    );
  }

  if (phase === 'unlocked' || phase === 'submitting') {
    const editable = phase === 'unlocked';
    return (
      <div className="rsvp-form">
        <div className="rsvp-hello">
          <span className="rsvp-hello-eyebrow">Hello, {guest.household_name}</span>
        </div>
        <div className="attendee" style={{ borderTop: 0, paddingTop: 0 }}>
          <Field label="Contact email — required" hint="So we can send your confirmation and any updates.">
            <input
              className="input"
              type="email"
              inputMode="email"
              autoComplete="email"
              required
              value={email}
              disabled={!editable}
              onChange={(e) => setEmail(e.target.value)}
              placeholder="you@example.com"
            />
          </Field>
        </div>
        {attendees.map((a, i) => (
          <AttendeeBlock key={a.id || `att-${i}`} a={a} onChange={(next) => updateAttendee(i, next)} locked={!editable} />
        ))}
        {plusOne && (
          <AttendeeBlock a={plusOne} onChange={updatePlusOne} locked={!editable} />
        )}
        {error && <div className="form-error">{error}</div>}
        <div className="form-row">
          <button type="button" className="btn" onClick={submit} disabled={!editable}>
            {phase === 'submitting' ? 'Sending…' : 'Send our reply'}
            <span className="arrow"></span>
          </button>
        </div>
      </div>
    );
  }

  if (phase === 'submitted' || phase === 'review') {
    const yes = attendees.filter(a => a.attending === 1);
    const no = attendees.filter(a => a.attending === 0);
    const confirmTo = guest?.email || email;
    return (
      <div className="rsvp-form">
        <h3 className="rsvp-thanks">
          {phase === 'submitted' ? 'Thank you. We have you down.' : 'You\'ve already replied.'}
        </h3>
        {phase === 'submitted' && confirmTo && (
          <p className="rsvp-confirmation">
            A confirmation has been sent to <em>{confirmTo}</em>.<br/>
            If it doesn't appear in a minute or two, check your spam folder.
          </p>
        )}
        <div className="rsvp-recap">
          {(guest?.email || email) && (
            <div className="recap-row"><span className="recap-k">Contact</span><span className="recap-v">{guest?.email || email}</span></div>
          )}
          <div className="recap-row"><span className="recap-k">Joining</span><span className="recap-v">{yes.length ? yes.map(a => a.display_name).join(', ') : '—'}</span></div>
          <div className="recap-row"><span className="recap-k">Not joining</span><span className="recap-v">{no.length ? no.map(a => a.display_name).join(', ') : '—'}</span></div>
          {attendees.filter(a => a.dietary).map(a => (
            <div className="recap-row" key={`d-${a.id}`}><span className="recap-k">{a.display_name} · dietary</span><span className="recap-v">{a.dietary}</span></div>
          ))}
        </div>
        <Field label="Need to change something? Tell us what.">
          <textarea className="textarea" rows={2} value={editReason} onChange={(e) => setEditReason(e.target.value)} placeholder="optional" />
        </Field>
        {error && <div className="form-error">{error}</div>}
        <button type="button" className="btn ghost" onClick={requestEdit}>
          Request an edit
          <span className="arrow"></span>
        </button>
      </div>
    );
  }

  if (phase === 'edit-requested') {
    return (
      <div className="rsvp-form">
        <h3 className="rsvp-thanks">We'll get back to you shortly.</h3>
        <p className="rsvp-recap-note">We'll review your request and reach out to unlock it.</p>
      </div>
    );
  }

  return null;
}

Object.assign(window, { RsvpForm });
