{% extends "base.html" %}

{% block title %}SFM Event DB Manager - Seismo Fleet Manager{% endblock %}

{% block content %}
<div class="mb-6 flex items-center justify-between">
    <div>
        <a href="/settings#developer" class="text-sm text-seismo-orange hover:text-seismo-burgundy">← Back to Developer Tools</a>
        <h1 class="text-3xl font-bold text-gray-900 dark:text-white mt-1">SFM Event DB Manager</h1>
        <p class="text-gray-600 dark:text-gray-400 mt-1">Browse, flag, and delete triggered events from SFM's events table. Destructive actions also clean up on-disk waveform / sidecar / pickle / hdf5 files.</p>
    </div>
    <button onclick="loadEvents()"
            class="px-4 py-2 bg-seismo-orange hover:bg-orange-600 text-white text-sm font-medium rounded-lg">
        ↻ Refresh
    </button>
</div>

<!-- Warning banner -->
<div class="rounded-xl p-4 mb-6 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800">
    <div class="flex items-start gap-3">
        <span class="text-2xl">⚠️</span>
        <div class="text-sm text-red-900 dark:text-red-200">
            <strong class="font-semibold">Destructive operations.</strong>
            Delete actions remove rows from SFM's events table AND delete associated waveform files on disk. Both are permanent — there is no undo. Use filters carefully, dry-run first, and verify the match count before confirming.
        </div>
    </div>
</div>

<!-- Filters -->
<div class="rounded-xl bg-white dark:bg-slate-800 shadow-lg p-5 mb-4">
    <h2 class="text-sm font-semibold text-gray-700 dark:text-gray-300 uppercase tracking-wider mb-3">Filters</h2>
    <div class="flex flex-wrap items-end gap-3">
        <div class="flex flex-col gap-1">
            <label class="text-xs text-gray-500 dark:text-gray-400">Serial</label>
            <input type="text" id="f-serial" placeholder="e.g. BE9558"
                   onkeydown="if (event.key === 'Enter') loadEvents()"
                   class="px-3 py-1.5 text-sm font-mono w-40 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
        </div>
        <div class="flex flex-col gap-1">
            <label class="text-xs text-gray-500 dark:text-gray-400">From</label>
            <input type="datetime-local" id="f-from"
                   class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
        </div>
        <div class="flex flex-col gap-1">
            <label class="text-xs text-gray-500 dark:text-gray-400">To</label>
            <input type="datetime-local" id="f-to"
                   class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
        </div>
        <div class="flex flex-col gap-1">
            <label class="text-xs text-gray-500 dark:text-gray-400">False Triggers</label>
            <select id="f-ft" class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                <option value="">All</option>
                <option value="false">Real Only</option>
                <option value="true">FT Only</option>
            </select>
        </div>
        <div class="flex flex-col gap-1">
            <label class="text-xs text-gray-500 dark:text-gray-400">Limit</label>
            <select id="f-limit" class="px-3 py-1.5 text-sm border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-slate-700 text-gray-900 dark:text-white">
                <option value="100">100</option>
                <option value="500" selected>500</option>
                <option value="1000">1000</option>
                <option value="5000">5000</option>
            </select>
        </div>
        <button onclick="loadEvents()"
                class="px-4 py-1.5 text-sm font-medium rounded-lg bg-seismo-orange hover:bg-orange-600 text-white">
            Apply
        </button>
        <button onclick="clearFilters()"
                class="px-3 py-1.5 text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white">
            Clear
        </button>
    </div>
</div>

<!-- Bulk actions -->
<div class="rounded-xl bg-white dark:bg-slate-800 shadow-lg p-4 mb-4 flex flex-wrap items-center gap-3">
    <span id="bulk-selected" class="text-sm text-gray-600 dark:text-gray-400">0 selected</span>

    <button id="bulk-delete-selected" onclick="deleteSelected()" disabled
            class="px-3 py-1.5 text-sm rounded-lg border border-red-300 dark:border-red-700 text-red-700 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/30 disabled:opacity-40 disabled:cursor-not-allowed">
        🗑 Delete selected
    </button>

    <button onclick="deleteByFilter()"
            class="px-3 py-1.5 text-sm rounded-lg border border-red-400 dark:border-red-600 bg-red-50 dark:bg-red-900/30 text-red-800 dark:text-red-200 hover:bg-red-100 dark:hover:bg-red-900/50 font-medium">
        🗑 Delete ALL matching current filter…
    </button>

    <div class="border-l border-gray-300 dark:border-gray-600 h-6"></div>

    <button id="bulk-flag-ft" onclick="flagSelected(true)" disabled
            class="px-3 py-1.5 text-sm rounded-lg border border-yellow-300 dark:border-yellow-700 text-yellow-700 dark:text-yellow-300 hover:bg-yellow-50 dark:hover:bg-yellow-900/30 disabled:opacity-40 disabled:cursor-not-allowed">
        🚩 Flag as FT
    </button>

    <button id="bulk-clear-ft" onclick="flagSelected(false)" disabled
            class="px-3 py-1.5 text-sm rounded-lg border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 disabled:opacity-40 disabled:cursor-not-allowed">
        ✓ Clear FT
    </button>
</div>

<!-- Results -->
<div class="rounded-xl bg-white dark:bg-slate-800 shadow-lg overflow-hidden">
    <div class="px-4 py-2 text-xs text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700" id="results-summary">
        Apply filters above to load events.
    </div>
    <div id="results-table" class="overflow-x-auto">
        <div class="text-center py-12 text-gray-500 dark:text-gray-400 text-sm">No query run yet.</div>
    </div>
</div>

<script>
const SFM_PROXY = '/api/sfm';
const _selected = new Set();
let _events = [];

function _esc(s) {
    return String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}
function _fmtPpv(v) { return (v === null || v === undefined) ? '—' : Number(v).toFixed(4); }

function _refreshBulkButtons() {
    const n = _selected.size;
    document.getElementById('bulk-selected').textContent = `${n} selected`;
    document.getElementById('bulk-delete-selected').disabled = (n === 0);
    document.getElementById('bulk-flag-ft').disabled = (n === 0);
    document.getElementById('bulk-clear-ft').disabled = (n === 0);
}

function _currentFilter() {
    const f = {};
    const serial = document.getElementById('f-serial').value.trim();
    const from   = document.getElementById('f-from').value;
    const to     = document.getElementById('f-to').value;
    const ft     = document.getElementById('f-ft').value;
    if (serial) f.serial = serial;
    if (from)   f.from_dt = from;
    if (to)     f.to_dt = to;
    if (ft === 'true')  f.false_trigger = true;
    if (ft === 'false') f.false_trigger = false;
    return f;
}

function clearFilters() {
    document.getElementById('f-serial').value = '';
    document.getElementById('f-from').value = '';
    document.getElementById('f-to').value = '';
    document.getElementById('f-ft').value = '';
    loadEvents();
}

async function loadEvents() {
    const f = _currentFilter();
    const limit = document.getElementById('f-limit').value || '500';
    const params = new URLSearchParams();
    if (f.serial)        params.set('serial', f.serial);
    if (f.from_dt)       params.set('from_dt', f.from_dt);
    if (f.to_dt)         params.set('to_dt', f.to_dt);
    if (f.false_trigger !== undefined) params.set('false_trigger', String(f.false_trigger));
    params.set('limit', limit);

    const container = document.getElementById('results-table');
    const summary = document.getElementById('results-summary');
    container.innerHTML = '<div class="text-center py-8 text-gray-500 dark:text-gray-400 text-sm">Loading…</div>';
    try {
        const resp = await fetch(`${SFM_PROXY}/db/events?${params.toString()}`);
        if (!resp.ok) {
            container.innerHTML = `<div class="text-center py-8 text-red-600 dark:text-red-400 text-sm">Load failed: HTTP ${resp.status}</div>`;
            return;
        }
        const data = await resp.json();
        _events = data.events || [];
        summary.textContent = `Showing ${_events.length} event${_events.length === 1 ? '' : 's'} (limit ${limit})`;
        renderTable();
    } catch (err) {
        container.innerHTML = `<div class="text-center py-8 text-red-600 dark:text-red-400 text-sm">Load failed: ${_esc(err.message || err)}</div>`;
    }
}

function renderTable() {
    const container = document.getElementById('results-table');
    if (_events.length === 0) {
        container.innerHTML = '<div class="text-center py-8 text-gray-500 dark:text-gray-400 text-sm">No events match the current filter.</div>';
        _refreshBulkButtons();
        return;
    }
    const rows = _events.map(ev => {
        const ts = ev.timestamp ? ev.timestamp.replace('T', ' ').slice(0, 19) : '—';
        const ft = ev.false_trigger
            ? '<span class="px-2 py-0.5 rounded text-xs bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-300">FT</span>'
            : '';
        const checked = _selected.has(ev.id) ? 'checked' : '';
        return `<tr class="hover:bg-gray-50 dark:hover:bg-slate-700/50 cursor-pointer"
                    onclick="showEventDetail('${_esc(ev.id)}')">
            <td class="px-3 py-2" onclick="event.stopPropagation()"><input type="checkbox" class="row-check" data-event-id="${_esc(ev.id)}" ${checked} onchange="onRowCheck(this)"></td>
            <td class="px-3 py-2 text-sm font-mono text-gray-700 dark:text-gray-300">${_esc(ev.serial)}</td>
            <td class="px-3 py-2 text-sm text-gray-900 dark:text-white whitespace-nowrap">${_esc(ts)}</td>
            <td class="px-3 py-2 text-sm font-mono text-right">${_fmtPpv(ev.tran_ppv)}</td>
            <td class="px-3 py-2 text-sm font-mono text-right">${_fmtPpv(ev.vert_ppv)}</td>
            <td class="px-3 py-2 text-sm font-mono text-right">${_fmtPpv(ev.long_ppv)}</td>
            <td class="px-3 py-2 text-sm font-mono text-right font-semibold">${_fmtPpv(ev.peak_vector_sum)}</td>
            <td class="px-3 py-2 text-sm">${ft}</td>
            <td class="px-3 py-2 text-sm font-mono text-gray-500 dark:text-gray-400 truncate" style="max-width:240px;" title="${_esc(ev.id)}">${_esc(ev.id).slice(0, 8)}…</td>
        </tr>`;
    }).join('');

    container.innerHTML = `
        <table class="w-full text-left">
            <thead class="bg-gray-50 dark:bg-slate-700 border-b border-gray-200 dark:border-gray-600">
                <tr>
                    <th class="px-3 py-2"><input type="checkbox" id="check-all" onchange="toggleAllRows(this.checked)"></th>
                    <th class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">Serial</th>
                    <th class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">Timestamp</th>
                    <th class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 text-right">Tran</th>
                    <th class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 text-right">Vert</th>
                    <th class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 text-right">Long</th>
                    <th class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400 text-right">PVS</th>
                    <th class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">Flags</th>
                    <th class="px-3 py-2 text-xs font-semibold uppercase tracking-wider text-gray-500 dark:text-gray-400">ID</th>
                </tr>
            </thead>
            <tbody class="divide-y divide-gray-200 dark:divide-gray-700">${rows}</tbody>
        </table>`;
    _refreshBulkButtons();
}

function onRowCheck(input) {
    const id = input.getAttribute('data-event-id');
    if (input.checked) _selected.add(id);
    else {
        _selected.delete(id);
        const master = document.getElementById('check-all');
        if (master) master.checked = false;
    }
    _refreshBulkButtons();
}

function toggleAllRows(checked) {
    document.querySelectorAll('.row-check').forEach(cb => {
        const id = cb.getAttribute('data-event-id');
        cb.checked = checked;
        if (checked) _selected.add(id);
        else         _selected.delete(id);
    });
    _refreshBulkButtons();
}

async function deleteSelected() {
    if (_selected.size === 0) return;
    const ids = Array.from(_selected);
    if (!confirm(`PERMANENTLY delete ${ids.length} event${ids.length === 1 ? '' : 's'} from the SFM DB?\n\nAlso removes associated waveform/sidecar files on disk.\nThis cannot be undone.`)) {
        return;
    }
    try {
        const resp = await fetch(`${SFM_PROXY}/db/events/delete_bulk`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ ids, confirm: true }),
        });
        const data = await resp.json();
        if (!resp.ok) {
            alert(`Delete failed: HTTP ${resp.status}\n${JSON.stringify(data)}`);
            return;
        }
        alert(`Deleted ${data.deleted} event${data.deleted === 1 ? '' : 's'}.  Removed ${data.files_removed} file${data.files_removed === 1 ? '' : 's'} from disk.`);
        _selected.clear();
        loadEvents();
    } catch (err) {
        alert(`Delete failed: ${err.message || err}`);
    }
}

async function deleteByFilter() {
    const f = _currentFilter();
    if (Object.keys(f).length === 0) {
        if (!confirm('No filters set — this would attempt to delete EVERY event in the SFM DB.\n\nAre you absolutely sure? You probably want a serial filter at minimum.')) {
            return;
        }
    }
    // Dry-run first
    let matched = 0;
    let sample_serials = [];
    try {
        const dry = await fetch(`${SFM_PROXY}/db/events/delete_bulk`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(f),
        });
        const dryData = await dry.json();
        if (!dry.ok) {
            alert(`Dry-run failed: HTTP ${dry.status}\n${JSON.stringify(dryData)}`);
            return;
        }
        matched = dryData.matched || 0;
        sample_serials = dryData.sample_serials || [];
    } catch (err) {
        alert(`Dry-run failed: ${err.message || err}`);
        return;
    }
    if (matched === 0) {
        alert('No events match the current filter — nothing to delete.');
        return;
    }

    const filterLines = Object.entries(f).map(([k, v]) => `  ${k} = ${v}`).join('\n') || '  (none)';
    const serialList = sample_serials.length ? `  serials: ${sample_serials.join(', ')}${sample_serials.length === 5 ? ' (and possibly more)' : ''}\n` : '';
    if (!confirm(`PERMANENTLY delete ${matched} event${matched === 1 ? '' : 's'}?\n\nFilter:\n${filterLines}\n${serialList}\nAlso removes associated waveform/sidecar files on disk.\nThis cannot be undone.`)) {
        return;
    }
    try {
        const resp = await fetch(`${SFM_PROXY}/db/events/delete_bulk`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ ...f, confirm: true }),
        });
        const data = await resp.json();
        if (!resp.ok) {
            alert(`Delete failed: HTTP ${resp.status}\n${JSON.stringify(data)}`);
            return;
        }
        if (data.status === 'too_many') {
            alert(`Refused: ${data.hint}`);
            return;
        }
        alert(`Deleted ${data.deleted} event${data.deleted === 1 ? '' : 's'}.  Removed ${data.files_removed} file${data.files_removed === 1 ? '' : 's'} from disk.`);
        _selected.clear();
        loadEvents();
    } catch (err) {
        alert(`Delete failed: ${err.message || err}`);
    }
}

async function flagSelected(value) {
    if (_selected.size === 0) return;
    const ids = Array.from(_selected);
    const verb = value ? 'flag as false trigger' : 'clear false-trigger flag on';
    if (!confirm(`${verb} ${ids.length} event${ids.length === 1 ? '' : 's'}?`)) return;
    let ok = 0, failed = 0, cursor = 0;
    async function worker() {
        while (cursor < ids.length) {
            const i = cursor++;
            const id = ids[i];
            try {
                const r = await fetch(`${SFM_PROXY}/db/events/${encodeURIComponent(id)}/false_trigger?value=${value ? 'true' : 'false'}`,
                                      { method: 'PATCH' });
                if (r.ok) ok++; else failed++;
            } catch (_) { failed++; }
        }
    }
    await Promise.all(Array.from({ length: 8 }, worker));
    if (failed) alert(`${ok} updated, ${failed} failed.`);
    _selected.clear();
    loadEvents();
}

// Initial empty state — let the user choose to load.

// Refresh the events table when the modal's review form saves — keeps
// the FT badge in sync without a full page reload.
window.addEventListener('sfm-event-review-saved', () => {
    if (_events.length) loadEvents();
});
</script>

{# Shared event-detail modal — rendered by /static/event-modal.js #}
{% include 'partials/event_detail_modal.html' %}
<script src="/static/event-modal.js"></script>
{% endblock %}
