{% extends "base.html" %}
{% import "macros/ui.html" as m %}

{% block title %}API Catalog — Ontoref{% endblock title %}
{% block nav_api %}active{% endblock nav_api %}
{% block nav_group_dev %}active{% endblock nav_group_dev %}

{% block head %}
<style>
  .method-get    { color: #4ade80; }
  .method-post   { color: #60a5fa; }
  .method-put    { color: #f59e0b; }
  .method-delete { color: #f87171; }
  .method-patch  { color: #c084fc; }
</style>
{% endblock head %}

{% block content %}
<div class="mb-6 flex items-center justify-between">
  <div>
    <h1 class="text-2xl font-bold">API Catalog</h1>
    {% if is_primary %}
    <p class="text-base-content/50 text-sm mt-1">Ontoref daemon HTTP surface — generated from <code>#[onto_api]</code> annotations</p>
    {% else %}
    <p class="text-base-content/50 text-sm mt-1">Project HTTP surface — routes declared in this project's ontology</p>
    {% endif %}
  </div>
  <span class="badge badge-lg badge-neutral">{{ route_count }} routes</span>
</div>

{% if route_count == 0 %}
<div class="flex flex-col items-center justify-center py-16 text-base-content/40 text-sm">
  <p>No API surface declared for <code class="font-mono">{{ slug }}</code>.</p>
  <p class="mt-2">Export an <code class="font-mono">api-catalog*.json</code> file to the project root to surface routes here.</p>
</div>
{% else %}

<!-- Filter bar -->
<div class="flex flex-wrap gap-2 mb-4" id="filter-bar">
  <input id="filter-input" type="text" placeholder="Filter by path or description…"
         class="input input-sm input-bordered flex-1 min-w-48 font-mono"
         oninput="filterRoutes()">
  <select id="filter-auth" class="select select-sm select-bordered" onchange="filterRoutes()">
    <option value="">All auth</option>
    <option value="none">none</option>
    <option value="viewer">viewer</option>
    <option value="admin">admin</option>
  </select>
  <select id="filter-method" class="select select-sm select-bordered" onchange="filterRoutes()">
    <option value="">All methods</option>
    <option value="GET">GET</option>
    <option value="POST">POST</option>
    <option value="PUT">PUT</option>
    <option value="DELETE">DELETE</option>
    <option value="PATCH">PATCH</option>
  </select>
  <span id="filter-count" class="text-xs text-base-content/40 self-center hidden"></span>
</div>

<!-- Catalog accordions -->
<div id="catalogs-container"></div>

<!-- Route detail modal -->
<dialog id="route-modal" class="modal">
  <div class="modal-box w-full max-w-2xl">
    <form method="dialog">
      <button class="btn btn-sm btn-ghost absolute right-3 top-3">✕</button>
    </form>
    <div class="flex items-baseline gap-2 mb-3 pr-8">
      <span id="detail-method" class="font-mono font-bold text-lg"></span>
      <span id="detail-path" class="font-mono text-base-content/60 text-sm break-all"></span>
    </div>
    <p id="detail-desc" class="text-sm text-base-content/80 mb-4"></p>
    <div id="detail-catalog-badge" class="mb-3 hidden">
      <span class="text-xs text-base-content/40">Catalog: </span>
      <span id="detail-catalog" class="badge badge-xs badge-ghost font-mono"></span>
    </div>
    <div id="detail-params" class="hidden mb-4">
      <h3 class="text-xs font-semibold uppercase tracking-wider text-base-content/50 mb-2">Parameters</h3>
      <table class="table table-xs w-full">
        <thead><tr class="text-base-content/40 text-xs"><th>Name</th><th>Type</th><th>Constraint</th><th>Description</th></tr></thead>
        <tbody id="detail-params-body"></tbody>
      </table>
    </div>
    <div id="detail-source" class="hidden mb-3">
      <h3 class="text-xs font-semibold uppercase tracking-wider text-base-content/50 mb-1">Source</h3>
      <button id="detail-source-btn"
              class="font-mono text-xs text-primary hover:underline underline-offset-2 bg-transparent border-none p-0 cursor-pointer text-left break-all"></button>
    </div>
    <div class="flex gap-4 text-xs text-base-content/40 border-t border-base-content/10 pt-3">
      <span>Feature: <code id="detail-feature" class="font-mono"></code></span>
      <span id="detail-auth-wrap">Auth: <span id="detail-auth"></span></span>
    </div>
  </div>
  <form method="dialog" class="modal-backdrop">
    <button>close</button>
  </form>
</dialog>

<script>
const CATALOGS  = {{ catalogs_json | safe }};
const MULTI     = CATALOGS.length > 1;
const CARD_REPO = "{{ card_repo | default(value='') | safe }}";
const CARD_DOCS = "{{ card_docs | default(value='') | safe }}";

function srcOpen(path) {
  if (!path) return;
  var ext = path.split('.').pop().toLowerCase();
  if (ext === 'rs' && CARD_DOCS) {
    window.open(CARD_DOCS.replace(/\/$/, ''), '_blank', 'noopener');
    return;
  }
  if (CARD_REPO) {
    window.open(CARD_REPO.replace(/\/$/, '') + '/src/branch/main/' + path, '_blank', 'noopener');
    return;
  }
}

// Flat array: each route carries _catalog and _flatIdx for showDetail lookup.
const ALL_ROUTES = [];
CATALOGS.forEach(function(cat) {
  (cat.routes || []).forEach(function(r) {
    ALL_ROUTES.push(Object.assign({}, r, { _catalog: cat.name }));
  });
});

let currentRoutes = ALL_ROUTES.slice();

function methodClass(m) {
  return 'method-' + m.toLowerCase();
}

function authBadge(auth) {
  const cls = {
    none:   'badge badge-ghost badge-xs font-mono',
    viewer: 'badge badge-info badge-xs font-mono',
    admin:  'badge badge-error badge-xs font-mono',
  }[auth] || 'badge badge-ghost badge-xs font-mono';
  return '<span class="' + cls + '">' + auth + '</span>';
}

function actorBadges(actors) {
  if (!actors || actors.length === 0) return '<span class="text-base-content/30">—</span>';
  return actors.map(function(a) {
    return '<span class="badge badge-xs badge-ghost font-mono">' + a + '</span>';
  }).join(' ');
}

function tagBadges(tags) {
  if (!tags || tags.length === 0) return '';
  return tags.map(function(t) {
    return '<span class="badge badge-xs badge-outline">' + t + '</span>';
  }).join(' ');
}

function routeRow(r, idx) {
  return '<tr class="hover cursor-pointer route-row" onclick="showDetail(' + idx + ')">' +
    '<td class="font-mono font-bold ' + methodClass(r.method) + '">' + r.method + '</td>' +
    '<td class="font-mono text-sm">' + r.path + '</td>' +
    '<td class="text-sm text-base-content/70">' + r.description + '</td>' +
    '<td>' + authBadge(r.auth) + '</td>' +
    '<td class="flex flex-wrap gap-1">' + actorBadges(r.actors) + '</td>' +
    '<td>' + tagBadges(r.tags) + '</td>' +
    '</tr>';
}

function tableFor(routes, startIdx) {
  if (routes.length === 0) return '';
  return '<div class="overflow-x-auto">' +
    '<table class="table table-sm w-full bg-base-200 rounded-lg">' +
    '<thead><tr class="text-base-content/50 text-xs uppercase tracking-wider">' +
    '<th class="w-16">Method</th><th>Path</th><th>Description</th>' +
    '<th class="w-20">Auth</th><th>Actors</th><th>Tags</th>' +
    '</tr></thead>' +
    '<tbody>' +
    routes.map(function(r, i) { return routeRow(r, startIdx + i); }).join('') +
    '</tbody></table></div>';
}

function renderRoutes(routes) {
  currentRoutes = routes;
  const container = document.getElementById('catalogs-container');
  const filterCount = document.getElementById('filter-count');
  const isFiltered = routes.length < ALL_ROUTES.length;

  if (isFiltered) {
    filterCount.textContent = routes.length + ' match' + (routes.length !== 1 ? 'es' : '');
    filterCount.classList.remove('hidden');
  } else {
    filterCount.classList.add('hidden');
  }

  if (!MULTI) {
    // Single catalog: plain table, no accordion.
    container.innerHTML = tableFor(routes, 0);
    return;
  }

  // Group by catalog, preserving flat index for showDetail.
  var groups = {};
  var order = [];
  routes.forEach(function(r, i) {
    if (!groups[r._catalog]) { groups[r._catalog] = []; order.push(r._catalog); }
    groups[r._catalog].push({ route: r, idx: i });
  });

  container.innerHTML = order.map(function(name) {
    var entries = groups[name];
    var rows = entries.map(function(e) { return routeRow(e.route, e.idx); }).join('');
    var count = entries.length;
    return '<details class="collapse collapse-arrow bg-base-200 mb-2" open>' +
      '<summary class="collapse-title font-mono font-semibold text-sm min-h-0 py-3">' +
        name +
        ' <span class="badge badge-xs badge-ghost ml-2">' + count + '</span>' +
      '</summary>' +
      '<div class="collapse-content px-0">' +
        '<div class="overflow-x-auto">' +
        '<table class="table table-sm w-full">' +
        '<thead><tr class="text-base-content/50 text-xs uppercase tracking-wider">' +
        '<th class="w-16">Method</th><th>Path</th><th>Description</th>' +
        '<th class="w-20">Auth</th><th>Actors</th><th>Tags</th>' +
        '</tr></thead>' +
        '<tbody>' + rows + '</tbody>' +
        '</table></div>' +
      '</div>' +
      '</details>';
  }).join('');
}

function showDetail(idx) {
  var r = currentRoutes[idx];
  if (!r) return;
  document.getElementById('detail-method').textContent = r.method;
  document.getElementById('detail-method').className = 'font-mono font-bold text-lg ' + methodClass(r.method);
  document.getElementById('detail-path').textContent = r.path;
  document.getElementById('detail-desc').textContent = r.description;
  document.getElementById('detail-feature').textContent = r.feature || 'default';
  document.getElementById('detail-auth').innerHTML = authBadge(r.auth);

  var catBadgeWrap = document.getElementById('detail-catalog-badge');
  if (MULTI) {
    document.getElementById('detail-catalog').textContent = r._catalog;
    catBadgeWrap.classList.remove('hidden');
  } else {
    catBadgeWrap.classList.add('hidden');
  }

  var paramsDiv = document.getElementById('detail-params');
  var tbody = document.getElementById('detail-params-body');
  if (r.params && r.params.length > 0) {
    tbody.innerHTML = r.params.map(function(p) {
      return '<tr>' +
        '<td class="font-mono text-xs">' + p.name + '</td>' +
        '<td class="text-xs text-base-content/60">' + (p.type || '') + '</td>' +
        '<td class="text-xs text-base-content/50">' + (p.constraint || '') + '</td>' +
        '<td class="text-xs">' + (p.description || '') + '</td>' +
        '</tr>';
    }).join('');
    paramsDiv.classList.remove('hidden');
  } else {
    paramsDiv.classList.add('hidden');
  }
  var sourceDiv = document.getElementById('detail-source');
  var sourceBtn = document.getElementById('detail-source-btn');
  if (r.source_file) {
    sourceBtn.textContent = r.source_file;
    sourceBtn.dataset.path = r.source_file;
    sourceDiv.classList.remove('hidden');
  } else {
    sourceDiv.classList.add('hidden');
  }

  document.getElementById('route-modal').showModal();
}

document.addEventListener('click', function(e) {
  var btn = e.target.closest('#detail-source-btn');
  if (btn && btn.dataset.path) { srcOpen(btn.dataset.path); }
});

function filterRoutes() {
  var text = document.getElementById('filter-input').value.toLowerCase();
  var auth = document.getElementById('filter-auth').value;
  var method = document.getElementById('filter-method').value;
  var filtered = ALL_ROUTES.filter(function(r) {
    var textMatch = !text || r.path.toLowerCase().includes(text) || r.description.toLowerCase().includes(text);
    var authMatch = !auth || r.auth === auth;
    var methodMatch = !method || r.method === method;
    return textMatch && authMatch && methodMatch;
  });
  renderRoutes(filtered);
}

renderRoutes(ALL_ROUTES);
</script>
{% endif %}
{% endblock content %}
