<?php
/**
 * Issues admin page — unified view of all AI-monitor findings.
 * Root only.
 */
if (!defined('AUTH_USER') || AUTH_USER !== 'root') {
    http_response_code(403);
    echo '<div class="alert alert-danger">Admin only.</div>';
    return;
}
$csrf_token = $_SESSION['csrf_token'] ?? '';
?>
<div class="container-fluid">
  <div class="d-flex justify-content-between align-items-center mb-3">
    <h2><i class="fa fa-exclamation-triangle"></i> Issues</h2>
    <button id="toggle-suppressions" class="btn btn-outline-secondary btn-sm">
      <i class="fa fa-ban"></i> Suppressions
    </button>
  </div>

  <!-- Summary -->
  <div class="row g-3 mb-3">
    <div class="col-md-3"><div class="card"><div class="card-body">
      <div class="text-muted small">Critical</div>
      <div class="h3 text-danger" id="sum-critical">—</div></div></div></div>
    <div class="col-md-3"><div class="card"><div class="card-body">
      <div class="text-muted small">Warning</div>
      <div class="h3 text-warning" id="sum-warning">—</div></div></div></div>
    <div class="col-md-3"><div class="card"><div class="card-body">
      <div class="text-muted small">Auto-resolved (review)</div>
      <div class="h3 text-info" id="sum-auto">—</div></div></div></div>
    <div class="col-md-3"><div class="card"><div class="card-body">
      <div class="text-muted small">Active suppressions</div>
      <div class="h3" id="sum-suppressions">—</div></div></div></div>
  </div>

  <!-- Filters -->
  <form id="filter-form" class="row g-2 align-items-end mb-3">
    <div class="col-auto">
      <label class="form-label small">Scope</label>
      <select name="scope" class="form-select form-select-sm">
        <option value="all">All</option>
        <option value="site">Site issues</option>
        <option value="server">Server-wide</option>
      </select>
    </div>
    <div class="col-auto">
      <label class="form-label small">Status</label>
      <select name="status" class="form-select form-select-sm">
        <option value="open" selected>Open</option>
        <option value="fixed">Fixed</option>
        <option value="ignored">Ignored</option>
        <option value="all">All</option>
      </select>
    </div>
    <div class="col-auto">
      <label class="form-label small">Severity</label>
      <select name="severity" class="form-select form-select-sm">
        <option value="">Any</option>
        <option value="critical">Critical</option>
        <option value="warning">Warning</option>
        <option value="info">Info</option>
      </select>
    </div>
    <div class="col-auto">
      <label class="form-label small">Source</label>
      <select name="source" class="form-select form-select-sm">
        <option value="">Any</option>
        <option value="logs">Logs</option>
        <option value="haproxy_stats">HAProxy stats</option>
        <option value="health_api">Health API</option>
      </select>
    </div>
    <div class="col-auto">
      <label class="form-label small">Signature prefix</label>
      <input name="signature" class="form-control form-control-sm" style="width:14rem"
             placeholder="first 8+ chars">
    </div>
    <div class="col-auto">
      <button class="btn btn-primary btn-sm" type="submit"><i class="fa fa-search"></i> Filter</button>
    </div>
  </form>

  <!-- Issues table -->
  <div id="issues-wrap" class="card">
    <div class="card-body">
      <div class="d-flex justify-content-between align-items-center mb-2 flex-wrap gap-2">
        <div class="d-flex align-items-center gap-2">
          <span id="bulk-count" class="text-muted small">No selection</span>
          <button id="bulk-fix" class="btn btn-success btn-sm" disabled>
            <i class="fa fa-check"></i> Mark Fixed
          </button>
          <button id="bulk-ignore" class="btn btn-warning btn-sm" disabled>
            <i class="fa fa-ban"></i> Ignore
          </button>
          <button id="bulk-delete" class="btn btn-outline-danger btn-sm" disabled>
            <i class="fa fa-trash-o"></i> Delete
          </button>
        </div>
        <div id="pagination-info" class="text-muted small"></div>
      </div>
      <div class="table-responsive">
        <table class="table table-sm table-hover">
          <thead>
            <tr>
              <th style="width:2em"><input type="checkbox" id="chk-all"></th>
              <th>Severity</th>
              <th>Category</th>
              <th>Title</th>
              <th>Site</th>
              <th>Count</th>
              <th>Last seen</th>
              <th>Status</th>
              <th></th>
            </tr>
          </thead>
          <tbody id="issues-body">
            <tr><td colspan="9" class="text-muted">Loading…</td></tr>
          </tbody>
        </table>
      </div>
      <nav><ul class="pagination pagination-sm" id="pagination"></ul></nav>
    </div>
  </div>

  <!-- Suppressions sub-view -->
  <div id="suppressions-wrap" class="card mt-3" style="display:none">
    <div class="card-header"><strong>Active suppressions</strong></div>
    <div class="card-body">
      <table class="table table-sm">
        <thead>
          <tr>
            <th>Issue</th><th>Site</th><th>Scope</th><th>By</th><th>At</th>
            <th>Suppressed count</th><th>Last suppressed</th><th></th>
          </tr>
        </thead>
        <tbody id="suppressions-body"><tr><td colspan="8" class="text-muted">Loading…</td></tr></tbody>
      </table>
    </div>
  </div>
</div>

<!-- Detail modal -->
<div class="modal fade" id="issue-modal" tabindex="-1">
  <div class="modal-dialog modal-lg"><div class="modal-content">
    <div class="modal-header">
      <h5 class="modal-title" id="modal-title">Issue</h5>
      <button class="btn-close" data-bs-dismiss="modal"></button>
    </div>
    <div class="modal-body" id="modal-body">…</div>
    <div class="modal-footer" id="modal-footer"></div>
  </div></div>
</div>

<script>
// jQuery and Bootstrap are loaded by the outer index.php layout AFTER page
// content, so defer init until the window is fully loaded.
window.addEventListener('load', function() {
(function(){
  const API = '/api/issues.php';
  const CSRF_TOKEN = <?php echo json_encode($csrf_token); ?>;
  let currentPage = 1;
  let selected = new Set();

  function render() { loadIssues(); }

  function loadIssues() {
    const f = $('#filter-form').serialize();
    const url = `${API}?action=list&page=${currentPage}&${f}`;
    $.ajax({url, dataType: 'json'})
      .done((r) => {
        if (!r || !r.success) {
          const msg = (r && r.error) ? r.error : 'unknown';
          $('#issues-body').html(`<tr><td colspan="9" class="text-danger">Load failed: ${escapeHtml(msg)}</td></tr>`);
          return;
        }
        const sum = r.summary || {};
        $('#sum-critical').text(sum.critical || 0);
        $('#sum-warning').text(sum.warning || 0);
        $('#sum-auto').text(sum.auto_resolved || 0);
        $('#sum-suppressions').text(sum.suppressions || 0);
        const body = $('#issues-body').empty();
        selected.clear();
        updateBulkButtons();
        if (!r.rows || r.rows.length === 0) {
          body.append('<tr><td colspan="9" class="text-muted">No issues.</td></tr>');
        } else {
          r.rows.forEach(row => body.append(renderRow(row)));
        }
        renderPagination(r.total || 0, r.page || 1, r.per_page || 25);
        $('#pagination-info').text(`${r.total || 0} issue(s) total`);
      })
      .fail((xhr, textStatus, err) => {
        const detail = xhr.responseText ? xhr.responseText.slice(0, 300) : (err || textStatus);
        console.error('issues list failed:', xhr.status, detail);
        $('#issues-body').html(
          `<tr><td colspan="9" class="text-danger">API request failed — HTTP ${xhr.status}. ${escapeHtml(detail)}</td></tr>`);
      });
  }

  function renderRow(i) {
    const sev = i.severity === 'critical' ? 'danger' : (i.severity === 'warning' ? 'warning' : 'info');
    const site = i.site_id ? (i.primary_domain || `site ${i.site_id}`) : '<em class="text-muted">Server-wide</em>';
    const auto = i.auto_resolved_at
      ? ` <span class="badge bg-info" title="System observed recovery at ${escapeHtml(i.auto_resolved_at)}">auto-resolved</span>` : '';
    return `<tr data-id="${i.id}">
      <td><input type="checkbox" class="chk-row" value="${i.id}"></td>
      <td><span class="badge bg-${sev}">${escapeHtml(i.severity)}</span></td>
      <td>${escapeHtml(i.category)}${i.subsystem ? ' / ' + escapeHtml(i.subsystem) : ''}</td>
      <td><a href="#" class="row-title">${escapeHtml(i.title || '')}</a>${auto}</td>
      <td>${site}</td>
      <td>${escapeHtml(i.occurrence_count)}</td>
      <td>${escapeHtml(i.last_seen)}</td>
      <td><span class="badge bg-secondary">${escapeHtml(i.status)}</span></td>
      <td>
        <button class="btn btn-xs btn-success act-fix" title="Mark this issue as fixed">Fixed</button>
        <button class="btn btn-xs btn-warning act-ignore">Ignore</button>
      </td>
    </tr>`;
  }

  function renderPagination(total, page, per) {
    const pages = Math.ceil(total / per);
    const ul = $('#pagination').empty();
    for (let p = 1; p <= pages; p++) {
      ul.append(`<li class="page-item${p===page?' active':''}">
        <a class="page-link" href="#" data-page="${p}">${p}</a></li>`);
    }
  }

  function showDetail(id) {
    $.getJSON(`${API}?action=get&id=${id}`, (r) => {
      if (!r.success) return alert('Not found');
      const i = r.row;
      $('#modal-title').text(i.title || `Issue ${i.id}`);
      const site = i.site_id ? (i.primary_domain || `site ${i.site_id}`) : 'Server-wide';
      // Stale-relevance check: if the AI-extracted evidence_observed_at is
      // more than a week old, the issue may be from before a site change
      // (rebuild, CMS swap, etc.) and not actually relevant anymore.
      let staleBanner = '';
      if (i.evidence_observed_at) {
        const observedTs = Date.parse(i.evidence_observed_at.replace(' ', 'T'));
        if (!isNaN(observedTs)) {
          const ageDays = Math.floor((Date.now() - observedTs) / 86400000);
          if (ageDays >= 7) {
            staleBanner = `<div class="alert alert-warning small">
              <i class="fa fa-clock-o"></i>
              <strong>Possibly stale.</strong>
              The supporting log line is from ${ageDays} day(s) ago — verify the issue is still happening before acting.
            </div>`;
          }
        }
      }
      $('#modal-body').html(`
        <dl class="row small">
          <dt class="col-sm-3">Severity</dt><dd class="col-sm-9">${escapeHtml(i.severity)}</dd>
          <dt class="col-sm-3">Category</dt><dd class="col-sm-9">${escapeHtml(i.category)}${i.subsystem?' / '+escapeHtml(i.subsystem):''}</dd>
          <dt class="col-sm-3">Site</dt><dd class="col-sm-9">${site}</dd>
          <dt class="col-sm-3">Source(s)</dt><dd class="col-sm-9">${escapeHtml(i.sources || i.source)}</dd>
          <dt class="col-sm-3">Signature</dt><dd class="col-sm-9"><code>${escapeHtml(i.signature)}</code></dd>
          <dt class="col-sm-3">First seen</dt><dd class="col-sm-9">${escapeHtml(i.first_seen)}</dd>
          <dt class="col-sm-3">Last seen</dt><dd class="col-sm-9">${escapeHtml(i.last_seen)}</dd>
          ${i.evidence_observed_at ? `<dt class="col-sm-3">Log line timestamp</dt><dd class="col-sm-9">${escapeHtml(i.evidence_observed_at)}</dd>` : ''}
          <dt class="col-sm-3">Status</dt><dd class="col-sm-9">${escapeHtml(i.status)}</dd>
          ${i.auto_resolved_at ? `<dt class="col-sm-3">Auto-resolved at</dt><dd class="col-sm-9">${escapeHtml(i.auto_resolved_at)}</dd>` : ''}
        </dl>
        ${staleBanner}
        <h6>Diagnosis</h6><pre class="small">${escapeHtml(i.diagnosis || '')}</pre>
        ${i.evidence ? `<h6><i class="fa fa-quote-left"></i> Evidence (verbatim log line)</h6><pre class="small bg-dark text-light p-2 rounded" style="white-space: pre-wrap;">${escapeHtml(i.evidence)}</pre>` : ''}
        ${i.recommendation ? `<h6>Recommendation</h6><pre class="small">${escapeHtml(i.recommendation)}</pre>` : ''}
        ${i.raw_log_excerpt ? `<details><summary>Raw log excerpt</summary><pre class="small">${escapeHtml(i.raw_log_excerpt)}</pre></details>` : ''}
        ${i.context_data ? `<details><summary>Context snapshot</summary><pre class="small">${escapeHtml(i.context_data)}</pre></details>` : ''}
        <div id="modal-explain-wrap" class="mt-3 ${i.user_explanation ? '' : 'd-none'}">
          <h6><i class="fa fa-comments-o"></i> Explanation</h6>
          <div id="modal-explain" class="p-3 rounded border" style="white-space: pre-wrap; line-height: 1.5; background: var(--bs-tertiary-bg, rgba(128,128,128,0.08)); color: var(--bs-body-color);">${escapeHtml(i.user_explanation || '')}</div>
          <small class="text-muted d-block mt-1" id="modal-explain-meta">${i.user_explanation_at ? 'Generated ' + escapeHtml(i.user_explanation_at) + (i.user_explanation_model ? ' · ' + escapeHtml(i.user_explanation_model) : '') : ''}</small>
        </div>
      `);
      $('#modal-footer').html(`
        <button class="btn btn-outline-info me-auto" data-explain="${i.id}"><i class="fa fa-comments-o"></i> Explain this to me</button>
        <button class="btn btn-success" data-fix="${i.id}">Mark Fixed</button>
        <button class="btn btn-warning" data-ignore="${i.id}">Ignore…</button>
        <button class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
      `);
      new bootstrap.Modal(document.getElementById('issue-modal')).show();
    });
  }

  function loadSuppressions() {
    $.getJSON(`${API}?action=list_suppressions`, (r) => {
      const body = $('#suppressions-body').empty();
      if (!r.success || r.rows.length === 0) {
        body.append('<tr><td colspan="8" class="text-muted">None.</td></tr>');
        return;
      }
      r.rows.forEach(s => {
        const title = s.issue_title
          ? escapeHtml(s.issue_title)
          : `<code title="signature">${escapeHtml((s.signature || '').slice(0,12))}…</code>`;
        const category = s.issue_category
          ? `<br><small class="text-muted">${escapeHtml(s.issue_category)}</small>`
          : '';
        body.append(`<tr>
          <td>${title}${category}</td>
          <td>${s.primary_domain ? escapeHtml(s.primary_domain) : '<em>Server-wide</em>'}</td>
          <td>${escapeHtml(s.scope)}</td>
          <td>${escapeHtml(s.suppressed_by_user)}</td>
          <td>${escapeHtml(s.suppressed_at)}</td>
          <td>${escapeHtml(s.suppressed_occurrence_count)}</td>
          <td>${s.last_suppressed_event_at ? escapeHtml(s.last_suppressed_event_at) : '—'}</td>
          <td><button class="btn btn-xs btn-outline-danger act-unsupp" data-id="${s.id}">Un-suppress</button></td>
        </tr>`);
      });
    });
  }

  function updateBulkButtons() {
    const n = selected.size;
    $('#bulk-fix, #bulk-ignore, #bulk-delete').prop('disabled', n === 0);
    $('#bulk-count').text(n === 0 ? 'No selection' : `${n} selected`);
  }

  function escapeHtml(s) {
    return (s == null ? '' : String(s))
      .replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')
      .replace(/"/g,'&quot;').replace(/'/g,'&#39;');
  }

  // Event wiring
  $('#filter-form').on('submit', (e) => { e.preventDefault(); currentPage = 1; loadIssues(); });
  $(document).on('click', '#pagination a', (e) => {
    e.preventDefault(); currentPage = +$(e.target).data('page'); loadIssues();
  });
  $(document).on('change', '.chk-row', (e) => {
    const id = +e.target.value;
    e.target.checked ? selected.add(id) : selected.delete(id);
    updateBulkButtons();
  });
  $('#chk-all').on('change', (e) => {
    $('.chk-row').prop('checked', e.target.checked).trigger('change');
  });
  $(document).on('click', '.row-title', (e) => {
    e.preventDefault();
    showDetail(+$(e.target).closest('tr').data('id'));
  });
  $(document).on('click', '.act-fix', (e) => {
    const id = +$(e.target).closest('tr').data('id');
    $.post(API, {action:'mark_fixed', id, csrf_token: CSRF_TOKEN}, () => loadIssues(), 'json');
  });
  $(document).on('click', '.act-ignore', (e) => {
    const id = +$(e.target).closest('tr').data('id');
    openIgnoreDialog(id);
  });
  $(document).on('click', '[data-explain]', (e) => {
    const id = +e.target.closest('[data-explain]').dataset.explain;
    const btn = $(e.target.closest('[data-explain]'));
    const wrap = $('#modal-explain-wrap');
    const body = $('#modal-explain');
    const meta = $('#modal-explain-meta');
    const origHtml = btn.html();
    btn.prop('disabled', true).html('<span class="spinner-border spinner-border-sm me-1"></span>Thinking…');
    $.post(API, {action:'explain_issue', id, csrf_token: CSRF_TOKEN}, 'json')
      .done((r) => {
        if (!r.success) { alert('Failed to explain: ' + (r.error || 'unknown')); return; }
        body.text(r.explanation || '');
        meta.text((r.cached ? 'Cached explanation' : 'Newly generated')
                 + (r.generated_at ? ' · ' + r.generated_at : '')
                 + (r.model ? ' · ' + r.model : ''));
        wrap.removeClass('d-none');
      })
      .fail((xhr) => {
        const err = xhr.responseJSON && xhr.responseJSON.error ? xhr.responseJSON.error : 'request failed';
        alert('Failed to explain: ' + err);
      })
      .always(() => { btn.prop('disabled', false).html(origHtml); });
  });
  $(document).on('click', '[data-fix]', (e) => {
    $.post(API, {action:'mark_fixed', id: +e.target.dataset.fix, csrf_token: CSRF_TOKEN}, () => {
      bootstrap.Modal.getInstance(document.getElementById('issue-modal')).hide();
      loadIssues();
    }, 'json');
  });
  $(document).on('click', '[data-ignore]', (e) => {
    bootstrap.Modal.getInstance(document.getElementById('issue-modal')).hide();
    openIgnoreDialog(+e.target.dataset.ignore);
  });
  $('#bulk-fix').on('click', () => {
    $.post(API, {action:'bulk_update', ids: JSON.stringify([...selected]), to_status:'fixed', csrf_token: CSRF_TOKEN}, loadIssues, 'json');
  });
  $('#bulk-ignore').on('click', () => {
    $.post(API, {action:'bulk_update', ids: JSON.stringify([...selected]), to_status:'ignored', csrf_token: CSRF_TOKEN}, loadIssues, 'json');
  });
  $('#bulk-delete').on('click', () => {
    const n = selected.size;
    if (n === 0) return;
    if (!confirm(`Permanently delete ${n} issue${n === 1 ? '' : 's'}? This cannot be undone.`)) return;
    $.post(API, {action:'bulk_delete', ids: JSON.stringify([...selected]), csrf_token: CSRF_TOKEN}, (r) => {
      if (!r || !r.success) {
        alert('Delete failed: ' + ((r && r.error) || 'unknown'));
        return;
      }
      loadIssues();
    }, 'json');
  });
  $('#toggle-suppressions').on('click', () => {
    const w = $('#suppressions-wrap');
    w.toggle();
    if (w.is(':visible')) loadSuppressions();
  });
  $(document).on('click', '.act-unsupp', (e) => {
    const id = +e.target.dataset.id;
    if (!confirm('Un-suppress this signature? Future matching events will create new Issues again.')) return;
    $.post(API, {action:'un_suppress', id, csrf_token: CSRF_TOKEN}, () => loadSuppressions(), 'json');
  });

  function openIgnoreDialog(id) {
    // Silent ignore: always suppress future matches, no prompt.
    // Un-suppress available from the Suppressions panel.
    $.post(API, {action:'mark_ignored', id, suppress: 1, reason: '', csrf_token: CSRF_TOKEN},
      () => loadIssues(), 'json');
  }

  // Initial load
  render();
})();
}); // window load
</script>
