/**
 * Demo Oracle — SQL Monitor natif, rafraichi en continu.
 *
 * Portage de modules/oracle/assets/js/report-sqlmon-live.js. Le rendu est le
 * meme ; c'est la SOURCE qui differe, et elle est resolue cote serveur : ici
 * l'activite est toujours echantillonnee depuis GV$SESSION, jamais lue dans
 * une vue sous licence.
 *
 * Les compteurs que seul Real-Time SQL Monitoring fournit — lignes reelles,
 * demarrages — arrivent a null et s'affichent « — », jamais 0 : un compteur
 * absent n'est pas un compteur nul.
 *
 * Aucune bibliotheque externe : les barres sont des div.
 */
(function () {
'use strict';

var D = (window.DemoOracleReport && window.DemoOracleReport.sqlmon) || {};
var BASE_URL   = D.baseUrl    || '';
var SQL_ID     = D.sqlId      || '';
var EXEC_ID    = D.execId     || '';
var EXEC_START = D.execStart  || '';
var PHV        = D.phv        || '';
var INST_ID    = D.instId     || 0;
var HAS_EXEC   = !!D.hasExec;
var REFRESH_MS = (D.refreshMs && D.refreshMs >= 1000) ? D.refreshMs : 5000;
var T          = D.T || {};

/* Au-dela, on cesse d'interroger : un onglet oublie ne doit pas sonder une
   base pendant des heures. */
var MAX_POLLS  = 720;
var MAX_STILL  = 120;

var _timer = null, _tick = null, _cd = 0;
var _polls = 0, _still = 0, _lastSig = '', _stopped = false;

/* ── Couleurs de classes d'attente — memes teintes que le temps reel ────── */
var WC = {
    'CPU':'#00BB00','Scheduler':'#78E078','User I/O':'#0043D0',
    'System I/O':'#0090E0','Network':'#989070','Commit':'#E06800',
    'Concurrency':'#801800','Configuration':'#606810','Application':'#FF0000',
    'Other':'#F071B0','Administrative':'#888888','Idle':'#cccccc'
};
var PALETTE = ['#4e79a7','#f28e2b','#59a14f','#b6992d','#499894',
               '#e15759','#79706e','#d37295','#b07aa1','#9d7660'];
var _pidx = {};
function wcColor(v) {
    if (WC[v] !== undefined) return WC[v];
    if (_pidx[v] === undefined) _pidx[v] = Object.keys(_pidx).length % PALETTE.length;
    return PALETTE[_pidx[v]];
}

/* ── Formatage — repris de report-sql-detail.js ─────────────────────────── */
function fmtN(n) {
    if (n === null || n === undefined) return '—';
    if (n >= 1e12) return (n/1e12).toFixed(1) + 'T';
    if (n >= 1e9)  return (n/1e9).toFixed(1)  + 'G';
    if (n >= 1e6)  return (n/1e6).toFixed(1)  + 'M';
    if (n >= 1e3)  return (n/1e3).toFixed(1)  + 'K';
    return String(n);
}
function fmtB(b) {
    if (b === null || b === undefined) return '—';
    if (b >= 1073741824) return (b/1073741824).toFixed(1) + ' GB';
    if (b >= 1048576)    return (b/1048576).toFixed(1)    + ' MB';
    if (b >= 1024)       return (b/1024).toFixed(1)       + ' KB';
    return b + ' B';
}
function fmtS(s) {
    if (s === null || s === undefined) return '—';
    if (s >= 3600) return (s/3600).toFixed(1) + 'h';
    if (s >= 60)   return (s/60).toFixed(1)   + 'min';
    return Math.round(s) + 's';
}
function esc(s) {
    return String(s === null || s === undefined ? '' : s)
        .replace(/&/g,'&amp;').replace(/</g,'&lt;')
        .replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}
function el(id) { return document.getElementById(id); }
function set(id, html) { var n = el(id); if (n) n.innerHTML = html; }

/* ── Bandeau de source ──────────────────────────────────────────────────── */
function renderSource(d) {
    var lab = T['src_' + d.source] || d.source;
    var hint = T['src_hint_' + d.source] || '';
    var color = (d.source === 'pack') ? '#059669'
              : (d.source === 'ash')  ? '#2563eb' : '#b45309';

    var h = '<span style="display:inline-block;padding:.15rem .5rem;border-radius:999px;'
          + 'font-size:.74rem;font-weight:700;color:#fff;background:' + color + '">'
          + esc(lab) + '</span>';

    if (hint) {
        h += '<span style="margin-left:.5rem;font-size:.8rem;color:var(--color-text-muted)">'
           + esc(hint) + '</span>';
    }
    /* Une colonne SQL_PLAN_LINE_ID absente ou jamais alimentee : on le dit,
       plutot que de laisser croire a une activite nulle. */
    if (d.plan_line === false) {
        h += '<div style="margin-top:.4rem;font-size:.8rem;color:#b45309">⚠ '
           + esc(T.activity_partial || '') + '</div>';
    }
    if (d.licensed) {
        h += '<div style="margin-top:.4rem;font-size:.8rem;color:#854d0e;'
           + 'background:#fef9c3;border-radius:4px;padding:.35rem .6rem">'
           + esc(T.licensed_note || '') + '</div>';
    }
    set('sqlmon-source', h);
}

/* ── En-tete de l'execution ─────────────────────────────────────────────── */
function renderHead(d) {
    var e = d.exec || {};
    var st = d.done ? (T.exec_done || 'DONE') : (T.exec_running || 'EXECUTING');
    var stColor = d.done ? '#64748b' : '#059669';

    var h = '<div style="display:flex;flex-wrap:wrap;gap:1.2rem;align-items:baseline">';
    h += '<div><code style="font-size:1rem;font-weight:700">' + esc(SQL_ID) + '</code>'
       + ' <span style="color:var(--color-text-muted);font-size:.82rem">#'
       + esc(EXEC_ID) + '</span></div>';
    h += '<div style="color:' + stColor + ';font-weight:600">' + esc(st) + '</div>';

    if (d.status_deduced) {
        h += '<div style="font-size:.78rem;color:var(--color-text-muted)">'
           + esc(T.status_deduced || '') + '</div>';
    }
    if (e.username) h += '<div style="font-size:.82rem">' + esc(e.username) + '</div>';
    if (e.module)   h += '<div style="font-size:.82rem;color:var(--color-text-muted)">'
                       + esc(e.module) + '</div>';
    if (e.sessions) h += '<div style="font-size:.82rem;color:var(--color-text-muted)">'
                       + esc(e.sessions) + ' ' + esc(T.exec_sessions || '') + '</div>';
    if (d.phv) h += '<div style="font-size:.78rem;color:var(--color-text-muted)">PHV '
                  + esc(d.phv) + '</div>';
    h += '</div>';
    set('sqlmon-head', h);

    /* Totaux. `scope` dit d'ou ils viennent — un cumul de curseur ne doit
       jamais passer pour la consommation d'une execution. */
    var t = d.totals || {};
    var scope = (t.scope === 'exec') ? (T.scope_exec || '') : (T.scope_delta || '');
    var cells = [
        [T.tot_elapsed || 'Elapsed', fmtS(t.elapsed_s)],
        [T.tot_cpu     || 'CPU',     fmtS(t.cpu_s)],
        [T.tot_io      || 'I/O',     fmtS(t.user_io_s)],
        [T.tot_lio     || 'LIO',     fmtN(t.lio)],
        [T.tot_pio     || 'PIO',     fmtN(t.pio)],
        [T.tot_read    || 'Read',    fmtB(t.read_bytes)]
    ];
    var th = '<div style="display:flex;flex-wrap:wrap;gap:1.4rem;margin-top:.6rem">';
    for (var i = 0; i < cells.length; i++) {
        th += '<div><div style="font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;'
            + 'color:var(--color-text-muted)">' + esc(cells[i][0]) + '</div>'
            + '<div style="font-size:1rem;font-weight:600">' + esc(cells[i][1]) + '</div></div>';
    }
    th += '</div>';
    if (scope) {
        th += '<div style="margin-top:.35rem;font-size:.76rem;color:var(--color-text-muted)">'
            + esc(scope) + '</div>';
    }
    set('sqlmon-totals', th);
}

/* ── Barre d'activite d'une ligne de plan ───────────────────────────────── */
function activityBar(act, sum, max, pct) {
    if (!sum) return '<span style="color:var(--color-text-muted)">—</span>';

    var width = max > 0 ? Math.max(2, Math.round(sum * 100 / max)) : 0;
    var h = '<div style="display:flex;align-items:center;gap:.4rem">'
          + '<div style="flex:1;min-width:60px;height:11px;border-radius:2px;overflow:hidden;'
          + 'display:flex;background:var(--color-border,#e5e7eb)">'
          + '<div style="display:flex;width:' + width + '%">';

    /* Les classes dans un ordre stable : sans tri, un rafraichissement
       reordonnerait les segments et la barre clignoterait. */
    var keys = Object.keys(act).sort();
    for (var i = 0; i < keys.length; i++) {
        var w = Math.round(act[keys[i]] * 100 / sum);
        h += '<div title="' + esc(keys[i]) + ' — ' + act[keys[i]] + '" style="width:' + w
           + '%;background:' + wcColor(keys[i]) + '"></div>';
    }
    h += '</div></div><span style="font-size:.72rem;white-space:nowrap;'
       + 'color:var(--color-text-muted);min-width:38px;text-align:right">'
       + pct.toFixed(1) + '%</span></div>';
    return h;
}

/* ── Tableau du plan ────────────────────────────────────────────────────── */
function renderPlan(d) {
    var plan = d.plan || [];
    if (!plan.length) {
        set('sqlmon-plan', '<p style="margin:0;color:var(--color-text-muted)">'
            + esc(T.plan_none || '') + '</p>');
        return;
    }

    var monitored = (d.plan_source === 'plan_monitor');
    var h = '<table style="width:100%;border-collapse:collapse;font-size:.8rem">'
          + '<thead><tr style="background:var(--color-input-bg,#f8f8f8);'
          + 'border-bottom:1px solid var(--color-border)">'
          + th(T.col_line || 'ID', 'right')
          + th(T.col_operation || 'Operation', 'left')
          + th(T.col_object || 'Object', 'left')
          + th(T.col_est_rows || 'Est.', 'right')
          + th(T.col_act_rows || 'Actual', 'right')
          + th(T.col_starts || 'Starts', 'right')
          + th(T.col_mem || 'Mem', 'right')
          + th(T.col_temp || 'Temp', 'right')
          + th(T.col_activity || 'Activity', 'left')
          + '</tr></thead><tbody>';

    for (var i = 0; i < plan.length; i++) {
        var p = plan[i];
        var indent = (p.depth || 0) * 16;
        var hasPred = (p.acc || p.flt);
        var rowStyle = 'border-bottom:1px solid var(--color-border)';
        if (p.active) rowStyle += ';box-shadow:inset 3px 0 0 0 #059669';

        h += '<tr style="' + rowStyle + '">';
        h += td(p.id, 'right', 'color:var(--color-text-muted)');
        h += '<td style="padding:3px 5px;padding-left:' + (indent + 5) + 'px;'
           + 'font-family:monospace;font-size:.76rem;white-space:nowrap">';
        if (hasPred) {
            h += '<span onclick="sqlmonTogglePred(' + i + ')" style="cursor:pointer;'
               + 'color:var(--color-text-muted);user-select:none">▸ </span>';
        }
        h += esc(p.op) + (p.opts ? ' ' + esc(p.opts) : '') + '</td>';

        var obj = p.obj ? ((p.owner ? p.owner + '.' : '') + p.obj) : '';
        h += td(obj, 'left', 'font-size:.76rem');
        h += td(fmtN(p.est_rows), 'right');

        /* null = la source ne fournit pas ce compteur ; le title l'explique
           plutot que d'afficher un zero trompeur. */
        var noCounter = monitored ? '' : ' title="' + esc(T.counter_rtsm_only || '') + '"';
        h += '<td style="padding:3px 5px;text-align:right"' + noCounter + '>'
           + esc(fmtN(p.act_rows)) + '</td>';
        h += '<td style="padding:3px 5px;text-align:right"' + noCounter + '>'
           + esc(fmtN(p.starts)) + '</td>';

        h += td(p.mem ? fmtB(p.mem) : '—', 'right');
        var temp = p.temp ? fmtB(p.temp) : '—';
        if (p.passes > 1) temp += ' <span style="color:#b45309">(' + p.passes + 'p)</span>';
        h += '<td style="padding:3px 5px;text-align:right">' + temp + '</td>';

        h += '<td style="padding:3px 5px;min-width:150px">'
           + activityBar(p.act || {}, p.act_sum || 0, d.activity_max || 0, p.act_pct || 0)
           + '</td>';
        h += '</tr>';

        if (hasPred) {
            h += '<tr id="sqlmon-pred-' + i + '" style="display:none;'
               + 'border-bottom:1px solid var(--color-border)">'
               + '<td colspan="9" style="padding:3px 5px;padding-left:' + (indent + 21)
               + 'px;font-family:monospace;font-size:.72rem">';
            if (p.acc) h += '<div style="color:#3b82f6">access: ' + esc(p.acc) + '</div>';
            if (p.flt) h += '<div style="color:#ef4444">filter: ' + esc(p.flt) + '</div>';
            h += '</td></tr>';
        }
    }
    h += '</tbody></table>';

    if (d.plan_truncated) {
        h += '<p style="margin:.5rem 0 0;font-size:.8rem;color:#b45309">⚠ '
           + esc(T.plan_truncated || '') + '</p>';
    }
    if (d.unattributed > 0) {
        h += '<p style="margin:.5rem 0 0;font-size:.78rem;color:var(--color-text-muted)">'
           + esc(T.activity_unattributed || '') + ' : ' + d.unattributed + '</p>';
    }
    set('sqlmon-plan', h);
}

function th(label, align) {
    return '<th style="text-align:' + align + ';padding:.3rem .5rem;white-space:nowrap">'
         + esc(label) + '</th>';
}
function td(v, align, extra) {
    return '<td style="padding:3px 5px;text-align:' + align + ';' + (extra || '') + '">'
         + esc(v) + '</td>';
}

window.sqlmonTogglePred = function (i) {
    var r = el('sqlmon-pred-' + i);
    if (r) r.style.display = (r.style.display === 'none') ? '' : 'none';
};

/* ── Longops ────────────────────────────────────────────────────────────── */
function renderLongOps(d) {
    var rows = d.longops || [];
    if (!rows.length) {
        set('sqlmon-longops', '<p style="margin:0;color:var(--color-text-muted)">'
            + esc(T.longops_none || '') + '</p>');
        return;
    }
    var h = '<table style="width:100%;border-collapse:collapse;font-size:.8rem"><tbody>';
    for (var i = 0; i < rows.length; i++) {
        var r = rows[i];
        var pct = r.pct || 0;
        var bar = '<div style="display:flex;align-items:center;gap:.4rem;min-width:140px">'
                + '<div style="flex:1;background:var(--color-border,#ddd);border-radius:2px;'
                + 'height:8px;overflow:hidden"><div style="height:100%;'
                + 'background:var(--color-primary,#4e79a7);width:' + Math.min(100, pct)
                + '%"></div></div><span style="font-size:.74rem;white-space:nowrap;'
                + 'color:var(--color-text-muted)">' + pct.toFixed(1) + '%</span></div>';

        h += '<tr style="border-bottom:1px solid var(--color-border)">'
           + td(r.opname, 'left')
           + td(r.target, 'left', 'font-size:.76rem;color:var(--color-text-muted)')
           + '<td style="padding:3px 5px">' + bar + '</td>'
           + td(fmtN(r.sofar) + ' / ' + fmtN(r.totalwork) + ' ' + (r.units || ''), 'right',
                'font-size:.76rem')
           + td(r.remaining > 0 ? (T.longops_eta || 'ETA') + ' ' + fmtS(r.remaining) : '—',
                'right', 'font-size:.76rem')
           + '</tr>';
    }
    h += '</tbody></table>';
    set('sqlmon-longops', h);
}

/* ── Inventaire des executions ──────────────────────────────────────────── */
function renderExecs(d) {
    var rows = d.executions || [];
    if (!rows.length) {
        set('sqlmon-execs', '<p style="margin:0;color:var(--color-text-muted)">'
            + esc(T.exec_none || '') + '</p>');
        return;
    }
    var h = '<table style="width:100%;border-collapse:collapse;font-size:.8rem">'
          + '<thead><tr style="background:var(--color-input-bg,#f8f8f8);'
          + 'border-bottom:1px solid var(--color-border)">'
          + th('SQL_ID', 'left') + th('#', 'right') + th(T.exec_begin || 'Start', 'left')
          + th(T.exec_status || 'Status', 'left') + th('Elapsed', 'right')
          + th('User', 'left') + th('Module', 'left') + th('', 'right')
          + '</tr></thead><tbody>';

    for (var i = 0; i < rows.length; i++) {
        var r = rows[i];
        var url = BASE_URL + '&sql_id=' + encodeURIComponent(r.sql_id)
                + '&exec_id=' + encodeURIComponent(r.sql_exec_id)
                + '&exec_start=' + encodeURIComponent(r.exec_start)
                + '&inst_id=' + encodeURIComponent(r.inst_id)
                + '&phv=' + encodeURIComponent(r.phv || '');
        h += '<tr style="border-bottom:1px solid var(--color-border)">'
           + '<td style="padding:3px 5px"><code>' + esc(r.sql_id) + '</code></td>'
           + td(r.sql_exec_id, 'right')
           + td(r.exec_start, 'left', 'font-family:monospace;font-size:.74rem')
           + '<td style="padding:3px 5px;color:'
           + (r.status === 'EXECUTING' ? '#059669' : '#64748b') + '">'
           + esc(r.status) + '</td>'
           + td(fmtS(r.elapsed_s), 'right')
           + td(r.username, 'left')
           + td(r.module, 'left', 'font-size:.76rem')
           + '<td style="padding:3px 5px;text-align:right">'
           + '<a class="btn btn-sm btn-secondary" href="' + esc(url) + '">'
           + esc(T.exec_open || 'open') + '</a></td></tr>';
    }
    h += '</tbody></table>';
    set('sqlmon-execs', h);
}

/* ── Cycle de rafraichissement ──────────────────────────────────────────── */
function url() {
    var u = BASE_URL + '&format=json';
    if (HAS_EXEC) {
        u += '&sql_id=' + encodeURIComponent(SQL_ID)
           + '&exec_id=' + encodeURIComponent(EXEC_ID)
           + '&exec_start=' + encodeURIComponent(EXEC_START)
           + '&inst_id=' + encodeURIComponent(INST_ID)
           + '&phv=' + encodeURIComponent(PHV);
    } else if (SQL_ID) {
        u += '&sql_id=' + encodeURIComponent(SQL_ID);
    }
    return u;
}

function fetchData() {
    _polls++;
    fetch(url(), { credentials: 'same-origin' })
        .then(function (r) { return r.json(); })
        .then(function (d) {
            if (d.error) { notice(d.error, true); return; }
            notice('', false);

            var up = el('sqlmon-updated');
            if (up) up.textContent = d.updated || '';

            if (!HAS_EXEC) { renderExecs(d); return; }

            renderSource(d);
            renderHead(d);
            renderPlan(d);
            renderLongOps(d);

            var sm = el('sqlmon-samples');
            if (sm) {
                sm.textContent = d.sample_count
                    ? (d.sample_count + ' ' + (T.samples || '') + ' / ' + fmtS(d.sample_span))
                    : '';
            }

            /* Signature de progression : si rien ne bouge pendant longtemps,
               inutile de continuer a interroger la base. */
            var sig = String(d.activity_total) + '|' + String(d.totals && d.totals.elapsed_s);
            if (sig === _lastSig) { _still++; } else { _still = 0; _lastSig = sig; }

            if (d.done) {
                /* Un dernier releve 2 s plus tard : le plan monitore traine
                   d'une passe de rafraichissement derriere la fin reelle. */
                stop();
                setTimeout(function () {
                    fetch(url(), { credentials: 'same-origin' })
                        .then(function (r) { return r.json(); })
                        .then(function (d2) {
                            if (d2 && !d2.error) {
                                renderSource(d2); renderHead(d2);
                                renderPlan(d2); renderLongOps(d2);
                            }
                            finished(T.finished || '');
                        })
                        .catch(function () { finished(T.finished || ''); });
                }, 2000);
                return;
            }
            if (_polls >= MAX_POLLS || _still >= MAX_STILL) {
                stop();
                finished(T.abandoned || '');
            }
        })
        .catch(function (e) { notice(String(e), true); });
}

function notice(msg, isError) {
    var n = el('sqlmon-notice');
    if (!n) return;
    n.style.display = msg ? '' : 'none';
    n.className = isError ? 'alert alert-error' : 'alert alert-warning';
    n.textContent = msg;
}

function finished(msg) {
    var f = el('sqlmon-finished');
    if (f) { f.style.display = ''; f.querySelector('span').textContent = msg; }
    var c = el('sqlmon-countdown-box');
    if (c) c.style.display = 'none';
}

function start() {
    stop();
    _stopped = false;
    _cd = Math.round(REFRESH_MS / 1000);
    _timer = setInterval(function () { _cd = Math.round(REFRESH_MS / 1000); fetchData(); }, REFRESH_MS);
    _tick  = setInterval(function () {
        _cd = Math.max(0, _cd - 1);
        var n = el('sqlmon-countdown');
        if (n) n.textContent = _cd;
    }, 1000);
}

function stop() {
    if (_timer) { clearInterval(_timer); _timer = null; }
    if (_tick)  { clearInterval(_tick);  _tick  = null; }
    _stopped = true;
}

/* Onglet en arriere-plan : on cesse d'interroger. Sans pack, chaque poll
   ajoute un echantillon a l'histogramme — continuer en arriere-plan
   fausserait la mesure autant que ca chargerait la base pour rien. */
document.addEventListener('visibilitychange', function () {
    if (document.hidden) {
        if (!_stopped) { stop(); _stopped = 'hidden'; }
    } else if (_stopped === 'hidden') {
        fetchData();
        start();
    }
});

window.sqlmonRestart = function () {
    _polls = 0; _still = 0; _lastSig = '';
    var f = el('sqlmon-finished');
    if (f) f.style.display = 'none';
    var c = el('sqlmon-countdown-box');
    if (c) c.style.display = '';
    fetchData();
    start();
};

fetchData();
start();

})();
