/* enterprise-demo.jsx — the single product demo on the public Globally page.
 *
 * ONE canvas, five stages: Organisation → Funding → Signals → Mira → Act.
 * The canvas persists and its contents transform; it is not a slideshow.
 *
 * WHAT MAKES IT REAL
 * ------------------
 * Funding records are the live corpus (funding-opportunities.json, normalised
 * into MX shape by MiraViz.normaliseOpp). Verdicts, reasons, concerns, missing
 * fields, readiness, pursuit state and next-best-action are ALL produced by the
 * authenticated product's own engine — window.MX.match / MX.readiness /
 * MX.pursuitState / MX.nextBestAction — not by a marketing re-implementation.
 * The verdict vocabulary is MX.ORDER. The eligibility rows, "why" breakdown and
 * pursuit chrome mirror OppView / PursuitView in enterprise-intel.jsx.
 *
 * Signals are real articles from the corpus the app already loaded: headline,
 * country, topic, source. The only interpretive line is why the geography and
 * programme area overlap the profile — which is exactly what MiraSignals
 * computes (relevance.overlaps) and is stated as an overlap, never as an
 * impact, severity or trend the sources do not support.
 *
 * WHAT IT DELIBERATELY DOES NOT DO
 * --------------------------------
 * It does not claim a signal re-scores a funding match — the real product does
 * not do that, so Mira is shown assembling the relevant items into one view.
 * It invents no amounts, deadlines, counts, clients or success rates. Where the
 * source publishes nothing, it says so, in the product's own words.
 *
 * The organisation profile is illustrative and labelled once, at the top of the
 * canvas. Its fields are exactly MX.org()'s fields.
 *
 * Exposes window.GloballyDemo.
 */
(function () {
  'use strict';
  var h = React.createElement;

  // The profile every later stage is judged against. Same field names as
  // MX.org(); values chosen so the real engine has something honest to say
  // about the real corpus — not so that it flatters the product.
  var DEMO_ORG = {
    name: 'Your organisation',
    _demo: true,
    types: ['International NGO', 'Consultancy'],
    countries: ['Bangladesh', 'Nepal', 'Pakistan', 'Sri Lanka'],
    sectors: ['Climate', 'Health', 'Education', 'Humanitarian'],
    typicalProjectGBP: 400000,
    preferredMinGBP: 25000,
    preferredMaxGBP: 5000000,
    yearsOperating: 18,
    consortiumCapable: true,
    leadCapable: true,
    coFinanceCapable: true,
    priorDonors: ['World Bank'],
    languages: ['English'],
  };

  var STAGES = [
    { id: 'org', label: 'Organisation' },
    { id: 'funding', label: 'Funding' },
    { id: 'signals', label: 'Signals' },
    { id: 'mira', label: 'Mira' },
    { id: 'act', label: 'Act' },
  ];
  var DWELL = 7000;

  function clip(s, n) {
    s = String(s || '');
    return s.length > n ? s.slice(0, n - 1).trim() + '…' : s;
  }

  // ── Small pieces of real product chrome ─────────────────────────────────
  // Verdict badge — MX.ORDER vocabulary, nothing invented.
  function Verdict(p) {
    var slug = String(p.value || '').toLowerCase().replace(/[^a-z]+/g, '-');
    return h('span', { className: 'gd-verdict gd-verdict--' + slug }, p.value);
  }

  // The Funding list row, matching enterprise-intel.jsx OppRow: title,
  // funder · type · place · closes, one confirmed reason, one concern.
  function OppRow(p) {
    var o = p.opp, m = p.match;
    var d = o.deadline ? window.MiraViz.daysUntil(o.deadline) : null;
    // The product's OppRow also prints o.type, but the corpus stores raw
    // pipeline slugs ("devbank") there — meaningless to a visitor, so the
    // public row shows funder and place only.
    var meta = [o.funder, (o.countries[0] || o.regions[0] || 'Global')]
      .filter(Boolean).join(' · ') + (d != null && d >= 0 ? ' · closes in ' + d + 'd' : '');
    return h('button', {
      className: 'gd-row' + (p.selected ? ' is-selected' : ''),
      onClick: p.onSelect, type: 'button',
    },
      h('span', { className: 'gd-row-main' },
        h('span', { className: 'gd-row-title' }, clip(o.title, 62)),
        h('span', { className: 'gd-row-meta' }, meta),
        m.reasons[0] ? h('span', { className: 'gd-row-reason' }, '✓ ' + m.reasons[0]) : null),
      h(Verdict, { value: m.overall }));
  }

  function KV(p) {
    return h('div', { className: 'gd-kv' },
      h('dt', null, p.k),
      h('dd', { className: p.muted ? 'is-muted' : '' }, p.v));
  }

  // ── Stage 1 — your organisation ─────────────────────────────────────────
  function StageOrg(p) {
    var V = window.MiraViz;
    var org = DEMO_ORG;
    var markers = V.countryMarkers(org.countries, 'home');
    return h('div', { className: 'gd-stage gd-stage--org' },
      h('div', { className: 'gd-pane' },
        h('p', { className: 'gd-pane-k' }, 'Organisation profile'),
        h('h4', { className: 'gd-pane-h' }, 'Mira starts with who you are.'),
        h('dl', { className: 'gd-kvs' },
          h(KV, { k: 'Operating countries', v: org.countries.join(', ') }),
          h(KV, { k: 'Programme areas', v: org.sectors.join(', ') }),
          h(KV, { k: 'Organisation type', v: org.types.join(' · ') }),
          h(KV, { k: 'Typical project size', v: '£' + V.num(org.typicalProjectGBP) }),
          h(KV, { k: 'Consortium capable', v: org.consortiumCapable ? 'Yes' : 'Not confirmed' })),
        h('p', { className: 'gd-note' },
          'Every opportunity, signal and briefing that follows is judged against ' +
          'this profile — which is why the result is short.')),
      h('div', { className: 'gd-pane gd-pane--map' },
        h(window.MiraViz.WorldMap, {
          markers: markers,
          label: 'The countries in the organisation profile: ' + org.countries.join(', '),
        }),
        h('div', { className: 'gd-maplegend' },
          h('span', { className: 'gd-lg gd-lg--home' }, 'Where you work'))));
  }

  // ── Stage 2 — find funding ──────────────────────────────────────────────
  function StageFunding(p) {
    var V = window.MiraViz;
    var intel = p.intel, score = p.score;
    var sel = p.selected, setSel = p.setSelected;
    if (!intel) return h('div', { className: 'gd-stage gd-loading' }, 'Reading the opportunity corpus…');
    if (!score) {
      return h('div', { className: 'gd-stage gd-loading' },
        'The matching engine is still loading.');
    }
    var strong = (score.groups['Strong candidate'] || []).length;
    var worth = (score.groups['Worth reviewing'] || []).length;
    var list = score.shortlist.slice(0, 5);
    var current = sel || list[0];

    var funnel = h('div', { className: 'gd-funnel' },
      h('div', { className: 'gd-funnel-step' },
        h('b', null, V.num(intel.total)),
        h('span', null, 'opportunities in the corpus'),
        h('em', null, intel.sourceCount + ' official sources · ' + intel.countryCount + ' countries')),
      h('div', { className: 'gd-funnel-arrow' }, '↓'),
      h('div', { className: 'gd-funnel-step' },
        h('b', null, DEMO_ORG.countries.length + ' countries'),
        h('span', null, 'matched against your profile'),
        h('em', null, DEMO_ORG.sectors.join(' · ') + ' · eligible applicant type')),
      h('div', { className: 'gd-funnel-arrow' }, '↓'),
      h('div', { className: 'gd-funnel-step is-out' },
        h('b', null, V.num(strong + worth)),
        h('span', null, 'worth your team’s time'),
        h('em', null, strong + ' strong candidates · ' + worth + ' worth reviewing')));

    var detail = current ? (function () {
      var o = current.opp, m = current.match;
      var order = ['geography', 'applicantType', 'sector', 'fundingSize', 'deadline'];
      return h('div', { className: 'gd-detail', key: o.id },
        h('div', { className: 'gd-detail-hd' },
          h('p', { className: 'gd-detail-funder' }, o.funder),
          h('h5', null, o.title),
          h(Verdict, { value: m.overall })),
        h('dl', { className: 'gd-kvs' },
          h(KV, { k: 'Location', v: o.countries.join(', ') || o.regions.join(', ') || 'Not published',
            muted: !o.countries.length && !o.regions.length }),
          h(KV, {
            k: 'Award amount',
            v: o.analyticalGBP != null ? '£' + V.num(o.analyticalGBP) : 'Not published in the source',
            muted: o.analyticalGBP == null,
          }),
          h(KV, {
            k: 'Deadline',
            v: o.deadline ? V.shortDate(o.deadline) : 'Not published in the source',
            muted: !o.deadline,
          }),
          h(KV, {
            k: 'Eligible applicants',
            v: o.eligible.join(', ') || 'Not published', muted: !o.eligible.length,
          })),
        h('p', { className: 'gd-detail-k' }, 'Why Mira ranked it here'),
        h('div', { className: 'gd-dims' }, order.map(function (k) {
          var dd = m.dims[k]; if (!dd) return null;
          return h('div', { className: 'gd-dim gd-dim--' + dd.verdict, key: k },
            h('b', null, k.replace(/([A-Z])/g, ' $1').replace(/^./, function (c) { return c.toUpperCase(); })),
            h('span', null, dd.note || dd.verdict));
        })),
        m.missing.length ? h('p', { className: 'gd-missing' },
          h('b', null, 'Still unpublished: '), m.missing.join(', ') + '.') : null,
        o.url ? h('p', { className: 'gd-source' },
          'Evidence: ', h('span', null, o.sourceName || o.funder), ' — every record links back to the funder’s own notice.') : null);
    })() : null;

    return h('div', { className: 'gd-stage gd-stage--funding' },
      h('div', { className: 'gd-pane' },
        funnel,
        h('div', { className: 'gd-rows' }, list.map(function (s) {
          return h(OppRow, {
            key: s.opp.id, opp: s.opp, match: s.match,
            selected: current && s.opp.id === current.opp.id,
            onSelect: function () { p.onInteract(); setSel(s); },
          });
        }))),
      h('div', { className: 'gd-pane gd-pane--detail' }, detail));
  }

  // ── Stage 3 — see what is changing ──────────────────────────────────────
  function StageSignals(p) {
    var V = window.MiraViz;
    var sigs = p.signals;
    var i = p.signalIndex;
    var a = sigs[i % Math.max(sigs.length, 1)];
    var markers = V.countryMarkers(DEMO_ORG.countries, 'home').map(function (m) {
      return a && m.label === a.country ? Object.assign({}, m, { tone: 'signal', active: true }) : m;
    });
    return h('div', { className: 'gd-stage gd-stage--signals' },
      h('div', { className: 'gd-pane gd-pane--map' },
        h(V.WorldMap, {
          markers: markers,
          label: 'Countries in the profile, with an active development in ' + ((a && a.country) || 'one of them'),
        }),
        h('div', { className: 'gd-maplegend' },
          h('span', { className: 'gd-lg gd-lg--home' }, 'Where you work'),
          h('span', { className: 'gd-lg gd-lg--signal' }, 'Something changed'))),
      a ? h('div', { className: 'gd-pane', key: a.id },
        h('p', { className: 'gd-pane-k' }, 'Signal'),
        h('h4', { className: 'gd-pane-h' }, a.headline),
        h('dl', { className: 'gd-kvs' },
          h(KV, { k: 'Country', v: a.country }),
          a.topic ? h(KV, { k: 'Topic', v: a.topic }) : null,
          h(KV, { k: 'Source', v: a.source || 'Not attributed', muted: !a.source })),
        h('p', { className: 'gd-detail-k' }, 'Why it may matter to you'),
        h('p', { className: 'gd-why' }, a.why),
        h('p', { className: 'gd-note' },
          'Mira flags a development only where it overlaps a country or programme ' +
          'area in your profile. It does not score severity the sources do not state.')) : null);
  }

  // ── Stage 4 — Mira brings the relevant information together ─────────────
  function StageMira(p) {
    var V = window.MiraViz;
    var top = p.score && p.score.shortlist[0];
    var sig = p.signals[p.signalIndex % Math.max(p.signals.length, 1)];
    var ctx = p.signals[(p.signalIndex + 1) % Math.max(p.signals.length, 1)];
    var inputs = [
      top ? { k: 'Funding', tone: 'funding', t: clip(top.opp.title, 54), m: top.opp.funder } : null,
      sig ? { k: 'Signal', tone: 'signal', t: clip(sig.headline, 54), m: sig.country } : null,
      ctx ? { k: 'Development context', tone: 'context', t: clip(ctx.headline, 54), m: ctx.country } : null,
    ].filter(Boolean);

    var lines = [];
    if (top) {
      lines.push({
        n: '01',
        t: top.match.overall + ' — ' + clip(top.opp.title, 66),
        s: top.match.reasons[0] ? top.match.reasons[0] + '.' : top.opp.funder + '.',
      });
    }
    if (sig) {
      lines.push({ n: '02', t: clip(sig.headline, 72), s: sig.country + (sig.topic ? ' · ' + sig.topic : '') + ' — overlaps your programme geography.' });
    }
    if (top && top.match.missing.length) {
      lines.push({
        n: '03',
        t: 'One thing to resolve before deciding',
        s: top.match.missing[0] + ' is not published in the source — confirm it from the official guidance.',
      });
    }

    return h('div', { className: 'gd-stage gd-stage--mira' },
      h('div', { className: 'gd-pane gd-pane--inputs' },
        h('p', { className: 'gd-pane-k' }, 'What Mira is reading'),
        h('div', { className: 'gd-inputs' }, inputs.map(function (it, k) {
          return h('div', { className: 'gd-input gd-input--' + it.tone, key: k },
            h('span', { className: 'gd-input-k' }, it.k),
            h('span', { className: 'gd-input-t' }, it.t),
            h('span', { className: 'gd-input-m' }, it.m));
        })),
        h('div', { className: 'gd-converge' }, h('span', null, 'Mira'))),
      h('div', { className: 'gd-pane' },
        h('p', { className: 'gd-pane-k' }, 'What your team should know'),
        h('div', { className: 'gd-brief' }, lines.map(function (l) {
          return h('div', { className: 'gd-brief-i', key: l.n },
            h('span', { className: 'gd-brief-n' }, l.n),
            h('div', null, h('h5', null, l.t), h('p', null, l.s)));
        })),
        h('p', { className: 'gd-note' },
          'Mira assembles what is relevant to your organisation into one view. ' +
          'It does not re-score a funding match because of a news development — ' +
          'eligibility comes from the funder’s own notice.')));
  }

  // ── Stage 5 — act ───────────────────────────────────────────────────────
  function StageAct(p) {
    var MX = window.MX;
    var top = p.score && p.score.shortlist[0];
    var added = p.added, setAdded = p.setAdded;
    if (!top || !MX) return h('div', { className: 'gd-stage gd-loading' }, 'Preparing…');
    var o = top.opp;
    // Exactly the record MX.store.createApp() builds when you add a pursuit.
    var app = {
      id: 'demo-pursuit', oppId: o.id, title: o.title, funder: o.funder,
      deadline: o.deadline, stage: 'Saved', assessed: true, tasks: [], notes: [],
    };
    var state = MX.pursuitState(app, o, DEMO_ORG);
    var nba = MX.nextBestAction(app, o, DEMO_ORG);
    var ready = MX.readiness(app, o, DEMO_ORG);
    var verified = ready.filter(function (r) { return r.status === 'verified'; }).length;

    return h('div', { className: 'gd-stage gd-stage--act' },
      h('div', { className: 'gd-pane' },
        h('p', { className: 'gd-pane-k' }, 'Act'),
        h('h4', { className: 'gd-pane-h' }, clip(o.title, 72)),
        h('p', { className: 'gd-detail-funder' }, o.funder),
        h('button', {
          type: 'button',
          className: 'gd-act-btn' + (added ? ' is-done' : ''),
          onClick: function () { p.onInteract(); setAdded(!added); },
        }, added ? '✓ In Pursuits' : 'Add to Pursuits'),
        h('p', { className: 'gd-note' },
          added
            ? 'It is now in Pursuits with everything Mira already knows about it — ' +
              'no re-entry, no separate tracker.'
            : 'One action moves an opportunity from discovery into your team’s pipeline.'),
        h('p', { className: 'gd-close' },
          h('span', null, 'Find funding.'),
          h('span', null, 'Understand change.'),
          h('span', null, 'Act earlier.'))),
      h('div', { className: 'gd-pane gd-pane--pursuit' },
        h('div', { className: 'gd-pursuit-hd' },
          h('span', { className: 'gd-state' }, state.state),
          h('span', { className: 'gd-state-src' }, state.source)),
        h('p', { className: 'gd-detail-k' }, 'Next best action'),
        h('p', { className: 'gd-nba' }, nba),
        h('p', { className: 'gd-detail-k' }, 'Readiness'),
        h('div', { className: 'gd-ready' }, ready.slice(0, 5).map(function (r, i) {
          return h('div', { className: 'gd-ready-i', key: i },
            h('span', { className: 'gd-ready-dot gd-ready-dot--' + r.status }),
            h('b', null, r.label),
            h('span', null, r.note));
        })),
        h('p', { className: 'gd-note' },
          verified + ' of ' + ready.length + ' components verified — the rest are named, ' +
          'not averaged into a score.')));
  }

  // ── The canvas ──────────────────────────────────────────────────────────
  function Demo(p) {
    var V = window.MiraViz;
    var intel = p.intel;
    var reduced = V.usePrefersReducedMotion();
    var ref = React.useRef(null);
    var near = V.useNearViewport(ref);

    var sS = React.useState(0); var stage = sS[0], setStage = sS[1];
    var sP = React.useState(false); var paused = sP[0], setPaused = sP[1];
    var sSel = React.useState(null); var selected = sSel[0], setSelected = sSel[1];
    var sSig = React.useState(0); var signalIndex = sSig[0], setSignalIndex = sSig[1];
    var sA = React.useState(false); var added = sA[0], setAdded = sA[1];

    function onInteract() { setPaused(true); }
    function goTo(i) { onInteract(); setStage(i); }

    // Score the real corpus with the real engine, once per corpus load.
    var score = React.useMemo(function () {
      return V.scoreCorpus(intel, DEMO_ORG);
    }, [intel]);

    // Real articles, reduced to the fields a Signal genuinely has. Only
    // developments in a profile country qualify; nothing is relocated to fit.
    var signals = React.useMemo(function () {
      var out = [], seen = {};
      (p.articles || []).forEach(function (a) {
        var c = a.country;
        if (!c || DEMO_ORG.countries.indexOf(c) === -1) return;
        if (seen[c]) return;
        var title = a.display_title || a.title;
        if (!title) return;
        seen[c] = 1;
        var topic = a.topic || a.category || (Array.isArray(a.topics) ? a.topics[0] : null) || null;
        out.push({
          id: a.id || title, headline: title, country: c, topic: topic,
          source: a.sourceName || a.source_name || a.source || null,
          why: c + ' is one of your operating countries' +
            (topic && DEMO_ORG.sectors.indexOf(topic) !== -1
              ? ', and ' + topic.toLowerCase() + ' is one of your programme areas.'
              : '. Mira surfaces it because your programmes are exposed to what happens there.'),
        });
      });
      return out;
    }, [p.articles]);

    var ready = !!(intel && score && signals.length);
    var playing = !reduced && !paused && near && ready;

    React.useEffect(function () {
      if (!playing) return;
      var t = setInterval(function () {
        setStage(function (s) {
          var next = (s + 1) % STAGES.length;
          if (next === 0) { setSelected(null); setAdded(false); setSignalIndex(function (n) { return n + 1; }); }
          return next;
        });
      }, DWELL);
      return function () { clearInterval(t); };
    }, [playing]);

    var body;
    if (!ready && stage !== 0) body = h('div', { className: 'gd-stage gd-loading' }, 'Loading the workspace…');
    else if (stage === 0) body = h(StageOrg, null);
    else if (stage === 1) body = h(StageFunding, { intel: intel, score: score, selected: selected, setSelected: setSelected, onInteract: onInteract });
    else if (stage === 2) body = h(StageSignals, { signals: signals, signalIndex: signalIndex });
    else if (stage === 3) body = h(StageMira, { score: score, signals: signals, signalIndex: signalIndex });
    else body = h(StageAct, { score: score, added: added, setAdded: setAdded, onInteract: onInteract });

    return h('div', { className: 'gd', ref: ref },
      h('div', { className: 'gd-bar' },
        h('div', { className: 'gd-tabs', role: 'tablist' }, STAGES.map(function (s, i) {
          return h('button', {
            key: s.id, type: 'button', role: 'tab', 'aria-selected': i === stage,
            className: 'gd-tab' + (i === stage ? ' is-on' : '') + (i < stage ? ' is-past' : ''),
            onClick: function () { goTo(i); },
          }, s.label);
        })),
        h('span', { className: 'gd-demo-tag' }, 'Demonstration')),
      h('div', { className: 'gd-progress' },
        h('i', { style: { width: ((stage + 1) / STAGES.length) * 100 + '%' } })),
      h('div', { className: 'gd-body' }, body));
  }

  window.GloballyDemo = { Demo: Demo, DEMO_ORG: DEMO_ORG, STAGES: STAGES };
})();
