// Pocket Marshal — Marshal's recommendation (verdict) card + reply parser

const PM_VERDICTS = {
  'PASS': { badge: '#1e7a4b', badgeInk: '#ffffff', soft: '#e3f3ea', edge: '#a9d8bd', dark: '#155c38' },
  'PASS WITH CONDITIONS': { badge: '#f5a623', badgeInk: '#0d1f38', soft: '#fdf1d8', edge: '#ecd095', dark: '#8a5f0a' },
  'IMMEDIATE FAIL': { badge: '#8c1c0e', badgeInk: '#fff8f0', soft: '#fae6e1', edge: '#e5b4a8', dark: '#7c1a0d' },
  'ESCALATE': { badge: '#0d1f38', badgeInk: '#fff8f0', soft: '#e9eef6', edge: '#c2cfe2', dark: '#27406b' },
};

function pmNormalizeVerdict(s) {
  const u = String(s || '').toUpperCase();
  if (u.includes('CONDITION')) return 'PASS WITH CONDITIONS';
  if (u.includes('FAIL')) return 'IMMEDIATE FAIL';
  if (u.includes('ESCALAT')) return 'ESCALATE';
  if (u.includes('PASS')) return 'PASS';
  return null;
}

// Parses Marshal's structured determination block. Returns null if reply is conversational.
function pmParseVerdict(reply) {
  if (!/^\s*VERDICT\s*:/m.test(reply)) return null;
  const v = { verdict: null, why: '', codes: [], say: [], next: [], nudge: '', closer: '', intro: '' };
  let lastKey = null, seenVerdict = false;
  const introLines = [];
  for (const rawLine of String(reply).split('\n')) {
    const ln = rawLine.trim();
    if (!ln) continue;
    const m = ln.match(/^(VERDICT|WHY|CODE|SAY|NEXT|NUDGE|CLOSER)\s*:\s*(.*)$/i);
    if (m) {
      const key = m[1].toUpperCase();
      const val = m[2].trim().replace(/^\[\[|\]\]$/g, '');
      lastKey = key;
      if (key === 'VERDICT') { seenVerdict = true; v.verdict = pmNormalizeVerdict(val); }
      else if (key === 'WHY') v.why += (v.why ? ' ' : '') + val;
      else if (key === 'CODE') { if (val) v.codes.push(val.replace(/\[\[|\]\]/g, '')); }
      else if (key === 'SAY') { if (val) v.say.push(val); }
      else if (key === 'NEXT') { if (val) v.next.push(val); }
      else if (key === 'NUDGE') v.nudge += (v.nudge ? ' ' : '') + val;
      else if (key === 'CLOSER') v.closer += (v.closer ? ' ' : '') + val;
    } else if (!seenVerdict) {
      introLines.push(ln);
    } else if (lastKey) {
      // continuation line
      if (lastKey === 'WHY') v.why += ' ' + ln;
      else if (lastKey === 'NUDGE') v.nudge += ' ' + ln;
      else if (lastKey === 'CLOSER') v.closer += ' ' + ln;
      else if (lastKey === 'SAY' && v.say.length) v.say[v.say.length - 1] += ' ' + ln;
      else if (lastKey === 'NEXT' && v.next.length) v.next[v.next.length - 1] += ' ' + ln;
    }
  }
  if (!v.verdict) return null;
  v.intro = introLines.join(' ');
  return v;
}

// Strip the inline markup pmRenderInline understands (**bold**, *italic*,
// [[citation]]) so exported text doesn't arrive with literal asterisks in it.
// No lookbehind — older iOS Safari chokes on it.
// SAY lines are rendered inside our own curly quotes. Strip any the model added
// itself, including a single unbalanced one, so quotes never nest or dangle.
function pmCleanSay(s) {
  let t = String(s == null ? '' : s).trim().replace(/^[\u201c\u201d"']+|[\u201c\u201d"']+$/g, '').trim();
  const dq = (t.match(/[\u201c\u201d"]/g) || []).length;
  if (dq === 1) t = t.replace(/[\u201c\u201d"]/, '').trim();
  return t;
}

function pmPlain(s) {
  return String(s == null ? '' : s)
    .replace(/\[\[|\]\]/g, '')
    .replace(/\*\*(.+?)\*\*/g, '$1')
    .replace(/\*(.+?)\*/g, '$1');
}

// One clock for every record that leaves the app: 12-hour, with the date.
function pmStamp(ms) {
  return new Date(ms).toLocaleString('en-US', {
    month: 'short', day: 'numeric', year: 'numeric',
    hour: 'numeric', minute: '2-digit', hour12: true,
  });
}

// A determination as plain text an inspector can paste into a report or email.
function pmVerdictToText(verdict, intro, meta) {
  const L = [];
  L.push('POCKET MARSHAL \u2014 DETERMINATION');
  L.push(pmStamp((meta && meta.at) || Date.now()));
  if (meta && meta.model) L.push('Generated by: ' + meta.model);
  L.push('');
  if (intro) { L.push(pmPlain(intro)); L.push(''); }
  const vd = PM_VERDICTS[verdict.verdict];
  L.push('CALL: ' + ((vd && vd.label) || verdict.verdict));
  if (verdict.why) L.push('WHY: ' + pmPlain(verdict.why));
  if (verdict.codes && verdict.codes.length) {
    L.push('');
    L.push('CODE REFERENCES');
    verdict.codes.forEach((c) => L.push('  \u2022 ' + pmPlain(c)));
  }
  if (verdict.say && verdict.say.length) {
    L.push('');
    L.push('SUGGESTED LANGUAGE FOR THE OWNER');
    verdict.say.forEach((s) => L.push('  "' + pmCleanSay(pmPlain(s)) + '"'));
  }
  if (verdict.next && verdict.next.length) {
    L.push('');
    L.push("THE OWNER'S PATH FORWARD");
    verdict.next.forEach((s, i) => L.push('  ' + (i + 1) + '. ' + pmPlain(s)));
  }
  if (verdict.nudge) { L.push(''); L.push('ESCALATE: ' + pmPlain(verdict.nudge)); }
  L.push('');
  L.push('\u2014');
  L.push('Guidance only. Verify the governing section in your adopted edition');
  L.push('before issuing any finding. The AHJ decides.');
  return L.join('\n');
}

// A determination packaged as a message to the Fire Marshal — the escalation
// Marshal recommends, actually sendable instead of just advised.
function pmEscalationText(verdict, intro, meta, who) {
  const L = [];
  L.push('Requesting your read on this before I finalize.');
  L.push('');
  if (who) L.push('Inspector: ' + who);
  L.push('Time: ' + pmStamp((meta && meta.at) || Date.now()));
  L.push('');
  L.push('WHAT I HAVE');
  if (intro) L.push(pmPlain(intro));
  const vd = PM_VERDICTS[verdict.verdict];
  L.push('Current call: ' + ((vd && vd.label) || verdict.verdict));
  if (verdict.why) L.push('Reasoning: ' + pmPlain(verdict.why));
  if (verdict.codes && verdict.codes.length) L.push('Sections: ' + verdict.codes.map(pmPlain).join('; '));
  if (verdict.nudge) { L.push(''); L.push('WHY I\u2019M ESCALATING'); L.push(pmPlain(verdict.nudge)); }
  L.push('');
  L.push('Let me know how you want me to proceed.');
  return L.join('\n');
}

async function pmCopyOrShare(text, title) {
  // iOS: the share sheet is far more useful in the field than the clipboard.
  if (navigator.share) {
    try { await navigator.share({ title: title || 'Pocket Marshal determination', text }); return 'shared'; }
    catch (e) { if (e && e.name === 'AbortError') return 'cancelled'; }
  }
  try { await navigator.clipboard.writeText(text); return 'copied'; }
  catch (e) { /* fall through */ }
  try {
    const ta = document.createElement('textarea');
    ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0';
    document.body.appendChild(ta); ta.select();
    document.execCommand('copy'); document.body.removeChild(ta);
    return 'copied';
  } catch (e) { return 'failed'; }
}

function PMFeedback({ theme, rating, onRate, onCopy }) {
  const [thanks, setThanks] = React.useState(false);
  const hit = (val) => {
    onRate && onRate(val);
    if (val === 'down' && rating !== 'down') { setThanks(true); setTimeout(() => setThanks(false), 2600); }
  };
  const base = {
    display: 'grid', placeItems: 'center', width: 44, height: 44, borderRadius: 11,
    cursor: 'pointer', flexShrink: 0, transition: 'background .15s, color .15s',
  };
  const [copied, setCopied] = React.useState(null);
  const doCopy = async () => {
    const r = await onCopy();
    if (r === 'cancelled') return;
    setCopied(r === 'failed' ? "Couldn't copy" : r === 'shared' ? 'Shared' : 'Copied');
    setTimeout(() => setCopied(null), 2200);
  };
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 2, flexWrap: 'wrap' }}>
      <span style={{ fontSize: '0.78em', fontWeight: 700, color: theme.muted, marginRight: 2 }}>
        {thanks ? 'Flagged \u2014 thanks. Trust your own read on this one.' : 'Was this right?'}
      </span>
      <button onClick={() => hit('up')} aria-label="This was right" aria-pressed={rating === 'up'} style={{
        ...base,
        background: rating === 'up' ? (theme.dark ? '#1e3b2a' : '#e6f2e9') : 'transparent',
        border: `1.5px solid ${rating === 'up' ? (theme.dark ? '#3f7a55' : '#bcd9c3') : theme.line}`,
        color: rating === 'up' ? (theme.dark ? '#7fd3a0' : '#2f6b41') : theme.muted,
      }}><IconThumbUp size={18} /></button>
      <button onClick={() => hit('down')} aria-label="This looked wrong" aria-pressed={rating === 'down'} style={{
        ...base,
        background: rating === 'down' ? (theme.dark ? '#3a201c' : '#fae6e1') : 'transparent',
        border: `1.5px solid ${rating === 'down' ? (theme.dark ? '#8c4436' : '#e5b4a8') : theme.line}`,
        color: rating === 'down' ? (theme.dark ? '#f0917c' : '#8c1c0e') : theme.muted,
      }}><IconThumbDown size={18} /></button>
      {onCopy && (
        <button onClick={doCopy} aria-label="Copy this determination" style={{
          display: 'inline-flex', alignItems: 'center', gap: 7, minHeight: 44, padding: '0 13px',
          marginLeft: 'auto', borderRadius: 11, cursor: 'pointer', font: 'inherit',
          background: 'transparent', border: `1.5px solid ${theme.line}`,
          color: copied ? theme.emberDark : theme.muted, fontWeight: 750, fontSize: '0.8em',
        }}><IconCopy size={17} /> {copied || 'Copy'}</button>
      )}
    </div>
  );
}

// A citation row: the whole row opens NFPA, but the citation itself is real
// selectable text so it can be long-pressed and copied. A row-level click is
// ignored while a selection exists, so highlighting never navigates away.
function pmCiteSource(cite) {
  const s = String(cite || '');
  if (/NFPA\s*1(01)?-?FL/i.test(s) || /NFPA\s*(1|101)\b/i.test(s)) return 'Florida Fire Prevention Code';
  const nf = s.match(/NFPA\s*(\d+)/i);
  if (nf) return 'NFPA ' + nf[1] + ' \u00b7 referenced standard';
  if (/\bFBC\b/i.test(s)) return 'Florida Building Code';
  if (/\bguide\b/i.test(s)) return 'Inspector 101 guide';
  return 'Code reference';
}

function PMCodeRow({ cite, theme, onCite }) {
  const open = () => {
    const sel = typeof window !== 'undefined' && window.getSelection && window.getSelection().toString();
    if (sel && sel.trim()) return; // they're highlighting, not tapping
    onCite && onCite(cite);
  };
  return (
    <div role="button" tabIndex={0} onClick={open}
      onKeyDown={(ev) => { if (ev.key === 'Enter' || ev.key === ' ') { ev.preventDefault(); open(); } }}
      style={{
        display: 'flex', alignItems: 'center', gap: 12, width: '100%', boxSizing: 'border-box',
        background: theme.navySoft, border: `1.5px solid ${theme.line}`, borderRadius: 12,
        padding: '11px 14px', minHeight: 52, cursor: 'pointer', font: 'inherit', textAlign: 'left',
      }}>
      <span style={{ color: theme.emberDark, display: 'grid', placeItems: 'center', flexShrink: 0 }}><IconBook size={20} /></span>
      <span style={{ flex: 1, minWidth: 0 }}>
        <span style={{ display: 'block', fontSize: '0.68em', fontWeight: 800, letterSpacing: '0.07em', textTransform: 'uppercase', color: theme.muted }}>{pmCiteSource(cite)}</span>
        <span style={{
          display: 'block', fontWeight: 800, fontSize: '0.95em', color: theme.ink, marginTop: 1,
          userSelect: 'text', WebkitUserSelect: 'text', WebkitTouchCallout: 'default', cursor: 'text',
        }}>{cite}</span>
      </span>
      <span style={{ color: theme.muted, display: 'grid', placeItems: 'center', flexShrink: 0 }}><IconChevron size={18} /></span>
    </div>
  );
}

function PMVerdictCard({ verdict, theme, onCite, rating, onRate, onCopy, onEscalate }) {
  const vd = PM_VERDICTS[verdict.verdict] || PM_VERDICTS['ESCALATE'];
  const teal = theme.dark
    ? { bg: '#14332e', edge: '#23584f', dark: '#7fd4c5', ink: '#cfe9e3' }
    : { bg: '#e7f4f1', edge: '#bfe0d9', dark: '#136a5e', ink: '#17433c' };
  const path = theme.dark
    ? { bg: '#2c2616', edge: '#574826', badge: '#d8a849', badgeInk: '#1a1407', dark: '#e8c478', ink: '#ece0c8' }
    : { bg: '#fbf3e3', edge: '#ecd6a8', badge: '#b07d12', badgeInk: '#fff8f0', dark: '#8a5f0a', ink: '#4d3c18' };
  return (
    <div style={{
      background: theme.paper, border: `2px solid ${vd.edge}`, borderRadius: 18,
      overflow: 'hidden', boxShadow: '0 2px 12px rgba(26,46,74,0.08)',
    }}>
      {/* Header */}
      <div style={{ background: vd.soft, padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 10 }}>
        <span style={{
          background: vd.badge, color: vd.badgeInk, fontWeight: 800, fontSize: '0.8em',
          letterSpacing: '0.06em', textTransform: 'uppercase', padding: '7px 12px', borderRadius: 9, whiteSpace: 'nowrap',
        }}>{verdict.verdict}</span>
        <span style={{ fontWeight: 750, fontSize: '0.86em', color: vd.dark }}>Marshal's call</span>
        <span style={{ marginLeft: 'auto', display: 'grid', placeItems: 'center', color: vd.dark }}><IconCheck size={18} stroke={2.6} /></span>
      </div>

      <div style={{ padding: '15px 16px 16px', display: 'flex', flexDirection: 'column', gap: 13 }}>
        {/* Why */}
        {verdict.why && (
          <p style={{ margin: 0, fontSize: '1.02em', lineHeight: 1.5, fontWeight: 600, color: theme.ink, textWrap: 'pretty' }}>
            {pmRenderInline(verdict.why.replace(/\[\[|\]\]/g, ''), theme, 'why')}
          </p>
        )}

        {/* Code references */}
        {verdict.codes.length > 0 && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {verdict.codes.map((c, i) => (
              <PMCodeRow key={i} cite={c} theme={theme} onCite={onCite} />
            ))}
          </div>
        )}

        {/* How to say this to the owner */}
        {verdict.say.length > 0 && (
          <div style={{ background: teal.bg, border: `1.5px solid ${teal.edge}`, borderRadius: 14, padding: '13px 16px' }}>
            <div style={{ fontSize: '0.74em', fontWeight: 800, letterSpacing: '0.07em', textTransform: 'uppercase', color: teal.dark, marginBottom: 9 }}>
              How to say this to the owner
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
              {verdict.say.map((s, i) => (
                <p key={i} style={{ margin: 0, color: teal.ink, fontWeight: 550, lineHeight: 1.5, fontSize: '0.95em', textWrap: 'pretty' }}>
                  <span aria-hidden="true" style={{ color: teal.dark, fontWeight: 800 }}>&ldquo;</span>{pmCleanSay(s)}<span aria-hidden="true" style={{ color: teal.dark, fontWeight: 800 }}>&rdquo;</span>
                </p>
              ))}
            </div>
          </div>
        )}

        {/* The path forward — concrete next steps for the owner */}
        {verdict.next && verdict.next.length > 0 && (
          <div style={{ background: path.bg, border: `1.5px solid ${path.edge}`, borderRadius: 14, padding: '13px 16px' }}>
            <div style={{ fontSize: '0.74em', fontWeight: 800, letterSpacing: '0.07em', textTransform: 'uppercase', color: path.dark, marginBottom: 11 }}>
              The owner's path forward
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 11 }}>
              {verdict.next.map((s, i) => (
                <div key={i} style={{ display: 'flex', gap: 11, alignItems: 'flex-start' }}>
                  <span aria-hidden="true" style={{
                    flexShrink: 0, width: 22, height: 22, borderRadius: '50%', background: path.badge,
                    color: path.badgeInk, fontWeight: 800, fontSize: '0.74em', display: 'grid', placeItems: 'center', marginTop: 1,
                  }}>{i + 1}</span>
                  <p style={{ margin: 0, color: path.ink, fontWeight: 600, lineHeight: 1.45, fontSize: '0.95em', textWrap: 'pretty' }}>
                    {pmRenderInline(s.replace(/\[\[|\]\]/g, ''), theme, 'next' + i)}
                  </p>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Escalation nudge */}
        {verdict.nudge && (
          <div style={{
            display: 'flex', alignItems: 'flex-start', gap: 11,
            background: PM_VERDICTS['ESCALATE'].soft, border: `1.5px solid ${PM_VERDICTS['ESCALATE'].edge}`,
            borderRadius: 12, padding: '12px 14px',
          }}>
            <span style={{ color: PM_VERDICTS['ESCALATE'].badge, display: 'grid', placeItems: 'center', marginTop: 1 }}><IconPhone size={19} /></span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <p style={{ margin: 0, fontWeight: 650, color: PM_VERDICTS['ESCALATE'].dark, lineHeight: 1.45, fontSize: '0.93em', textWrap: 'pretty' }}>{verdict.nudge}</p>
              {onEscalate && (
                <button onClick={onEscalate} style={{
                  display: 'inline-flex', alignItems: 'center', gap: 8, minHeight: 44, padding: '0 15px', marginTop: 10,
                  background: PM_VERDICTS['ESCALATE'].badge, color: '#fff', border: 'none', borderRadius: 11,
                  font: 'inherit', fontWeight: 800, fontSize: '0.86em', cursor: 'pointer',
                }}><IconSend size={16} stroke={2.4} /> Send this to the Fire Marshal</button>
              )}
            </div>
          </div>
        )}

        {(onRate || onCopy) && <PMFeedback theme={theme} rating={rating} onRate={onRate} onCopy={onCopy} />}

        {/* Encouraging closer */}
        {verdict.closer && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 9, marginTop: 1 }}>
            <MarshalAvatar styleKind={window.__pmAvatarKind === 'mascot' ? 'mascot' : 'badge'} size={24} />
            <p style={{ margin: 0, fontStyle: 'italic', color: theme.muted, fontWeight: 600, fontSize: '0.92em', lineHeight: 1.4 }}>{verdict.closer}</p>
          </div>
        )}
      </div>
    </div>
  );
}

// Serialize a verdict message back to Marshal's raw structured text (for AI conversation history)
function pmVerdictToRaw(msg) {
  if (msg.raw && msg.raw !== 'structured') return msg.raw;
  const v = msg.verdict;
  const lines = [];
  if (msg.text) lines.push(msg.text);
  lines.push('VERDICT: ' + v.verdict);
  if (v.why) lines.push('WHY: ' + v.why);
  (v.codes || []).forEach((c) => lines.push('CODE: ' + c));
  (v.say || []).forEach((s) => lines.push('SAY: ' + s));
  (v.next || []).forEach((s) => lines.push('NEXT: ' + s));
  if (v.nudge) lines.push('NUDGE: ' + v.nudge);
  if (v.closer) lines.push('CLOSER: ' + v.closer);
  return lines.join('\n');
}

Object.assign(window, { PMFeedback, PMCodeRow, pmPlain, pmCleanSay, pmEscalationText, pmStamp, pmVerdictToText, pmCopyOrShare, PM_VERDICTS, pmParseVerdict, pmNormalizeVerdict, pmVerdictToRaw, PMVerdictCard });
