/* Humanitarian Organization — Console theme. */

const LEVEL_MAP_COLOR = { calm: '#16A37B', watch: '#E5A019', warning: '#E2742B', severe: '#DC4A4F' };

function VillageMap({ villages, synthesisByVillage, onSelect, focusVillageId }) {
  const ref = React.useRef(null);
  const mapRef = React.useRef(null);
  const markersRef = React.useRef({});

  React.useEffect(function () {
    if (!ref.current || mapRef.current || !window.L) return;
    const map = window.L.map(ref.current, { scrollWheelZoom: false }).setView([20.9, 95.5], 7);
    window.L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; OpenStreetMap contributors', maxZoom: 12,
    }).addTo(map);
    mapRef.current = map;
    villages.forEach(function (v) {
      const level = synthesisByVillage[v.id].level;
      const marker = window.L.circleMarker([v.lat, v.lng], {
        radius: 7, color: LEVEL_MAP_COLOR[level], fillColor: LEVEL_MAP_COLOR[level], fillOpacity: 0.85, weight: 2,
      }).addTo(map);
      marker.bindPopup('<strong>' + v.name + '</strong><br/>' + v.township + ', ' + v.region + '<br/>' + LEVEL_LABEL[level]);
      marker.on('click', function () { onSelect && onSelect(v.id); });
      markersRef.current[v.id] = marker;
    });
    setTimeout(function () { map.invalidateSize(); }, 60);
    return function () { map.remove(); mapRef.current = null; markersRef.current = {}; };
  }, []);

  // Keep the map and the list linked: expanding a row flies to and opens that village.
  React.useEffect(function () {
    if (!focusVillageId || !mapRef.current) return;
    const v = villages.find(function (x) { return x.id === focusVillageId; });
    const marker = markersRef.current[focusVillageId];
    if (v && marker) {
      mapRef.current.flyTo([v.lat, v.lng], 10, { duration: 0.8 });
      setTimeout(function () { marker.openPopup(); }, 850);
    }
  }, [focusVillageId]);

  return (
    <div style={{ position: 'relative' }}>
      <div ref={ref} style={{ height: 320, borderRadius: 'var(--radius-card)', overflow: 'hidden', border: '1px solid var(--border-subtle)' }} />
      <div style={{ position: 'absolute', bottom: 10, left: 10, zIndex: 500, display: 'flex', gap: 10, padding: '6px 12px', borderRadius: 'var(--radius-pill)', background: 'rgba(255,255,255,0.92)', boxShadow: 'var(--shadow-sm)', font: '11px var(--font-text)', color: 'var(--text-body)' }}>
        {['calm', 'watch', 'warning', 'severe'].map(function (l) {
          return <span key={l} style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
            <span style={{ width: 8, height: 8, borderRadius: '50%', background: LEVEL_MAP_COLOR[l] }} />{LEVEL_LABEL[l]}
          </span>;
        })}
      </div>
    </div>
  );
}

function AutoTriggerCountdown({ deadline }) {
  const [now, setNow] = React.useState(Date.now());
  React.useEffect(function () {
    const t = setInterval(function () { setNow(Date.now()); }, 250);
    return function () { clearInterval(t); };
  }, []);
  const total = window.AsterStore.AUTO_TRIGGER_WINDOW_MS;
  const remaining = Math.max(0, deadline - now);
  const s = Math.ceil(remaining / 1000);
  return (
    <div style={{ flex: 1, minWidth: 220 }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 4 }}>
        <span style={{ font: '600 11px var(--font-text)', color: 'var(--warning-strong)' }}>Review window — auto-triggers unless declined</span>
        <span style={{ font: '600 12px var(--font-mono)', color: 'var(--warning-strong)' }}>{s}s</span>
      </div>
      <div className="ap-countdown-track"><div className="ap-countdown-fill" style={{ width: (100 * remaining / total) + '%' }} /></div>
      <div style={{ font: '10px var(--font-mono)', color: 'var(--text-subtle)', marginTop: 3 }}>demo-accelerated window</div>
    </div>
  );
}

function LevelSummaryStrip({ villages, synthesis, activeLevel, onLevelClick }) {
  const counts = { calm: 0, watch: 0, warning: 0, severe: 0 };
  villages.forEach(function (v) { counts[synthesis[v.id].level] += 1; });
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))', gap: 10, marginBottom: 'var(--space-5)' }}>
      {['severe', 'warning', 'watch', 'calm'].map(function (l) {
        const active = activeLevel === l;
        return (
          <button key={l} className="ap-chip ap-hover-lift" aria-pressed={active} onClick={function () { onLevelClick(active ? 'all' : l); }} style={{
            display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', borderRadius: 'var(--radius-card)', cursor: 'pointer', textAlign: 'left',
            border: '1px solid ' + (active ? LEVEL_COLOR[l] : 'var(--border-subtle)'),
            background: active ? 'var(--' + l + '-soft)' : 'var(--surface-card)',
          }}>
            <span style={{ width: 10, height: 10, borderRadius: '50%', background: LEVEL_COLOR[l], flexShrink: 0 }} />
            <span>
              <span style={{ display: 'block', font: '700 18px var(--font-mono)', color: 'var(--text-strong)' }}>{counts[l]}</span>
              <span style={{ font: '11px var(--font-text)', color: 'var(--text-muted)' }}>{LEVEL_LABEL[l]}</span>
            </span>
          </button>
        );
      })}
    </div>
  );
}

function HumanitarianApp() {
  const store = useAsterStore();
  const [tab, setTab] = React.useState('villages');
  const [callFilter, setCallFilter] = React.useState('all');
  const [levelFilter, setLevelFilter] = React.useState('all');
  const [search, setSearch] = React.useState('');
  const [expandedVillage, setExpandedVillage] = React.useState(null);
  const [toast, setToast, toastNode] = useToast();
  const [protocolModal, setProtocolModal] = React.useState(null); // { template } | { blank } | null
  const [matchModal, setMatchModal] = React.useState(null);       // callId
  const [approveCall, setApproveCall] = React.useState(null);     // callId
  const [declineCall, setDeclineCall] = React.useState(null);     // callId
  const [retractTarget, setRetractTarget] = React.useState(null); // protocolId

  const org = window.AsterStore.ORG;
  const focusVillages = store.villages.filter(function (v) { return org.focusRegions.indexOf(v.region) !== -1; });

  const openCalls = store.calls.filter(function (c) { return c.status !== 'triggered' && c.status !== 'declined'; });
  const activeProtocols = store.protocols.filter(function (p) { return p.active; });

  const visibleVillages = focusVillages.filter(function (v) {
    if (levelFilter !== 'all' && store.synthesis[v.id].level !== levelFilter) return false;
    if (search.trim()) {
      const q = search.trim().toLowerCase();
      if ((v.name + ' ' + v.township + ' ' + v.region).toLowerCase().indexOf(q) === -1) return false;
    }
    return true;
  }).sort(function (a, b) {
    return window.AsterStore.levelRank(store.synthesis[b.id].level) - window.AsterStore.levelRank(store.synthesis[a.id].level);
  });

  const callCounts = { all: store.calls.length };
  ['submitted', 'reviewed', 'auto_pending', 'triggered', 'declined'].forEach(function (s) {
    callCounts[s] = store.calls.filter(function (c) { return c.status === s; }).length;
  });
  const CALL_FILTERS = [
    { id: 'all', label: 'All', count: callCounts.all },
    { id: 'submitted', label: 'Submitted', count: callCounts.submitted },
    { id: 'reviewed', label: 'Under review', count: callCounts.reviewed },
    { id: 'auto_pending', label: 'Auto-trigger pending', count: callCounts.auto_pending },
    { id: 'triggered', label: 'Triggered', count: callCounts.triggered },
    { id: 'declined', label: 'Declined', count: callCounts.declined },
  ];
  const visibleCalls = store.calls.filter(function (c) { return callFilter === 'all' || c.status === callFilter; });

  const approveTarget = approveCall ? window.AsterStore.findCall(approveCall) : null;
  const approveProtocol = approveTarget && approveTarget.protocolId ? window.AsterStore.findProtocol(approveTarget.protocolId) : null;

  return (
    <div style={{ maxWidth: 1080, margin: '0 auto', padding: 'var(--space-6) var(--space-5) var(--space-11)' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', flexWrap: 'wrap', gap: 12, marginBottom: 'var(--space-5)' }}>
        <div>
          <div style={{ font: '600 11px var(--font-mono)', letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-subtle)' }}>Humanitarian Organization</div>
          <h1 style={{ margin: '2px 0 0', font: '700 22px var(--font-display)', color: 'var(--text-strong)' }}>{org.name}</h1>
        </div>
        <div style={{ display: 'flex', gap: 6 }}>
          {org.focusRegions.map(function (r) { return <Badge key={r} tone="brand">{r}</Badge>; })}
        </div>
      </div>

      <Tabs active={tab} onChange={setTab} items={[
        { id: 'villages', label: 'Villages', icon: 'map-pin', count: focusVillages.length },
        { id: 'calls', label: 'Calls for help', icon: 'megaphone', count: openCalls.length },
        { id: 'protocols', label: 'Protocol library', icon: 'file-text', count: activeProtocols.length },
      ]} />

      {/* ================= VILLAGES ================= */}
      {tab === 'villages' && (
        <div>
          <LevelSummaryStrip villages={focusVillages} synthesis={store.synthesis} activeLevel={levelFilter} onLevelClick={setLevelFilter} />

          <Card padding="md" elevation="sm" style={{ marginBottom: 'var(--space-5)' }}>
            <VillageMap villages={focusVillages} synthesisByVillage={store.synthesis} onSelect={setExpandedVillage} focusVillageId={expandedVillage} />
          </Card>

          <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap', marginBottom: 'var(--space-4)' }}>
            <div style={{ flex: 1, minWidth: 220 }}>
              <Input placeholder="Search village, township or region…" value={search} onChange={function (e) { setSearch(e.target.value); }}
                iconLeft={Ic('search', { width: 14, height: 14 })} />
            </div>
            <span style={{ font: '12px var(--font-text)', color: 'var(--text-subtle)' }}>{visibleVillages.length} of {focusVillages.length} villages{levelFilter !== 'all' ? ' · ' + LEVEL_LABEL[levelFilter] + ' only' : ''}</span>
          </div>

          <Card padding="none" elevation="sm">
            {visibleVillages.length === 0 && <div style={{ padding: 'var(--space-6)' }}><EmptyState icon="search-x" text="No villages match this search / filter." /></div>}
            {visibleVillages.map(function (v) {
              const syn = store.synthesis[v.id];
              const trend = store.trends[v.id];
              const expanded = expandedVillage === v.id;
              return (
                <div key={v.id} style={{ borderBottom: '1px solid var(--border-subtle)', background: expanded ? 'var(--surface-sunken)' : 'transparent' }}>
                  <button className="ap-row" onClick={function () { setExpandedVillage(expanded ? null : v.id); }} aria-expanded={expanded} style={{
                    width: '100%', display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10,
                    padding: '12px 16px', border: 'none', background: 'none', cursor: 'pointer', textAlign: 'left',
                  }}>
                    <span style={{ font: '600 13px var(--font-text)', color: 'var(--text-strong)', minWidth: 110 }}>{v.name}</span>
                    <span style={{ font: '12px var(--font-text)', color: 'var(--text-muted)', flex: 1 }}>{v.township}, {v.region}</span>
                    <span style={{ flexShrink: 0 }}><Sparkline points={trend.composite} color={LEVEL_COLOR[syn.level]} width={72} height={22} fill={false} /></span>
                    <Badge tone={syn.level}>{LEVEL_LABEL[syn.level]}</Badge>
                    {Ic(expanded ? 'chevron-up' : 'chevron-down', { width: 15, height: 15 })}
                  </button>
                  {expanded && (
                    <div className="ap-rise" style={{ padding: '0 16px 16px' }}>
                      <div style={{ display: 'flex', gap: 'var(--space-6)', flexWrap: 'wrap', marginBottom: 10 }}>
                        <Stat label="Composite" value={syn.composite} unit="/100" />
                        <Stat label="Lead time" value={syn.leadTime} />
                        <Stat label="Confidence" value={syn.confidence} />
                        <Stat label="Top domain" value={DOMAIN_LABEL[syn.topDomain]} />
                      </div>
                      <div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', marginBottom: 10 }}>
                        {['climate', 'economic', 'conflict'].map(function (d) {
                          const sc = store.signals[v.id][d].score;
                          return (
                            <span key={d} style={{ display: 'flex', alignItems: 'center', gap: 6, font: '12px var(--font-text)', color: 'var(--text-muted)' }}>
                              {Ic(DOMAIN_ICON[d], { width: 13, height: 13 })}{DOMAIN_LABEL[d]}
                              <strong style={{ font: '600 12px var(--font-mono)', color: sc >= 50 ? 'var(--warning-strong)' : 'var(--text-strong)' }}>{sc}</strong>
                            </span>
                          );
                        })}
                      </div>
                      <ul style={{ margin: 0, paddingLeft: 18, font: '13px/1.6 var(--font-text)', color: 'var(--text-body)' }}>
                        {syn.keyDrivers.map(function (d, i) { return <li key={i}>{d}</li>; })}
                      </ul>
                    </div>
                  )}
                </div>
              );
            })}
          </Card>
        </div>
      )}

      {/* ================= CALLS ================= */}
      {tab === 'calls' && (
        <div>
          <div style={{ marginBottom: 'var(--space-5)' }}>
            <FilterChips items={CALL_FILTERS} active={callFilter} onChange={setCallFilter} />
          </div>
          {visibleCalls.length === 0 && <EmptyState icon="inbox" text="No calls for help in this filter." />}
          {visibleCalls.map(function (c) {
            const v = window.AsterStore.findVillage(c.villageId);
            const p = c.protocolId ? window.AsterStore.findProtocol(c.protocolId) : null;
            const syn = store.synthesis[c.villageId];
            const matching = window.AsterStore.protocolsMatchingVillage(c.villageId);
            const canDecide = (c.status === 'submitted' || c.status === 'reviewed') && p;
            return (
              <Card key={c.id} padding="md" elevation="sm" className="ap-hover-lift" style={{ marginBottom: 10 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
                  <div>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                      <span style={{ font: '600 14px var(--font-text)', color: 'var(--text-strong)' }}>{v.name}</span>
                      <span style={{ font: '12px var(--font-text)', color: 'var(--text-subtle)' }}>{v.township}, {v.region}</span>
                      <Badge tone={syn.level} variant="soft">{LEVEL_LABEL[syn.level]}</Badge>
                    </div>
                    <div style={{ font: '12px var(--font-text)', color: 'var(--text-muted)', marginTop: 2 }}>
                      {p ? p.name + ' · ' + fmtUSD(p.amount) + (p.unrestricted ? ' · unrestricted' : '') : 'General distress signal'}
                    </div>
                  </div>
                  <Badge tone={CALL_STATUS_TONE[c.status]}>{CALL_STATUS_LABEL[c.status]}</Badge>
                </div>

                {c.message && <div style={{ font: '13px/1.5 var(--font-text)', color: 'var(--text-body)', marginTop: 8 }}>{c.message}</div>}
                {c.orgNote && <div style={{ font: '12px/1.5 var(--font-text)', color: 'var(--text-muted)', marginTop: 6, fontStyle: 'italic' }}>Risk desk note: {c.orgNote}</div>}
                <CallStepper call={c} />
                <div style={{ font: '11px var(--font-mono)', color: 'var(--text-subtle)', marginTop: 8 }}>Submitted {fmtDateTime(c.submittedAt)}{c.reviewedAt ? ' · reviewed ' + fmtDateTime(c.reviewedAt) : ''}</div>

                {c.status === 'auto_pending' && (
                  <div style={{ marginTop: 12, display: 'flex', alignItems: 'flex-end', gap: 14, flexWrap: 'wrap' }}>
                    <AutoTriggerCountdown deadline={c.autoTriggerDeadline} />
                    <Button variant="danger" size="sm" onClick={function () { setDeclineCall(c.id); }}>Decline now</Button>
                  </div>
                )}

                {!p && (c.status === 'submitted' || c.status === 'reviewed') && (
                  <div style={{ marginTop: 10 }}>
                    <Button variant="secondary" size="sm" iconLeft={Ic('link', { width: 13, height: 13 })} onClick={function () { setMatchModal(c.id); }} disabled={matching.length === 0}>
                      {matching.length === 0 ? 'No matching protocol yet' : 'Match to a protocol'}
                    </Button>
                  </div>
                )}

                {canDecide && (
                  <div style={{ marginTop: 10, display: 'flex', gap: 8 }}>
                    <Button variant="primary" size="sm" iconLeft={Ic('check', { width: 13, height: 13 })} onClick={function () { setApproveCall(c.id); }}>Approve…</Button>
                    <Button variant="danger" size="sm" iconLeft={Ic('x', { width: 13, height: 13 })} onClick={function () { setDeclineCall(c.id); }}>Decline…</Button>
                  </div>
                )}
              </Card>
            );
          })}
        </div>
      )}

      {/* ================= PROTOCOLS ================= */}
      {tab === 'protocols' && (
        <div>
          <SectionHeading eyebrow="Starter templates" title="Protocol library"
            hint="Start from a template or build your own — villages apply against what you publish."
            right={<Button variant="secondary" size="sm" iconLeft={Ic('plus', { width: 14, height: 14 })} onClick={function () { setProtocolModal({ blank: true }); }}>Build custom protocol</Button>} />
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(230px, 1fr))', gap: 10, marginBottom: 'var(--space-6)' }}>
            {window.AsterStore.PROTOCOL_TEMPLATES.map(function (t) {
              return (
                <Card key={t.id} padding="md" elevation="sm" className="ap-hover-lift">
                  <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 4 }}>
                    {Ic(DOMAIN_ICON[t.domain], { width: 14, height: 14, style: { color: 'var(--text-subtle)' } })}
                    <span style={{ font: '600 13px var(--font-text)', color: 'var(--text-strong)' }}>{t.name}</span>
                  </div>
                  <div style={{ font: '12px/1.5 var(--font-text)', color: 'var(--text-muted)', marginBottom: 8 }}>{t.purpose}</div>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                    <span style={{ font: '12px var(--font-mono)', color: 'var(--text-subtle)' }}>{fmtUSD(t.amount)} · min {LEVEL_LABEL[t.minLevel]}</span>
                    <Button variant="ghost" size="sm" onClick={function () { setProtocolModal({ template: t }); }}>Use template</Button>
                  </div>
                </Card>
              );
            })}
          </div>

          <SectionHeading eyebrow={org.name} title="Published protocols" />
          {activeProtocols.map(function (p) {
            const matchCount = focusVillages.filter(function (v) {
              return window.AsterStore.levelRank(store.synthesis[v.id].level) >= window.AsterStore.levelRank(p.minLevel);
            }).length;
            return (
              <Card key={p.id} padding="md" elevation="sm" style={{ marginBottom: 10 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 8 }}>
                  <div>
                    <div style={{ font: '600 13px var(--font-text)', color: 'var(--text-strong)' }}>{p.name} · {fmtUSD(p.amount)}</div>
                    <div style={{ font: '12px/1.5 var(--font-text)', color: 'var(--text-muted)' }}>{p.purpose}</div>
                    <div style={{ font: '11px var(--font-mono)', color: 'var(--text-subtle)', marginTop: 4 }}>
                      Min level {LEVEL_LABEL[p.minLevel]} · domain {DOMAIN_LABEL[p.domain]}{p.unrestricted ? ' · unrestricted funding' : ''} · currently matches <strong>{matchCount}</strong> villages
                    </div>
                  </div>
                  <Button variant="ghost" size="sm" onClick={function () { setRetractTarget(p.id); }}>Retract…</Button>
                </div>
              </Card>
            );
          })}
          {store.protocols.filter(function (p) { return !p.active; }).length > 0 && (
            <div style={{ marginTop: 'var(--space-5)' }}>
              <div style={{ font: '600 12px var(--font-text)', color: 'var(--text-subtle)', marginBottom: 8 }}>Retracted</div>
              {store.protocols.filter(function (p) { return !p.active; }).map(function (p) {
                return <div key={p.id} style={{ font: '12px var(--font-text)', color: 'var(--text-subtle)', padding: '4px 0', textDecoration: 'line-through' }}>{p.name}</div>;
              })}
            </div>
          )}
        </div>
      )}

      {/* ================= DIALOGS ================= */}
      <ProtocolFormModal open={!!protocolModal} onClose={function () { setProtocolModal(null); }} seed={protocolModal}
        focusVillages={focusVillages} synthesis={store.synthesis}
        onSubmit={function (data) { window.AsterStore.publishProtocol(data); setProtocolModal(null); setToast('Protocol published — matching villages can now apply.'); }} />

      <MatchProtocolModal callId={matchModal} onClose={function () { setMatchModal(null); }}
        onSubmit={function (callId, protocolId) { window.AsterStore.matchProtocolToCall(callId, protocolId); setMatchModal(null); setToast('Matched to protocol.'); }} />

      <ConfirmDialog open={!!approveCall} onClose={function () { setApproveCall(null); }}
        title="Approve this call for help?"
        confirmLabel={approveProtocol && approveProtocol.unrestricted ? 'Approve — start review window' : 'Approve — trigger funds'}
        onConfirm={function () {
          const unrestricted = approveProtocol && approveProtocol.unrestricted;
          window.AsterStore.humanitarianDecide(approveCall, 'approve');
          setApproveCall(null);
          setToast(unrestricted ? { message: 'Approved — review window started.', tone: 'info' } : 'Protocol triggered — funds moved to the Community Fund.');
        }}>
        {approveTarget && approveProtocol && (function () {
          const v = window.AsterStore.findVillage(approveTarget.villageId);
          return (
            <div>
              <div style={{ border: '1px solid var(--border-subtle)', borderRadius: 'var(--radius-card)', padding: '12px 14px', marginBottom: 12 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}>
                  <span style={{ font: '600 13px var(--font-text)', color: 'var(--text-strong)' }}>{approveProtocol.name}</span>
                  <span style={{ font: '600 13px var(--font-mono)', color: 'var(--brand)' }}>{fmtUSD(approveProtocol.amount)}</span>
                </div>
                <div style={{ font: '12px var(--font-text)', color: 'var(--text-muted)', marginTop: 3 }}>{v.name} · {v.township}, {v.region}</div>
              </div>
              <div style={{ font: '13px/1.6 var(--font-text)', color: 'var(--text-body)' }}>
                {approveProtocol.unrestricted
                  ? 'This protocol is unrestricted: approving starts a short review window, after which funds move automatically unless someone declines.'
                  : 'Approving triggers the protocol immediately — ' + fmtUSD(approveProtocol.amount) + ' moves to the village Community Fund.'}
              </div>
            </div>
          );
        })()}
      </ConfirmDialog>

      <DeclineDialog callId={declineCall} onClose={function () { setDeclineCall(null); }}
        onDecline={function (callId, reason) {
          window.AsterStore.humanitarianDecide(callId, 'decline', reason);
          setDeclineCall(null);
          setToast({ message: 'Call declined — the village sees your reason.', tone: 'info' });
        }} />

      <ConfirmDialog open={!!retractTarget} onClose={function () { setRetractTarget(null); }}
        title="Retract this protocol?" tone="danger" confirmLabel="Retract protocol"
        body={retractTarget ? 'Villages will no longer be able to apply against "' + (window.AsterStore.findProtocol(retractTarget) || {}).name + '". Calls already in progress are not affected.' : ''}
        onConfirm={function () { window.AsterStore.retractProtocol(retractTarget); setRetractTarget(null); setToast({ message: 'Protocol retracted.', tone: 'info' }); }} />

      {toastNode}
    </div>
  );
}

function DeclineDialog({ callId, onClose, onDecline }) {
  const [reason, setReason] = React.useState('');
  React.useEffect(function () { if (callId) setReason(''); }, [callId]);
  const call = callId ? window.AsterStore.findCall(callId) : null;
  const v = call ? window.AsterStore.findVillage(call.villageId) : null;
  return (
    <Modal open={!!callId} onClose={onClose} title="Decline this call for help?" width={460}
      subtitle={v ? v.name + ' · ' + v.township + ', ' + v.region : null}
      footer={
        <React.Fragment>
          <Button variant="secondary" onClick={onClose}>Cancel</Button>
          <Button variant="danger" disabled={!reason.trim()} onClick={function () { onDecline(callId, reason.trim()); }}>Decline call</Button>
        </React.Fragment>
      }>
      <Textarea label="Reason (shared with the village governance body)" rows={3} value={reason} maxLength={280}
        onChange={function (e) { setReason(e.target.value); }}
        placeholder="e.g. Criteria borderline and covered by an existing disbursement…"
        hint="A clear reason keeps trust with the village — it appears on their request timeline." />
    </Modal>
  );
}

function ProtocolFormModal({ open, onClose, seed, onSubmit, focusVillages, synthesis }) {
  const t = seed && seed.template;
  const [name, setName] = React.useState('');
  const [domain, setDomain] = React.useState('climate');
  const [minLevel, setMinLevel] = React.useState('warning');
  const [amount, setAmount] = React.useState('');
  const [purpose, setPurpose] = React.useState('');
  const [unrestricted, setUnrestricted] = React.useState(false);

  React.useEffect(function () {
    if (!open) return;
    setName(t ? t.name : '');
    setDomain(t ? t.domain : 'climate');
    setMinLevel(t ? t.minLevel : 'warning');
    setAmount(t ? String(t.amount) : '');
    setPurpose(t ? t.purpose : '');
    setUnrestricted(t ? t.unrestricted : false);
  }, [open, seed]);

  // Live preview: how many villages this protocol would match right now.
  const matchCount = (focusVillages || []).filter(function (v) {
    return window.AsterStore.levelRank(synthesis[v.id].level) >= window.AsterStore.levelRank(minLevel);
  }).length;

  const valid = name.trim() && Number(amount) > 0 && purpose.trim();

  return (
    <Modal open={open} onClose={onClose} title={t ? 'Customize “' + t.name + '”' : 'Build a custom protocol'} width={500}
      footer={
        <React.Fragment>
          <span style={{ font: '12px var(--font-text)', color: 'var(--text-subtle)', marginRight: 'auto', display: 'flex', alignItems: 'center', gap: 6 }}>
            {Ic('map-pin', { width: 13, height: 13 })} Would currently match <strong style={{ color: 'var(--text-strong)' }}>{matchCount}</strong> villages
          </span>
          <Button variant="secondary" onClick={onClose}>Cancel</Button>
          <Button variant="primary" disabled={!valid} onClick={function () {
            onSubmit({ templateId: t ? t.id : null, name: name.trim(), domain: domain, minLevel: minLevel, amount: Number(amount), purpose: purpose.trim(), unrestricted: unrestricted });
          }}>Publish protocol</Button>
        </React.Fragment>
      }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--space-4)' }}>
        <Input label="Protocol name" value={name} onChange={function (e) { setName(e.target.value); }} />
        <div style={{ display: 'flex', gap: 'var(--space-4)', flexWrap: 'wrap' }}>
          <div style={{ flex: 1, minWidth: 150 }}>
            <Select label="Primary domain" options={[{ value: 'climate', label: 'Climate' }, { value: 'economic', label: 'Economic' }, { value: 'conflict', label: 'Conflict' }]} value={domain} onChange={function (e) { setDomain(e.target.value); }} />
          </div>
          <div style={{ flex: 1, minWidth: 150 }}>
            <Select label="Minimum trigger level" options={[{ value: 'watch', label: 'Watch' }, { value: 'warning', label: 'Warning' }, { value: 'severe', label: 'Severe' }]} value={minLevel} onChange={function (e) { setMinLevel(e.target.value); }} />
          </div>
        </div>
        <Input label="Committed amount (USD)" type="number" value={amount} onChange={function (e) { setAmount(e.target.value); }} />
        <Textarea label="Stated purpose" rows={3} value={purpose} maxLength={300} onChange={function (e) { setPurpose(e.target.value); }}
          hint="Villages report fund usage against this purpose." />
        <Switch checked={unrestricted} onChange={setUnrestricted} label="Unrestricted funding (enables auto-trigger with a review window)" />
      </div>
    </Modal>
  );
}

function MatchProtocolModal({ callId, onClose, onSubmit }) {
  const call = callId ? window.AsterStore.findCall(callId) : null;
  const [protocolId, setProtocolId] = React.useState('');
  const matching = call ? window.AsterStore.protocolsMatchingVillage(call.villageId) : [];

  React.useEffect(function () { setProtocolId(matching[0] ? matching[0].id : ''); }, [callId]);

  return (
    <Modal open={!!callId} onClose={onClose} title="Match to a protocol" width={460}
      subtitle="Link this general signal to one of your published protocols so it can be approved."
      footer={matching.length > 0 && (
        <React.Fragment>
          <Button variant="secondary" onClick={onClose}>Cancel</Button>
          <Button variant="primary" disabled={!protocolId} onClick={function () { onSubmit(callId, protocolId); }}>Match</Button>
        </React.Fragment>
      )}>
      {matching.length === 0 ? <EmptyState icon="file-x" text="No matching protocol for this village." /> : (
        <div role="radiogroup" aria-label="Matching protocols">
          {matching.map(function (p) {
            const selected = protocolId === p.id;
            return (
              <label key={p.id} className="ap-chip" style={{ display: 'flex', gap: 10, alignItems: 'flex-start', padding: '10px 12px', border: '1px solid ' + (selected ? 'var(--border-brand)' : 'var(--border-subtle)'), borderRadius: 'var(--radius-card)', marginBottom: 8, cursor: 'pointer', background: selected ? 'var(--surface-brand-soft)' : 'transparent' }}>
                <input type="radio" name="match-protocol" checked={selected} onChange={function () { setProtocolId(p.id); }} style={{ marginTop: 3 }} />
                <div style={{ flex: 1 }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', gap: 8 }}>
                    <span style={{ font: '600 12px var(--font-text)' }}>{p.name}</span>
                    <span style={{ font: '12px var(--font-mono)', color: 'var(--text-muted)' }}>{fmtUSD(p.amount)}</span>
                  </div>
                  {p.unrestricted && <Badge tone="brand" variant="soft" style={{ marginTop: 4 }}>Unrestricted funding</Badge>}
                </div>
              </label>
            );
          })}
        </div>
      )}
    </Modal>
  );
}
