---
import Base from '../../layouts/Base.astro';
import DriftGate from '../../components/DriftGate.astro';
---

<Base title="Fleet Dashboard — Kredo Drift" description="Monitor all your AI agents' identity drift from a single dashboard. Live aura visualizations, drift scores, and trend tracking.">
  <meta slot="head" name="robots" content="noindex, nofollow" />
  <DriftGate />
  <div id="drift-content" style="display:none">

  <link rel="stylesheet" href="/css/aura-animations.css" />

  <div class="dashboard-page">

    <!-- Header -->
    <div class="dash-header">
      <div class="dash-header-top">
        <div>
          <h1>Fleet Dashboard</h1>
          <p class="dash-sub">Real-time identity drift monitoring across all registered agents.</p>
        </div>
        <div class="dash-header-actions">
          <a href="/drift/aura-compare" class="dash-link">Aura Visualizer</a>
        </div>
      </div>

      <!-- Stats bar -->
      <div class="stats-bar" id="stats-bar">
        <div class="stat-card">
          <span class="stat-value" id="stat-total">--</span>
          <span class="stat-label">Total Agents</span>
        </div>
        <div class="stat-card stat-healthy">
          <span class="stat-value" id="stat-healthy">--</span>
          <span class="stat-label">Healthy</span>
        </div>
        <div class="stat-card stat-shifting">
          <span class="stat-value" id="stat-shifting">--</span>
          <span class="stat-label">Shifting</span>
        </div>
        <div class="stat-card stat-drifting">
          <span class="stat-value" id="stat-drifting">--</span>
          <span class="stat-label">Drifting</span>
        </div>
        <div class="stat-card stat-critical">
          <span class="stat-value" id="stat-critical">--</span>
          <span class="stat-label">Critical</span>
        </div>
      </div>
    </div>

    <!-- Controls -->
    <div class="dash-controls">
      <div class="filter-buttons" id="filter-buttons">
        <button class="filter-btn active" data-filter="all">All</button>
        <button class="filter-btn" data-filter="healthy">Healthy</button>
        <button class="filter-btn" data-filter="shifting">Shifting</button>
        <button class="filter-btn" data-filter="drifting">Drifting</button>
        <button class="filter-btn" data-filter="critical">Critical</button>
      </div>
      <div class="sort-controls">
        <label class="sort-label" for="sort-select">Sort by</label>
        <select id="sort-select" class="sort-select">
          <option value="drift-desc">Drift (highest)</option>
          <option value="drift-asc">Drift (lowest)</option>
          <option value="name-asc">Name (A-Z)</option>
          <option value="name-desc">Name (Z-A)</option>
          <option value="updated">Last Updated</option>
        </select>
      </div>
    </div>

    <!-- Agent grid -->
    <div class="agent-grid" id="agent-grid">
      <div class="grid-loading">
        <div class="loading-spinner"></div>
        <p>Loading agents...</p>
      </div>
    </div>

    <!-- Empty state -->
    <div class="empty-state" id="empty-state" style="display:none;">
      <p>No agents found matching the current filter.</p>
    </div>

  </div>

  <!-- Tooltip element -->
  <div class="aura-tooltip" id="aura-tooltip"></div>

  </div>
</Base>

<style>
  .dashboard-page {
    max-width: 1200px;
    margin: 0 auto;
    padding: 2rem 1.5rem 4rem;
  }

  /* Header */
  .dash-header {
    margin-bottom: 2rem;
  }

  .dash-header-top {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    margin-bottom: 1.5rem;
  }

  .dash-header h1 {
    font-size: 1.8rem;
    font-weight: 600;
    color: var(--text-primary);
    margin: 0 0 0.35rem;
  }

  .dash-sub {
    color: var(--text-muted);
    font-size: 0.92rem;
    margin: 0;
  }

  .dash-header-actions {
    display: flex;
    gap: 0.75rem;
    flex-shrink: 0;
  }

  .dash-link {
    font-family: var(--font-mono);
    font-size: 0.8rem;
    padding: 0.5rem 1rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    color: var(--text-secondary);
    text-decoration: none;
    transition: border-color 0.2s, color 0.2s;
  }

  .dash-link:hover {
    border-color: var(--border-hover);
    color: var(--text-primary);
  }

  /* Stats bar */
  .stats-bar {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 0.75rem;
  }

  .stat-card {
    background: rgba(255,255,255,0.03);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1rem;
    text-align: center;
    transition: border-color 0.2s;
  }

  .stat-card:hover {
    border-color: var(--border-hover);
  }

  .stat-value {
    display: block;
    font-family: var(--font-mono);
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--text-primary);
    line-height: 1.2;
  }

  .stat-label {
    display: block;
    font-size: 0.72rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-top: 0.3rem;
  }

  .stat-healthy .stat-value { color: #22c55e; }
  .stat-shifting .stat-value { color: #eab308; }
  .stat-drifting .stat-value { color: #f97316; }
  .stat-critical .stat-value { color: #ef4444; }

  /* Controls */
  .dash-controls {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1.5rem;
    flex-wrap: wrap;
  }

  .filter-buttons {
    display: flex;
    gap: 0.4rem;
    flex-wrap: wrap;
  }

  .filter-btn {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    padding: 0.4rem 0.9rem;
    border: 1px solid var(--border);
    border-radius: 20px;
    background: transparent;
    color: var(--text-muted);
    cursor: pointer;
    transition: all 0.2s;
  }

  .filter-btn:hover {
    border-color: var(--border-hover);
    color: var(--text-secondary);
  }

  .filter-btn.active {
    border-color: var(--accent);
    color: var(--accent);
    background: rgba(99,102,241,0.08);
  }

  .sort-controls {
    display: flex;
    align-items: center;
    gap: 0.5rem;
  }

  .sort-label {
    font-size: 0.78rem;
    color: var(--text-muted);
  }

  .sort-select {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    padding: 0.4rem 0.7rem;
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    background: rgba(255,255,255,0.03);
    color: var(--text-secondary);
    cursor: pointer;
    outline: none;
  }

  .sort-select:focus {
    border-color: var(--border-hover);
  }

  .sort-select option {
    background: #0a0a14;
    color: var(--text-secondary);
  }

  /* WHY 320px min: smallest size that fits the aura SVG + pills without
     horizontal scroll — gives 3 columns on 1200px, 2 on tablet, 1 on mobile. */
  .agent-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 1.25rem;
  }

  /* Agent card */
  .agent-card {
    background: rgba(255,255,255,0.02);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 1.25rem;
    transition: border-color 0.2s, transform 0.2s;
    cursor: pointer;
    text-decoration: none;
    display: block;
    color: inherit;
  }

  .agent-card:hover {
    border-color: var(--border-hover);
    transform: translateY(-2px);
  }

  .agent-card-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 1rem;
    gap: 1rem;
  }

  .agent-card-drift-score {
    font-family: var(--font-mono);
    font-size: 1.8rem;
    font-weight: 800;
    display: block;
  }

  .agent-card-name {
    font-size: 1.4rem;
    font-weight: 600;
    color: var(--text-primary);
  }

  .agent-card-drift {
    font-family: var(--font-mono);
    font-size: 1.6rem;
    font-weight: 700;
  }

  .agent-card .viz-container {
    max-width: 280px;
    margin: 0 auto 0.5rem;
  }

  .agent-card-status {
    text-align: center;
    margin-bottom: 0.75rem;
    padding: 0.5rem 0;
  }

  .agent-card-class-lg {
    font-size: 1.3rem;
    font-weight: 700;
    text-transform: capitalize;
    display: block;
    margin-bottom: 0.2rem;
    letter-spacing: 0.02em;
  }

  .agent-card-meta {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.75rem;
    font-size: 0.8rem;
    color: var(--text-muted);
    margin-bottom: 0.75rem;
  }

  .agent-card-trend {
    font-size: 0.8rem;
  }

  .agent-card-pills {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    margin-top: 0.5rem;
  }

  .pill-row {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    flex-wrap: nowrap;
  }

  .agent-card-pills .pill {
    font-size: 0.72rem;
    padding: 0.15rem 0.4rem;
    white-space: nowrap;
    flex-shrink: 0;
  }

  .pill-tier-label {
    font-size: 0.6rem;
    color: var(--text-muted);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    padding: 0.1rem 0;
    opacity: 0.5;
    flex-shrink: 0;
    min-width: 2.5rem;
  }

  /* Loading state */
  .grid-loading {
    grid-column: 1 / -1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
    padding: 4rem 0;
    color: var(--text-muted);
  }

  .loading-spinner {
    width: 32px;
    height: 32px;
    border: 2px solid var(--border);
    border-top-color: var(--accent);
    border-radius: 50%;
    animation: spin 0.8s linear infinite;
  }

  @keyframes spin {
    to { transform: rotate(360deg); }
  }

  /* Empty state */
  .empty-state {
    text-align: center;
    padding: 3rem;
    color: var(--text-muted);
    font-size: 0.9rem;
  }

  /* Responsive */
  @media (max-width: 768px) {
    .dash-header-top {
      flex-direction: column;
    }

    .stats-bar {
      grid-template-columns: repeat(3, 1fr);
    }

    .dash-controls {
      flex-direction: column;
      align-items: flex-start;
    }

    .agent-grid {
      grid-template-columns: 1fr;
    }
  }

  @media (max-width: 480px) {
    .stats-bar {
      grid-template-columns: repeat(2, 1fr);
    }
  }
</style>

<script is:inline src="/js/aura-engine.js?v=20260325b"></script>
<script is:inline>
  var AE = window.AuraEngine;

  var allAgents = [];
  var currentFilter = 'all';
  var currentSort = 'drift-desc';

  function classifyAgent(driftScore) {
    if (driftScore > 60) return 'critical';
    if (driftScore > 35) return 'drifting';
    if (driftScore > 15) return 'shifting';
    return 'healthy';
  }

  function getDriftColor(driftScore) {
    if (driftScore > 60) return '#ef4444';
    if (driftScore > 35) return '#f97316';
    if (driftScore > 15) return '#eab308';
    return '#22c55e';
  }

  function getTrendArrow(history) {
    if (!history || history.length < 2) return '';
    var latest = history[history.length - 1].score;
    var prev = history[history.length - 2].score;
    var diff = latest - prev;
    if (Math.abs(diff) < 1) return '<span style="color:var(--text-muted);">--</span>';
    if (diff > 0) return '<span style="color:#ef4444;">\u2191 ' + diff.toFixed(1) + '</span>';
    return '<span style="color:#22c55e;">\u2193 ' + Math.abs(diff).toFixed(1) + '</span>';
  }

  function updateStats() {
    var total = allAgents.length;
    var healthy = 0, shifting = 0, drifting = 0, critical = 0;
    allAgents.forEach(function(a) {
      var cat = classifyAgent(a.data.overall_drift || 0);
      if (cat === 'healthy') healthy++;
      else if (cat === 'shifting') shifting++;
      else if (cat === 'drifting') drifting++;
      else critical++;
    });
    document.getElementById('stat-total').textContent = total;
    document.getElementById('stat-healthy').textContent = healthy;
    document.getElementById('stat-shifting').textContent = shifting;
    document.getElementById('stat-drifting').textContent = drifting;
    document.getElementById('stat-critical').textContent = critical;
  }

  function sortAgents(agents) {
    var sorted = agents.slice();
    switch (currentSort) {
      case 'drift-desc':
        sorted.sort(function(a,b) { return (b.data.overall_drift || 0) - (a.data.overall_drift || 0); });
        break;
      case 'drift-asc':
        sorted.sort(function(a,b) { return (a.data.overall_drift || 0) - (b.data.overall_drift || 0); });
        break;
      case 'name-asc':
        sorted.sort(function(a,b) { return a.name.localeCompare(b.name); });
        break;
      case 'name-desc':
        sorted.sort(function(a,b) { return b.name.localeCompare(a.name); });
        break;
      case 'updated':
        sorted.sort(function(a,b) {
          var ta = a.data.updated ? new Date(a.data.updated).getTime() : 0;
          var tb = b.data.updated ? new Date(b.data.updated).getTime() : 0;
          return tb - ta;
        });
        break;
    }
    return sorted;
  }

  function filterAgents(agents) {
    if (currentFilter === 'all') return agents;
    return agents.filter(function(a) {
      return classifyAgent(a.data.overall_drift || 0) === currentFilter;
    });
  }

  /**
   * Build tier-grouped pill HTML for an agent's scores.
   * WHY grouped pills: with 14 dimensions, a flat list of pills is
   * visual noise. Grouping by Identity Core / Cognitive Profile lets
   * the operator scan structural health separately from cognitive style.
   */
  function buildPillRow(dims, scores, drift) {
    var html = '';
    dims.forEach(function(dim) {
      if (scores[dim] == null) return;
      var score = scores[dim];
      var driftVal = drift[dim] || 0;
      var color = AE.COLORS[dim] || '#888';
      var abbr = AE.ABBREV[dim] || dim;
      var pillCls = '';
      if (driftVal > 25) pillCls = 'danger';
      else if (driftVal > 15) pillCls = 'warn';
      html += '<span class="pill ' + pillCls + '" style="--pill-color:' + color + '" title="' + dim + ': ' + score + '">' + abbr + ' ' + Math.round(score) + '</span>';
    });
    return html;
  }

  function buildPillsHtml(scores, drift) {
    var coreHtml = buildPillRow(AE.INNER_RING, scores, drift);
    var cogHtml = buildPillRow(AE.OUTER_RING, scores, drift);
    var html = '';
    if (coreHtml) {
      html += '<div class="pill-row"><span class="pill-tier-label">Core</span>' + coreHtml + '</div>';
    }
    if (cogHtml) {
      html += '<div class="pill-row"><span class="pill-tier-label">Cog</span>' + cogHtml + '</div>';
    }
    return html;
  }

  function renderGrid() {
    var grid = document.getElementById('agent-grid');
    var emptyState = document.getElementById('empty-state');
    if (!grid) return;

    var filtered = filterAgents(allAgents);
    var sorted = sortAgents(filtered);

    grid.innerHTML = '';

    if (sorted.length === 0) {
      emptyState.style.display = '';
      return;
    }
    emptyState.style.display = 'none';

    sorted.forEach(function(agentEntry) {
      var name = agentEntry.name;
      var data = agentEntry.data;

      var scores = Object.assign({}, data.scores);
      var drift = Object.assign({}, data.drift);
      if (data.fidelity_score != null) {
        scores['Fidelity'] = Math.round((100 - data.fidelity_score) * 10) / 10;
        drift['Fidelity'] = Math.round(data.fidelity_score * 10) / 10;
      }

      var baseline = {};
      AE.DIMENSIONS.forEach(function(dim) {
        if (scores[dim] != null && drift[dim] != null) {
          baseline[dim] = Math.min(100, scores[dim] + drift[dim]);
        }
      });

      var agent = {
        name: data.agent || name,
        scores: scores,
        links: data.links || [],
        drift: drift,
        baseline: Object.keys(baseline).length > 0 ? baseline : undefined,
      };

      var driftScore = data.overall_drift || 0;
      var driftColor = getDriftColor(driftScore);
      var cls = (data.classification || 'stable').replace(/_/g, ' ');
      var clsLabel = cls.charAt(0).toUpperCase() + cls.slice(1);
      var timestamp = data.updated ? AE.relativeTime(data.updated) : '';
      var trendHtml = getTrendArrow(data.history);

      var pillsHtml = buildPillsHtml(scores, drift);

      var vizId = 'dash-aura-' + name;

      var card = document.createElement('a');
      card.className = 'agent-card';
      card.href = '/drift/agent/?id=' + encodeURIComponent(name);

      card.innerHTML =
        '<div class="agent-card-header">' +
          '<span class="agent-card-name">' + agent.name + '</span>' +
        '</div>' +
        '<div class="viz-container" id="' + vizId + '"></div>' +
        '<div class="agent-card-status">' +
          '<span class="agent-card-class-lg" style="color:' + driftColor + '">' + clsLabel + '</span>' +
          '<span class="agent-card-drift-score" style="color:' + driftColor + '">' + driftScore.toFixed(1) + '</span>' +
        '</div>' +
        '<div class="agent-card-meta">' +
          '<span>' + timestamp + '</span>' +
          '<span>' + (data.days_monitored || 1) + ' days monitored</span>' +
          '<span class="agent-card-trend">' + trendHtml + '</span>' +
        '</div>' +
        '<div class="agent-card-pills">' + pillsHtml + '</div>';

      grid.appendChild(card);

      // WHY setTimeout: DOM element must be mounted before renderEnergy can
      // measure it and inject the SVG. 10ms is enough for the layout pass.
      setTimeout(function() { AE.renderEnergy(vizId, agent); }, 10);
    });

    setTimeout(function() { AE.setupTooltips(); }, 100);
  }

  // Filter buttons
  document.getElementById('filter-buttons').addEventListener('click', function(e) {
    var btn = e.target.closest('.filter-btn');
    if (!btn) return;
    currentFilter = btn.getAttribute('data-filter');
    document.querySelectorAll('.filter-btn').forEach(function(b) { b.classList.remove('active'); });
    btn.classList.add('active');
    renderGrid();
  });

  // Sort dropdown
  document.getElementById('sort-select').addEventListener('change', function(e) {
    currentSort = e.target.value;
    renderGrid();
  });

  // WHY live API instead of static JSON: agents register dynamically via the
  // drift API. Static files required manual updates and never showed new agents.
  // WHY public: aura scores and classifications contain no secrets — making the
  // dashboard public lets any agent verify their identity is registered.
  var API_BASE = 'https://api.aikredo.com';

  function transformAgent(a) {
    var daysMonitored = 1;
    if (a.baseline_at) {
      daysMonitored = Math.max(1, Math.round((Date.now() - new Date(a.baseline_at).getTime()) / 86400000));
    }
    return {
      name: a.name,
      data: {
        agent: a.name,
        overall_drift: a.score || 0,
        scores: a.scores || {},
        drift: a.drift || {},
        classification: a.classification || 'stable',
        updated: a.tested_at || a.baseline_at || a.created_at,
        days_monitored: daysMonitored,
        links: [],
        history: null,
      }
    };
  }

  function loadDashboard() {
    fetch(API_BASE + '/api/drift/agents/auras')
      .then(function(res) {
        if (!res.ok) throw new Error('API error: ' + res.status);
        return res.json();
      })
      .then(function(body) {
        var agents = (body.agents || [])
          .filter(function(a) { return a.status !== 'pending'; })
          .map(transformAgent);

        if (agents.length === 0) {
          var grid = document.getElementById('agent-grid');
          if (grid) grid.innerHTML = '<div class="grid-loading"><p>No agents with completed baselines yet. <a href="/drift/register" style="color:var(--accent)">Register your agent</a> to get started.</p></div>';
          return;
        }
        allAgents = agents;
        updateStats();
        renderGrid();
      })
      .catch(function(err) {
        var grid = document.getElementById('agent-grid');
        if (grid) grid.innerHTML = '<div class="grid-loading"><p>' + (err.message || 'Failed to load agent data.') + '</p></div>';
      });
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', loadDashboard);
  } else {
    loadDashboard();
  }
</script>
