// Phase 07 · LIVE apology on-ramp.
//
// SYMMETRIC: either party can offer a repair. Either can skip. Neither is required.
//
// Layout (single column, two stacked cards):
//   TOP    · your offer to them — private drafter + coach flags + send
//   BOTTOM · their offer to you — shows status (nothing yet / drafting / received / skipped),
//            plus receiver controls once received: accept · ask for more · decline
//
// Coach runs ONLY when you click 'coach · review'. No auto-fire.
// Only a finished, confirmed offer crosses the firewall. Drafts-in-progress never leak.

(function () {
  const { useState, useEffect, useRef } = React;

  window.LiveApologyPhase = function LiveApologyPhase({ seat, onPhase }) {
    const [, tick] = useState(0);
    useEffect(() => CG.session.onState(() => tick(t => t + 1)), []);

    const shared = CG.session.shared || {};
    const names = (CG.session.getNames && CG.session.getNames()) || {};
    const mp = CG.session.isMultiplayer && CG.session.isMultiplayer();
    const isHost = !mp || seat === 'A';
    const otherSeat = seat === 'A' ? 'B' : 'A';
    const myName = names[seat] || (seat === 'A' ? 'host' : 'guest');
    const otherName = names[otherSeat] || (otherSeat === 'A' ? 'host' : 'guest');

    // Guard: require at least one established truth from phase 05.
    const truths = (shared.truths || []).filter(t => t.confirmedA && t.confirmedB);
    if (truths.length === 0) {
      return (
        <div style={{ padding: 40, maxWidth: 720, margin: '0 auto' }}>
          <div className="tiny">phase 07 · apology on-ramp</div>
          <h1 style={{ marginTop: 6 }}>not ready.</h1>
          <p style={{ color: 'var(--ink-2)', lineHeight: 1.55 }}>
            establish at least one truth together before offering repair.{' '}
            <a onClick={() => onPhase && onPhase('room')} style={{ color: 'var(--blue)', cursor: 'pointer' }}>← back to the room</a>
          </p>
        </div>
      );
    }

    const ap = shared.apology || { A: {}, B: {}, continueA: false, continueB: false };
    const mine = ap[seat] || {};
    const theirs = ap[otherSeat] || {};
    const myContinue = seat === 'A' ? ap.continueA : ap.continueB;
    const theirContinue = seat === 'A' ? ap.continueB : ap.continueA;

    // persistent private draft
    const [draft, setDraft] = useState('');
    useEffect(() => { (async () => {
      const s = await CG.store.get(`apology_draft_${seat}`);
      if (s && typeof s === 'string') setDraft(s);
    })(); }, [seat]);
    useEffect(() => { CG.store.set(`apology_draft_${seat}`, draft); }, [draft, seat]);

    const [review, setReview] = useState(null);
    const [reviewing, setReviewing] = useState(false);
    const [reviewError, setReviewError] = useState('');
    const [confirmOpen, setConfirmOpen] = useState(false);

    const setMine = (partial) => {
      const payload = { apology: { [seat]: partial } };
      if (isHost) {
        const curAp = shared.apology || { A: {}, B: {}, continueA: false, continueB: false };
        CG.session.setShared({ apology: { ...curAp, [seat]: { ...(curAp[seat] || {}), ...partial } } });
      } else {
        CG.peer.send('state:propose', payload);
      }
    };

    const runReview = async () => {
      const t = draft.trim();
      if (!t || reviewing) return;
      setReviewing(true); setReviewError(''); setReview(null);
      try {
        const r = await CG.agents.apologyCoach.review(seat, t);
        setReview(r);
        if (!mine.status) setMine({ status: 'drafting' });
      } catch (e) {
        setReviewError(String(e.message || e));
      } finally { setReviewing(false); }
    };

    const sendOffer = () => {
      const t = draft.trim();
      if (!t) return;
      const offer = { text: t, ts: Date.now() };
      setMine({ status: 'offered', offer });
      setConfirmOpen(false);
    };

    const skipOffer = () => {
      setMine({ status: 'skipped' });
    };

    // Receiver controls — I am responding to their offer.
    const respond = (kind, note = '') => {
      const payload = { apology: { [otherSeat]: { response: { kind, note, ts: Date.now() } } } };
      if (isHost) {
        const curAp = shared.apology || { A: {}, B: {}, continueA: false, continueB: false };
        CG.session.setShared({
          apology: { ...curAp, [otherSeat]: { ...(curAp[otherSeat] || {}), response: payload.apology[otherSeat].response } },
        });
      } else {
        CG.peer.send('state:propose', payload);
      }
    };

    const setContinue = (v) => {
      if (seat === 'A') {
        if (isHost) CG.session.setShared({ apology: { ...ap, continueA: !!v } });
      } else {
        if (isHost) CG.session.setShared({ apology: { ...ap, continueB: !!v } });
        else CG.peer.send('state:propose', { apology: { continueB: !!v } });
      }
    };

    const bothDecided = (status) => ['offered', 'skipped', 'accepted', 'more-asked', 'declined'].includes(status);
    const readyToAdvance =
      // each side has made a decision: either offered (and gotten a receiver response) or skipped
      (mine.status === 'skipped' || (mine.status === 'offered' && theirs.response))
      &&
      (theirs.status === 'skipped' || (theirs.status === 'offered' && mine.response));

    // Presence
    useEffect(() => {
      let activity = 'considering offer';
      if (readyToAdvance) activity = 'ready to advance';
      else if (mine.status === 'offered' && !theirs.response) activity = `waiting for ${otherName}`;
      else if (mine.status === 'skipped') activity = 'skipped';
      else if (reviewing) activity = 'coach reviewing';
      else if ((draft || '').trim().length > 0) activity = 'drafting offer';
      try { CG.session.setCursor && CG.session.setCursor({ activity }); } catch (e) {}
    }, [mine.status, theirs.response, readyToAdvance, reviewing, draft, otherName]);

    return (
      <div style={{ height: '100vh', display: 'flex', flexDirection: 'column', overflow: 'hidden', background: 'var(--paper-2)' }}>
        {/* header */}
        <div style={{ padding: '18px 28px 12px', borderBottom: '1px solid var(--rule)' }}>
          <div className="tiny">phase 07 · apology on-ramp · live</div>
          <h2 style={{ marginTop: 4, marginBottom: 2, fontFamily: 'var(--font-prose)', fontWeight: 500, letterSpacing: '-0.015em' }}>
            repair, if there is one to make. nothing is required.
          </h2>
          <div className="mono" style={{ fontSize: '0.75rem', color: 'var(--ink-3)', marginTop: 4 }}>
            either of you may offer, either may skip. the other has to answer.
          </div>
        </div>

        <div style={{ flex: 1, overflow: 'auto', padding: '22px 28px', maxWidth: 820, margin: '0 auto', width: '100%' }}>
          {/* YOUR OFFER */}
          <OfferDrafter
            seat={seat}
            mine={mine}
            otherName={otherName}
            draft={draft}
            setDraft={setDraft}
            review={review}
            setReview={setReview}
            reviewing={reviewing}
            reviewError={reviewError}
            onReview={runReview}
            onOpenConfirm={() => setConfirmOpen(true)}
            onSkip={skipOffer}
            onReset={() => { setMine({ status: 'drafting' }); setReview(null); }}
          />

          <div style={{ height: 22 }} />

          {/* THEIR OFFER */}
          <IncomingOffer
            seat={seat}
            theirs={theirs}
            otherName={otherName}
            myName={myName}
            onRespond={respond}
          />

          <div style={{ height: 26 }} />

          {/* continue */}
          <div style={{ borderTop: '1px dashed var(--rule)', paddingTop: 18 }}>
            <div className="tiny mb-2">when you're both done here</div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <button
                className="btn btn-sm btn-primary"
                disabled={!readyToAdvance || myContinue}
                onClick={() => setContinue(true)}
              >
                {myContinue ? 'you · ready' : 'i\'m ready · continue'}
              </button>
              <div className="mono" style={{ fontSize: '0.72rem', color: 'var(--ink-3)' }}>
                {myContinue ? '✓ you ' : '· you '}
                {theirContinue ? `· ✓ ${otherName}` : `· ${otherName} pending`}
              </div>
              {myContinue && !theirContinue && (
                <div style={{ marginTop: 8, padding: '7px 9px', background: 'var(--paper-2)', borderLeft: '2px solid var(--ink-3)', fontSize: '0.8rem', color: 'var(--ink-2)', lineHeight: 1.4 }}>
                  waiting on {otherName} to mark ready.
                </div>
              )}
              <div style={{ flex: 1 }} />
              {myContinue && theirContinue && (
                <button className="btn btn-sm btn-primary" onClick={() => onPhase && onPhase('artifact')}>
                  → phase 08 · artifact
                </button>
              )}
            </div>
            {!readyToAdvance && (
              <div className="mono mt-2" style={{ fontSize: '0.7rem', color: 'var(--ink-3)' }}>
                both of you need to either offer (and get a response) or skip before continuing.
              </div>
            )}
          </div>
        </div>

        {confirmOpen && (
          <ConfirmModal
            draft={draft}
            otherName={otherName}
            onCancel={() => setConfirmOpen(false)}
            onConfirm={sendOffer}
          />
        )}
      </div>
    );
  };

  // -------- your offer drafter --------

  function OfferDrafter({ seat, mine, otherName, draft, setDraft, review, setReview, reviewing, reviewError, onReview, onOpenConfirm, onSkip, onReset }) {
    // States: unset → drafting → offered | skipped
    const status = mine.status;

    if (status === 'offered') {
      return (
        <div className="paper-card" style={{ padding: '16px 18px', background: 'var(--paper)', borderLeft: '3px solid var(--green)' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
            <div className="tiny">your offer · sent to {otherName}</div>
            <span className="stamp stamp-green" style={{ fontSize: '0.58rem' }}>offered</span>
          </div>
          <div style={{ fontSize: '0.95rem', lineHeight: 1.6, whiteSpace: 'pre-wrap', fontFamily: 'var(--font-prose)' }}>
            {mine.offer?.text}
          </div>
          <div className="mono mt-2" style={{ fontSize: '0.7rem', color: 'var(--ink-3)' }}>
            waiting for {otherName}'s response. you can't retract.
          </div>
        </div>
      );
    }

    if (status === 'skipped') {
      return (
        <div className="paper-card" style={{ padding: '14px 18px', background: 'var(--paper)', borderLeft: '3px solid var(--ink-3)' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
            <div className="tiny">your offer · skipped</div>
            <button className="btn btn-sm btn-ghost" onClick={onReset} style={{ padding: '2px 6px', fontSize: '0.72rem' }}>
              change my mind
            </button>
          </div>
          <div className="mono mt-1" style={{ fontSize: '0.78rem', color: 'var(--ink-3)', textTransform: 'none', letterSpacing: 0 }}>
            you chose not to offer a repair right now. that's a valid answer.
          </div>
        </div>
      );
    }

    return (
      <div className="paper-card" style={{ padding: '16px 18px', background: 'var(--paper)', borderLeft: '3px solid var(--blue)' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
          <div className="tiny">your offer to {otherName}</div>
          <div style={{ display: 'flex', gap: 6 }}>
            <button className="btn btn-sm btn-ghost" onClick={onSkip} style={{ fontSize: '0.72rem' }}>skip this</button>
          </div>
        </div>
        <div className="mono mb-3" style={{ fontSize: '0.75rem', color: 'var(--ink-3)', textTransform: 'none', letterSpacing: 0, lineHeight: 1.45 }}>
          name the specific thing. own the impact. don't attach a counter-grievance. your coach will flag problems but won't rewrite.
        </div>

        <textarea
          className="paper-input"
          rows={6}
          placeholder={`to ${otherName}…`}
          value={draft}
          onChange={e => setDraft(e.target.value)}
          disabled={reviewing}
          style={{ fontFamily: 'var(--font-prose)', fontSize: '0.95rem', lineHeight: 1.55 }}
        />

        <div style={{ display: 'flex', gap: 8, marginTop: 10, alignItems: 'center' }}>
          <button className="btn btn-sm" disabled={!draft.trim() || reviewing} onClick={onReview}>
            {reviewing ? 'coach reading…' : review ? 'coach · re-review' : 'coach · review'}
          </button>
          <button
            className="btn btn-sm btn-primary"
            disabled={!draft.trim() || reviewing}
            onClick={onOpenConfirm}
          >
            offer this to {otherName} →
          </button>
          <div style={{ flex: 1 }} />
          <span className="mono" style={{ fontSize: '0.7rem', color: 'var(--ink-3)' }}>
            {draft.trim().split(/\s+/).filter(Boolean).length} w
          </span>
        </div>

        {reviewError && (
          <div className="paper-card p-2 mt-3" style={{ background: 'var(--red-soft)', borderLeft: '2px solid var(--red)' }}>
            <div className="micro" style={{ color: 'var(--red)' }}>error</div>
            <div style={{ fontSize: '0.82rem' }}>{reviewError}</div>
          </div>
        )}

        {review && <CoachReview review={review} />}
      </div>
    );
  }

  function CoachReview({ review }) {
    return (
      <div className="fade-up" style={{ marginTop: 14, borderTop: '1px dashed var(--rule)', paddingTop: 12 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 8 }}>
          <div className="tiny">coach · private to you</div>
          <div className="mono" style={{
            fontSize: '0.7rem',
            color: review.ready_to_offer ? 'var(--green)' : 'oklch(52% 0.14 70)',
          }}>
            {review.ready_to_offer ? '✓ looks ready to offer' : '· not there yet'}
          </div>
        </div>

        {review.note && (
          <div style={{ fontSize: '0.88rem', lineHeight: 1.5, marginBottom: 10, color: 'var(--ink-1)' }}>
            {review.note}
          </div>
        )}

        {review.issues.length > 0 && (
          <div style={{ marginBottom: 10 }}>
            <div className="micro mb-2">issues</div>
            {review.issues.map((iss, i) => (
              <div key={i} style={{
                fontSize: '0.8rem',
                padding: '6px 10px',
                borderLeft: `2px solid ${iss.severity === 'high' ? 'var(--red)' : iss.severity === 'med' ? 'oklch(55% 0.18 30)' : 'oklch(55% 0.14 85)'}`,
                background: 'var(--paper-2)',
                marginBottom: 5,
              }}>
                <div className="mono" style={{ fontSize: '0.62rem', color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
                  {iss.kind} · {iss.severity}
                </div>
                <div style={{ fontStyle: 'italic', marginTop: 2 }}>"{iss.quote}"</div>
                <div style={{ color: 'var(--ink-2)', marginTop: 2 }}>{iss.reason}</div>
              </div>
            ))}
          </div>
        )}

        {review.missing.length > 0 && (
          <div style={{ marginBottom: 10 }}>
            <div className="micro mb-2">missing</div>
            <ul style={{ margin: 0, paddingLeft: 18, fontSize: '0.85rem', lineHeight: 1.5 }}>
              {review.missing.map((m, i) => <li key={i}>{m}</li>)}
            </ul>
          </div>
        )}

        {review.suggestions.length > 0 && (
          <div>
            <div className="micro mb-2">directions (not rewrites)</div>
            <ul style={{ margin: 0, paddingLeft: 18, fontSize: '0.85rem', lineHeight: 1.5, color: 'var(--ink-1)' }}>
              {review.suggestions.map((s, i) => <li key={i} style={{ marginBottom: 3 }}>{s}</li>)}
            </ul>
          </div>
        )}
      </div>
    );
  }

  // -------- incoming offer card --------

  function IncomingOffer({ seat, theirs, otherName, myName, onRespond }) {
    const status = theirs.status;
    const offer = theirs.offer;
    const response = theirs.response;

    if (!status || status === 'drafting') {
      return (
        <div className="paper-card" style={{ padding: '14px 18px', background: 'var(--paper)', borderLeft: '3px dashed var(--ink-4)' }}>
          <div className="tiny mb-1">from {otherName}</div>
          <div className="mono" style={{ fontSize: '0.78rem', color: 'var(--ink-3)', textTransform: 'none', letterSpacing: 0 }}>
            {status === 'drafting' ? `${otherName} is drafting something. you'll see it only if they choose to send it.` : `nothing has arrived yet.`}
          </div>
        </div>
      );
    }

    if (status === 'skipped') {
      return (
        <div className="paper-card" style={{ padding: '14px 18px', background: 'var(--paper)', borderLeft: '3px solid var(--ink-3)' }}>
          <div className="tiny mb-1">from {otherName}</div>
          <div className="mono" style={{ fontSize: '0.8rem', color: 'var(--ink-2)', textTransform: 'none', letterSpacing: 0, lineHeight: 1.45 }}>
            {otherName} is not offering a repair at this time.
          </div>
        </div>
      );
    }

    if (status === 'offered') {
      return (
        <div className="paper-card" style={{ padding: '16px 18px', background: 'var(--paper)', borderLeft: '3px solid oklch(62% 0.14 85)' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
            <div className="tiny">from {otherName}</div>
            {!response && <span className="stamp stamp-blue" style={{ fontSize: '0.58rem' }}>your turn</span>}
          </div>
          <div style={{ fontSize: '0.98rem', lineHeight: 1.65, whiteSpace: 'pre-wrap', fontFamily: 'var(--font-prose)', marginBottom: 12 }}>
            {offer?.text}
          </div>

          {!response ? (
            <ResponderControls onRespond={onRespond} otherName={otherName} />
          ) : (
            <div className="paper-card p-2" style={{
              background: 'var(--paper-2)',
              borderLeft: `2px solid ${response.kind === 'accepted' ? 'var(--green)' : response.kind === 'more-asked' ? 'oklch(55% 0.14 85)' : 'var(--ink-3)'}`,
            }}>
              <div className="mono" style={{ fontSize: '0.65rem', color: 'var(--ink-3)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: 3 }}>
                you responded · {response.kind.replace('-', ' ')}
              </div>
              {response.note && <div style={{ fontSize: '0.85rem', lineHeight: 1.5 }}>{response.note}</div>}
            </div>
          )}
        </div>
      );
    }

    return null;
  }

  function ResponderControls({ onRespond, otherName }) {
    const [mode, setMode] = useState(null); // 'more' | 'decline' | null
    const [note, setNote] = useState('');

    if (mode === null) {
      return (
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <button className="btn btn-sm btn-primary" onClick={() => onRespond('accepted')}>accept</button>
          <button className="btn btn-sm" onClick={() => setMode('more')}>ask for more</button>
          <button className="btn btn-sm btn-ghost" onClick={() => setMode('decline')}>decline</button>
        </div>
      );
    }

    const placeholder = mode === 'more'
      ? `what more would help? (optional, visible to ${otherName})`
      : `a one-line reason? (optional, visible to ${otherName})`;

    return (
      <div>
        <textarea
          className="paper-input"
          rows={2}
          placeholder={placeholder}
          value={note}
          onChange={e => setNote(e.target.value)}
          style={{ fontSize: '0.88rem' }}
        />
        <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
          <button
            className="btn btn-sm btn-primary"
            onClick={() => onRespond(mode === 'more' ? 'more-asked' : 'declined', note.trim())}
          >
            {mode === 'more' ? 'send ask' : 'send decline'}
          </button>
          <button className="btn btn-sm btn-ghost" onClick={() => { setMode(null); setNote(''); }}>
            back
          </button>
        </div>
      </div>
    );
  }

  function ConfirmModal({ draft, otherName, onCancel, onConfirm }) {
    return (
      <div style={{
        position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.35)',
        display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 200,
      }} onClick={onCancel}>
        <div
          className="paper-card"
          onClick={e => e.stopPropagation()}
          style={{ maxWidth: 520, width: '90%', padding: 22, background: 'var(--paper)' }}
        >
          <div className="tiny mb-2">send this to {otherName}?</div>
          <div style={{ fontSize: '0.93rem', lineHeight: 1.6, whiteSpace: 'pre-wrap', fontFamily: 'var(--font-prose)', background: 'var(--paper-2)', padding: 12, border: '1px solid var(--rule)', borderRadius: 2, marginBottom: 14 }}>
            {draft}
          </div>
          <div className="mono mb-3" style={{ fontSize: '0.72rem', color: 'var(--ink-3)', textTransform: 'none', letterSpacing: 0, lineHeight: 1.45 }}>
            once you send, you can't retract. {otherName} will decide to accept, ask for more, or decline.
          </div>
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <button className="btn btn-sm btn-ghost" onClick={onCancel}>keep editing</button>
            <button className="btn btn-sm btn-primary" onClick={onConfirm}>send</button>
          </div>
        </div>
      </div>
    );
  }

  console.log('[phases-apology-live] loaded');
})();
