// Partner invite modal + invite landing page.
//
// SharePartnerModal — owner enters partner email, we insert a list_invites
// row, send a magic-link, and show "✓ Invite sent" feedback.
//
// InviteLandingPage — when a user clicks a magic link with #invite=TOKEN,
// the App routes here. If they're signed in, we call claim_list_invite and
// drop them into the list. If they're signed out, we capture the token and
// trigger the auth modal so they can sign in / sign up.

const { useState: _is, useEffect: _ie, useRef: _ir } = React;

function SharePartnerModal({ open, list, onClose, onInvited }) {
  const [email, setEmail] = _is('');
  const [busy, setBusy] = _is(false);
  const [sent, setSent] = _is(false);
  const [err, setErr] = _is('');
  const [collaborators, setCollaborators] = _is([]);
  const [shareUrl, setShareUrl] = _is('');
  const [copied, setCopied] = _is(false);
  // Shipping address — owner can fill these in and toggle visibility to buyers.
  const [addrName, setAddrName] = _is('');
  const [addrLine1, setAddrLine1] = _is('');
  const [addrLine2, setAddrLine2] = _is('');
  const [addrCity, setAddrCity] = _is('');
  const [addrState, setAddrState] = _is('');
  const [addrPostcode, setAddrPostcode] = _is('');
  const [addrCountry, setAddrCountry] = _is('');
  const [addrInstructions, setAddrInstructions] = _is('');
  const [addrPublic, setAddrPublic] = _is(false);
  const [addrOpen, setAddrOpen] = _is(false);
  const [addrSaving, setAddrSaving] = _is(false);
  const [addrSaved, setAddrSaved] = _is(false);
  const inputRef = _ir(null);

  // Fetch (or create) the public share token + load any saved shipping
  // address when the modal opens.
  _ie(() => {
    if (!open || !list || !window.MR || !window.MR.lists) return;
    let cancelled = false;
    (async () => {
      const res = await window.MR.lists.ensureShareToken(list.id);
      if (cancelled) return;
      if (res.ok) {
        const base = window.location.origin + '/?token=' + encodeURIComponent(res.token) + '#registry';
        setShareUrl(base);
      }
      // Hydrate the address fields from the list row
      const a = list.shipping_address || {};
      setAddrName(a.name || '');
      setAddrLine1(a.line1 || '');
      setAddrLine2(a.line2 || '');
      setAddrCity(a.city || '');
      setAddrState(a.state || '');
      setAddrPostcode(a.postcode || '');
      setAddrCountry(a.country || '');
      setAddrInstructions(a.instructions || '');
      setAddrPublic(!!list.shipping_address_public);
      setAddrSaved(false);
    })();
    return () => { cancelled = true; };
  }, [open, list?.id]);

  const handleSaveAddress = async () => {
    if (!list || !window.MR || !window.MR.lists) return;
    setAddrSaving(true);
    const addr = {
      name:         addrName.trim(),
      line1:        addrLine1.trim(),
      line2:        addrLine2.trim(),
      city:         addrCity.trim(),
      state:        addrState.trim(),
      postcode:     addrPostcode.trim(),
      country:      addrCountry.trim(),
      instructions: addrInstructions.trim(),
    };
    // Strip empty fields so the jsonb stays tidy
    Object.keys(addr).forEach(k => { if (!addr[k]) delete addr[k]; });
    const hasAny = Object.keys(addr).length > 0;
    const res = await window.MR.lists.updateShippingAddress(list.id, hasAny ? addr : null, addrPublic);
    setAddrSaving(false);
    if (res.ok) {
      setAddrSaved(true);
      setTimeout(() => setAddrSaved(false), 2000);
      // Mutate the in-memory list so subsequent opens see the new value
      if (list) {
        list.shipping_address = hasAny ? addr : null;
        list.shipping_address_public = addrPublic;
      }
    }
  };

  const handleCopy = async () => {
    if (!shareUrl) return;
    try {
      await navigator.clipboard.writeText(shareUrl);
      setCopied(true);
      setTimeout(() => setCopied(false), 1800);
    } catch (e) {
      // Fallback — select text manually
    }
  };

  _ie(() => {
    if (open) {
      setEmail(''); setBusy(false); setSent(false); setErr('');
      setTimeout(() => inputRef.current && inputRef.current.focus(), 60);
      if (list && window.MR && window.MR.lists) {
        window.MR.lists.fetchCollaborators(list.id).then(res => {
          if (res.ok) setCollaborators(res.collaborators);
        });
      }
    }
  }, [open, list?.id]);

  if (!open || !list) return null;

  const submit = async (e) => {
    e.preventDefault();
    if (!email.trim()) return;
    setBusy(true);
    setErr('');
    try {
      const res = await window.MR.lists.inviteCollaborator(list.id, email.trim());
      if (!res.ok) {
        if (res.reason === 'bad-email')      setErr('That doesn\'t look like a valid email — double-check?');
        else if (res.reason === 'send-link-failed' && /rate limit/i.test(res.error?.message || '')) {
          setErr('Email rate limit reached. Wait 60 seconds and try again, or set up custom SMTP for unlimited sends.');
        } else {
          setErr(res.error?.message || 'Could not send the invite. Try again?');
        }
      } else {
        setSent(true);
        // Refresh collaborators in case we already had them
        const refr = await window.MR.lists.fetchCollaborators(list.id);
        if (refr.ok) setCollaborators(refr.collaborators);
        if (typeof onInvited === 'function') onInvited(email.trim());
      }
    } catch (e) {
      setErr(e.message || 'Network problem — try again?');
    } finally {
      setBusy(false);
    }
  };

  return (
    <>
      <div className="share-scrim is-open" onClick={onClose} />
      <div className="share-modal is-open share-partner-modal">
        {!sent ? (
          <form onSubmit={submit}>
            <div className="share-partner-eyebrow">Co-build this list</div>
            <h3 className="share-partner-h">
              Invite your <em><Whimsy text="partner" /></em>
            </h3>
            <p className="share-partner-sub">
              They'll get a magic-link email — clicking it signs them in and adds them as a
              collaborator on <strong>{list.name}</strong>. Both of you can add, edit, and
              tick items off together.
            </p>

            <label className="auth-field" style={{ marginTop: 8 }}>
              <span className="auth-lbl">Partner's email</span>
              <input
                ref={inputRef}
                type="email"
                required
                inputMode="email"
                autoComplete="email"
                placeholder="partner@email.com"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                className="auth-input"
              />
            </label>

            {err && <div className="auth-err">{err}</div>}

            <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 18 }}>
              <button type="button" className="btn btn-ghost" onClick={onClose} style={{ width: 'auto', padding: '10px 16px' }}>
                <span>Cancel</span>
              </button>
              <button type="submit" className="btn" disabled={!email.trim() || busy} style={{ width: 'auto', padding: '10px 18px' }}>
                <span className="btn-row">{busy ? 'Sending…' : 'Send invite'}</span>
                <span className="arrow">→</span>
              </button>
            </div>

            {shareUrl && (
              <div className="share-partner-existing">
                <div className="share-partner-existing-lbl">Public registry link</div>
                <div className="share-link-row">
                  <input
                    type="text"
                    readOnly
                    className="share-link-input"
                    value={shareUrl}
                    onFocus={(e) => e.target.select()}
                  />
                  <button type="button" className="btn" onClick={handleCopy} style={{ width: 'auto', padding: '8px 14px' }}>
                    {copied ? '✓ Copied' : 'Copy'}
                  </button>
                </div>
                <p className="share-link-help">
                  Anyone with this link can see the items you've flagged "in registry", claim them, and leave a note. No account needed.
                </p>
              </div>
            )}

            {/* Shipping address — collapsible */}
            <div className="share-partner-existing">
              <button
                type="button"
                className="share-addr-toggle"
                onClick={() => setAddrOpen(o => !o)}
                aria-expanded={addrOpen}
              >
                <span>Shipping address {addrPublic && '· visible to buyers'}</span>
                <span>{addrOpen ? '▴' : '▾'}</span>
              </button>
              {addrOpen && (
                <div className="share-addr-form">
                  <p className="share-link-help" style={{ marginTop: 0, marginBottom: 12 }}>
                    Where should buyers ship gifts? Toggle "Show to buyers" below to reveal this on the registry page after they claim something.
                  </p>
                  <div className="share-addr-grid">
                    <input className="auth-input" placeholder="Recipient name" value={addrName} onChange={(e) => setAddrName(e.target.value)} />
                    <input className="auth-input" placeholder="Address line 1" value={addrLine1} onChange={(e) => setAddrLine1(e.target.value)} />
                    <input className="auth-input" placeholder="Apartment / unit (optional)" value={addrLine2} onChange={(e) => setAddrLine2(e.target.value)} />
                    <input className="auth-input" placeholder="City" value={addrCity} onChange={(e) => setAddrCity(e.target.value)} />
                    <input className="auth-input" placeholder="State / region" value={addrState} onChange={(e) => setAddrState(e.target.value)} />
                    <input className="auth-input" placeholder="Postcode" value={addrPostcode} onChange={(e) => setAddrPostcode(e.target.value)} />
                    <input className="auth-input" placeholder="Country" value={addrCountry} onChange={(e) => setAddrCountry(e.target.value)} />
                    <input className="auth-input" placeholder="Delivery notes (optional)" value={addrInstructions} onChange={(e) => setAddrInstructions(e.target.value)} />
                  </div>
                  <label className="auth-checkrow" style={{ marginTop: 12 }}>
                    <input type="checkbox" checked={addrPublic} onChange={(e) => setAddrPublic(e.target.checked)} />
                    <span><strong>Show address to buyers</strong> — appears on the registry page after they claim an item.</span>
                  </label>
                  <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end', marginTop: 12 }}>
                    <button type="button" className="btn" onClick={handleSaveAddress} disabled={addrSaving} style={{ width: 'auto', padding: '8px 16px' }}>
                      <span>{addrSaving ? 'Saving…' : addrSaved ? '✓ Saved' : 'Save address'}</span>
                    </button>
                  </div>
                </div>
              )}
            </div>

            {collaborators.length > 0 && (
              <div className="share-partner-existing">
                <div className="share-partner-existing-lbl">Already on this list</div>
                <ul className="share-partner-existing-list">
                  {collaborators.map((c, i) => (
                    <li key={i} className="share-partner-existing-row">
                      <span className="share-partner-existing-email">{c.invited_email || c.user_id?.slice(0, 8)}</span>
                      <span className="share-partner-existing-role">
                        {c.accepted_at ? c.role : 'pending'}
                      </span>
                    </li>
                  ))}
                </ul>
              </div>
            )}
          </form>
        ) : (
          <div className="share-partner-sent">
            <div className="auth-sent-icon" aria-hidden="true">
              <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
                <path d="M3 7 l9 7 l9 -7 v12 H3z"/>
                <path d="M3 7 l9 7 l9 -7 V5 H3 z"/>
              </svg>
            </div>
            <h3 className="share-partner-h" style={{ textAlign: 'center' }}>Invite sent</h3>
            <p className="share-partner-sub" style={{ textAlign: 'center' }}>
              We sent a magic link to <strong>{email}</strong>. Once they open it, they'll appear
              on this list and you'll both be able to edit together.
            </p>
            <div style={{ display: 'flex', justifyContent: 'center', marginTop: 18 }}>
              <button type="button" className="btn" onClick={onClose} style={{ width: 'auto', padding: '10px 20px' }}>
                <span>Done</span>
              </button>
            </div>
          </div>
        )}
      </div>
    </>
  );
}

// Landing page for /#invite=TOKEN links. Two states:
//   - Signed in: claim the invite, navigate to the list.
//   - Signed out: stash the token in sessionStorage, open the auth modal,
//     then claim on sign-in.
function InviteLandingPage({ token, isSignedIn, onSignIn, onClaimed }) {
  const [state, setState] = _is('loading');   // 'loading' | 'pending' | 'claimed' | 'error'
  const [invite, setInvite] = _is(null);
  const [err, setErr] = _is('');

  _ie(() => {
    if (!token) { setState('error'); setErr('No invite token in this link.'); return; }
    let cancelled = false;
    (async () => {
      const res = await window.MR.lists.fetchInvite(token);
      if (cancelled) return;
      if (!res.ok) { setState('error'); setErr('That invite link is invalid or expired.'); return; }
      if (res.invite.accepted_at) { setState('error'); setErr('That invite has already been claimed.'); return; }
      setInvite(res.invite);

      if (isSignedIn) {
        const claim = await window.MR.lists.claimInvite(token);
        if (cancelled) return;
        if (claim.ok) {
          setState('claimed');
          if (typeof onClaimed === 'function') onClaimed(claim.claimed.list_id);
        } else {
          setState('error');
          setErr(claim.error?.message || 'Could not claim the invite. Try again?');
        }
      } else {
        setState('pending');
        try { sessionStorage.setItem('mr-pending-invite', token); } catch {}
      }
    })();
    return () => { cancelled = true; };
  }, [token, isSignedIn]);

  const listName = invite && invite.user_lists ? invite.user_lists.name : 'a list';

  return (
    <main className="invite-landing">
      <div className="invite-landing-card">
        {state === 'loading' && (
          <>
            <div className="invite-landing-eyebrow">Reading invite…</div>
            <h1 className="invite-landing-h">One sec</h1>
          </>
        )}
        {state === 'pending' && (
          <>
            <div className="invite-landing-eyebrow">You're invited</div>
            <h1 className="invite-landing-h">Join <em><Whimsy text={listName} /></em></h1>
            <p className="invite-landing-sub">
              Sign in or create an account to join this shared list. We'll attach the invite as soon as you're signed in.
            </p>
            <button className="btn" onClick={onSignIn} style={{ width: 'auto', padding: '12px 22px' }}>
              <span className="btn-row">Sign in to join</span>
              <span className="arrow">→</span>
            </button>
          </>
        )}
        {state === 'claimed' && (
          <>
            <div className="invite-landing-eyebrow">You're in</div>
            <h1 className="invite-landing-h">Welcome to <em><Whimsy text={listName} /></em></h1>
            <p className="invite-landing-sub">Opening the list…</p>
          </>
        )}
        {state === 'error' && (
          <>
            <div className="invite-landing-eyebrow">Hmm</div>
            <h1 className="invite-landing-h">Couldn't open this invite</h1>
            <p className="invite-landing-sub">{err}</p>
            <button className="btn btn-ghost" onClick={() => { window.location.hash = ''; window.location.reload(); }} style={{ width: 'auto', padding: '12px 22px' }}>
              <span>Back to MagicRascals</span>
            </button>
          </>
        )}
      </div>
    </main>
  );
}

window.SharePartnerModal = SharePartnerModal;
window.InviteLandingPage = InviteLandingPage;
