// DbWorkspaceOverview.jsx · R47.1 (2026-05-28)
//
// Live-DB workspace overview · top-level "domain catalog" view.
// Self-mounts on ?screen=db-workspace&workspace_id=<id>. Reads from
// /api/v1/projects?workspace_id=... and renders each project as a clickable
// tile representing one MB-P domain.
//
//   ┌──────────────────────────────────────────────────────────────────────┐
//   │  ← cockpit                              workspace: Xlooop Internal   │
//   │  org_3EIO8YYcUTtjOQ9d1i7eh8mvBFv · 8 domains · 15 events             │
//   │                                                                      │
//   │  ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐│
//   │  │ MB-P governance     │ │ Unified intake      │ │ Private domains ││
//   │  │ Operate · on-track  │ │ Validate · on-track │ │ Validate · ...  ││
//   │  │ scope bound         │ │ no binding          │ │ no binding      ││
//   │  │ 7 matched events    │ │ 1 matched event     │ │ 1 matched event ││
//   │  │ [open ›]            │ │ [open ›]            │ │ [open ›]        ││
//   │  └─────────────────────┘ └─────────────────────┘ └─────────────────┘│
//   │  …                                                                   │
//   └──────────────────────────────────────────────────────────────────────┘
//
// Clicking a tile navigates to ?screen=db-project&project_id=<id> which
// opens the R47 single-project detail view.

(function () {
  'use strict';

  if (typeof window === 'undefined') return;
  if (window.__DbWorkspaceOverviewMounted) return;
  window.__DbWorkspaceOverviewMounted = true;

  var React = window.React;
  var ReactDOM = window.ReactDOM;
  if (!React || !ReactDOM) {
    window.addEventListener('xcp:data-ready', mount, { once: true });
    return;
  }
  mount();

  function mount() {
    var container = document.createElement('div');
    container.id = 'xcp-db-workspace-overview-portal';
    container.style.cssText = 'position:fixed;inset:0;z-index:99989;pointer-events:none;';
    document.body.appendChild(container);
    var root = ReactDOM.createRoot ? ReactDOM.createRoot(container) : null;
    if (!root) { try { ReactDOM.render(React.createElement(Gate), container); } catch (e) { void e; } return; }
    root.render(React.createElement(Gate));
  }

  function Gate() {
    var st = React.useState(parseUrl());
    var url = st[0]; var setUrl = st[1];
    React.useEffect(function () {
      function onChange() { setUrl(parseUrl()); }
      window.addEventListener('popstate', onChange);
      var iv = setInterval(onChange, 1500);
      return function () { window.removeEventListener('popstate', onChange); clearInterval(iv); };
    }, []);
    if (url.screen !== 'db-workspace') return null;
    if (!url.workspaceId) return el(EmptyState);
    return el(LiveDbWorkspaceView, { workspaceId: url.workspaceId, key: url.workspaceId });
  }

  function parseUrl() {
    try {
      var u = new URL(window.location.href);
      return {
        screen: u.searchParams.get('screen'),
        workspaceId: u.searchParams.get('workspace_id') || u.searchParams.get('ws'),
      };
    } catch (_) { return {}; }
  }

  function EmptyState() {
    return el('div', { style: emptyStyle() },
      el('div', { style: { maxWidth: 560, textAlign: 'center', padding: 32 } },
        el('h1', { style: titleH1Style() }, 'Live DB workspace overview'),
        el('p', null, 'Missing ', el('code', null, 'workspace_id'), ' query param.'),
        el('p', { style: { opacity: 0.5, fontSize: 13 } },
          'Try: ', el('code', null, '?screen=db-workspace&workspace_id=org_3EIO8YYcUTtjOQ9d1i7eh8mvBFv')),
        backLink(),
      ),
    );
  }

  function LiveDbWorkspaceView(props) {
    var workspaceId = props.workspaceId;
    var [projects, setProjects] = useState([]);
    var [loading, setLoading] = useState(true);
    var [error, setError] = useState(null);
    var [eventCounts, setEventCounts] = useState({});  // { projectId: count }
    var [session, setSession] = useState(null);

    React.useEffect(function () {
      var s = window.xcp && window.xcp.store && window.xcp.store.useSession
        ? window.xcp.store.useSession() : null;
      setSession(s);
    }, []);

    React.useEffect(function () {
      var cancelled = false;
      async function load() {
        try {
          setLoading(true);
          var headers = await authHeaders();
          var res = await fetch(apiBase() + '/api/v1/projects?workspace_id=' + encodeURIComponent(workspaceId), { headers: headers });
          if (!res.ok) {
            var body = await res.json().catch(function () { return null; });
            throw new Error((body && body.error) || ('HTTP ' + res.status));
          }
          var data = await res.json();
          if (cancelled) return;
          // R47.3 · only show ROOT projects as top-level tiles. Children
          // (parent_project_id !== null) are revealed inside the parent's
          // R47 detail view, not at the workspace level.
          var allRows = Array.isArray(data.projects) ? data.projects : [];
          var rootRows = allRows.filter(function (p) { return !p.parent_project_id; });
          setProjects(rootRows);
          setLoading(false);
          // After projects render, fan out fetches for event counts (parallel)
          rootRows.forEach(function (p) { fetchEventCount(p.id, headers); });
        } catch (e) {
          if (cancelled) return;
          setError(e && e.message ? e.message : String(e));
          setLoading(false);
        }
      }
      function fetchEventCount(projectId, headers) {
        fetch(apiBase() + '/api/v1/projects/' + encodeURIComponent(projectId) + '/events?limit=100', { headers: headers })
          .then(function (r) { return r.ok ? r.json() : { events: [] }; })
          .then(function (d) {
            if (cancelled) return;
            var count = Array.isArray(d.events) ? d.events.length : 0;
            setEventCounts(function (prev) { var n = Object.assign({}, prev); n[projectId] = count; return n; });
          })
          .catch(function () {});
      }
      load();
      return function () { cancelled = true; };
    }, [workspaceId]);

    return el('div', { style: shellStyle() },
      // top bar
      el('div', { style: topBarStyle() },
        backLink(),
        el('span', { style: { flex: 1 } }),
        el('span', { style: { opacity: 0.5, fontSize: 12 } }, 'LIVE DB workspace · ' + workspaceId.slice(0, 16) + '…'),
      ),
      el('div', { style: bodyStyle() },
        // Header
        el('div', { style: { marginBottom: 24 } },
          el('div', { style: { fontSize: 11, opacity: 0.5, fontFamily: 'JetBrains Mono, ui-monospace, monospace', letterSpacing: '0.06em', textTransform: 'uppercase' } },
            'WORKSPACE · /api/v1/projects?workspace_id=' + workspaceId.slice(0, 20) + '…'),
          el('h1', { style: titleH1Style() }, session && session.workspace_name ? session.workspace_name : 'Workspace overview'),
          el('div', { style: { opacity: 0.55, fontSize: 13, marginTop: 4 } },
            projects.length + ' domain' + (projects.length === 1 ? '' : 's') +
            ' · session role: ' + (session && session.role ? session.role : '—')),
        ),
        loading
          ? el('div', { style: { padding: 40, opacity: 0.5 } }, 'Loading domains from DB…')
          : error
            ? el('div', { style: errStyle() }, error)
            : projects.length === 0
              ? el('div', { style: { padding: 40, opacity: 0.6 } },
                  'No projects in this workspace yet. Seed via ',
                  el('code', null, 'src/workers/db/seed/operator-mbp-projects.sql'),
                  ' or use MCP ', el('code', null, 'xlooop.event.append'), ' to start.')
              : el('div', { style: tilesGridStyle() },
                  projects.map(function (p) {
                    return el(DomainTile, {
                      key: p.id,
                      project: p,
                      eventCount: eventCounts[p.id],
                    });
                  })),
        // Footer · helper
        el('div', { style: footerStyle() },
          'R47.1 live-DB workspace view · click any domain to open detail + configure scope binding · R48 will unify this with the main cockpit'),
      ),
    );
  }

  function DomainTile(props) {
    var p = props.project;
    var binding = p.scope_binding;
    var hasBinding = !!binding && Array.isArray(binding.filters) && binding.filters.length > 0;
    var stage = p.metadata && p.metadata.stage;
    var health = p.metadata && p.metadata.health;

    function open() {
      var u = new URL(window.location.href);
      u.searchParams.set('screen', 'db-project');
      u.searchParams.set('project_id', p.id);
      window.history.pushState({}, '', u.toString());
      window.dispatchEvent(new Event('popstate'));
    }

    return el('div', { style: tileStyle(), onClick: open, role: 'button', tabIndex: 0,
                       onKeyDown: function (e) { if (e.key === 'Enter') open(); } },
      el('div', { style: tileTopStyle() },
        el('span', { style: tileNameStyle() }, p.name),
        hasBinding ? el('span', { style: bindChip() }, '⛓ scoped') : el('span', { style: noBindChip() }, 'no binding'),
      ),
      p.description ? el('div', { style: tileDescStyle() }, p.description) : null,
      el('div', { style: tileMetaStyle() },
        stage ? el('span', { style: metaPill('stage', stage) }, stage) : null,
        health ? el('span', { style: metaPill('health', health) }, health) : null,
        el('span', { style: metaPill('status', p.status) }, p.status),
      ),
      el('div', { style: tileBottomStyle() },
        el('span', { style: countStyle() },
          props.eventCount === undefined ? '…' : props.eventCount + ' event' + (props.eventCount === 1 ? '' : 's')),
        el('span', { style: openArrow() }, 'open ›'),
      ),
    );
  }

  // Helpers
  var useState = React.useState;
  function el() { return React.createElement.apply(React, arguments); }
  function apiBase() { return window.XLOOOP_API_BASE_URL || 'https://api.xlooop.com'; }
  async function authHeaders() {
    var clerk = window.XcpClerk;
    if (!clerk || !clerk.instance || !clerk.instance.session) throw new Error('not signed in');
    var token = await clerk.getToken({ template: 'xlooop-workers' });
    if (!token) throw new Error('no token');
    return { Authorization: 'Bearer ' + token, 'Content-Type': 'application/json' };
  }
  function backLink() {
    return el('a', { href: '?', style: { color: '#7df0a8', textDecoration: 'none', fontSize: 13 } }, '← back to fixture cockpit');
  }

  // Styles
  function shellStyle() {
    return {
      position: 'fixed', inset: 0,
      background: '#0a0a0e', color: '#f0f0f4',
      fontFamily: 'Geist, ui-sans-serif, system-ui, sans-serif',
      overflowY: 'auto', pointerEvents: 'auto',
      zIndex: 99989,
    };
  }
  function topBarStyle() {
    return {
      display: 'flex', alignItems: 'center', gap: 12,
      padding: '10px 16px',
      borderBottom: '1px solid rgba(255,255,255,0.08)',
      background: 'rgba(14,14,18,0.96)',
      position: 'sticky', top: 0, zIndex: 2,
    };
  }
  function bodyStyle() { return { maxWidth: 1180, margin: '0 auto', padding: '20px 16px 80px' }; }
  function emptyStyle() { return Object.assign({}, shellStyle(), { display: 'flex', alignItems: 'center', justifyContent: 'center' }); }
  function titleH1Style() { return { fontSize: 28, margin: '4px 0', fontWeight: 600, letterSpacing: '-0.01em' }; }
  function tilesGridStyle() {
    return { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 14 };
  }
  function tileStyle() {
    return {
      background: 'rgba(18,18,22,0.95)',
      border: '1px solid rgba(255,255,255,0.08)',
      borderRadius: 10,
      padding: '14px 16px',
      cursor: 'pointer',
      transition: 'border-color 0.15s, transform 0.15s',
      display: 'flex', flexDirection: 'column',
      minHeight: 160,
    };
  }
  function tileTopStyle() { return { display: 'flex', alignItems: 'baseline', gap: 8, marginBottom: 6 }; }
  function tileNameStyle() { return { fontSize: 16, fontWeight: 600, flex: 1 }; }
  function tileDescStyle() { return { fontSize: 12, opacity: 0.55, marginBottom: 10, lineHeight: 1.45, flex: 1 }; }
  function tileMetaStyle() { return { display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 10 }; }
  function tileBottomStyle() { return { display: 'flex', alignItems: 'center', gap: 10, marginTop: 'auto', paddingTop: 8, borderTop: '1px solid rgba(255,255,255,0.05)' }; }
  function bindChip() { return { background: 'rgba(125,240,168,0.16)', color: '#7df0a8', padding: '2px 7px', borderRadius: 3, fontSize: 10, fontFamily: 'JetBrains Mono, ui-monospace, monospace' }; }
  function noBindChip() { return { background: 'rgba(180,180,180,0.10)', color: '#aaa', padding: '2px 7px', borderRadius: 3, fontSize: 10, fontFamily: 'JetBrains Mono, ui-monospace, monospace' }; }
  function metaPill(kind, label) {
    var bg = 'rgba(180,180,180,0.10)', fg = '#cdcdcd';
    if (kind === 'health') {
      if (label === 'on-track') { bg = 'rgba(125,240,168,0.13)'; fg = '#7df0a8'; }
      else if (label === 'watch') { bg = 'rgba(240,200,125,0.15)'; fg = '#f0c87d'; }
      else if (label === 'blocked' || label === 'risk') { bg = 'rgba(240,138,138,0.13)'; fg = '#f08a8a'; }
    }
    return { background: bg, color: fg, padding: '2px 8px', borderRadius: 3, fontSize: 10, fontFamily: 'JetBrains Mono, ui-monospace, monospace' };
  }
  function countStyle() { return { fontSize: 12, opacity: 0.75, fontFamily: 'JetBrains Mono, ui-monospace, monospace' }; }
  function openArrow() { return { marginLeft: 'auto', color: '#7df0a8', fontSize: 12 }; }
  function errStyle() { return { color: '#f08a8a', fontSize: 13, padding: '12px 14px', background: 'rgba(240,138,138,0.06)', border: '1px solid rgba(240,138,138,0.2)', borderRadius: 6 }; }
  function footerStyle() { return { marginTop: 24, padding: '14px 8px', opacity: 0.4, fontSize: 11, fontFamily: 'JetBrains Mono, ui-monospace, monospace', borderTop: '1px solid rgba(255,255,255,0.05)' }; }

  window.DbWorkspaceOverview = LiveDbWorkspaceView;
})();
