"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.remoteMetricsDB = void 0;
const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
// @group Configuration : SQLite DB lives alongside other server config files
const DB_DIR = path_1.default.join(__dirname, '../config');
const DB_PATH = path_1.default.join(DB_DIR, 'remote-metrics.db');
// @group RemoteMetricsDB : SQLite-backed persistent store for remote process metrics
class RemoteMetricsDB {
    constructor() {
        if (!fs_1.default.existsSync(DB_DIR))
            fs_1.default.mkdirSync(DB_DIR, { recursive: true });
        this.db = new better_sqlite3_1.default(DB_PATH);
        this.db.pragma('journal_mode = WAL');
        this.initSchema();
        this.purgeOld();
    }
    // @group RemoteMetricsDB : Create tables and indexes on first run
    initSchema() {
        this.db.exec(`
      CREATE TABLE IF NOT EXISTS remote_metrics (
        id              INTEGER PRIMARY KEY AUTOINCREMENT,
        connection_id   TEXT    NOT NULL,
        connection_name TEXT    NOT NULL,
        process_name    TEXT    NOT NULL,
        pm_id           INTEGER NOT NULL,
        timestamp       INTEGER NOT NULL,
        cpu             REAL    NOT NULL,
        memory_bytes    INTEGER NOT NULL,
        memory_mb       REAL    NOT NULL
      );

      CREATE INDEX IF NOT EXISTS idx_rmet_conn_proc_ts
        ON remote_metrics (connection_id, process_name, timestamp);

      CREATE INDEX IF NOT EXISTS idx_rmet_conn_ts
        ON remote_metrics (connection_id, timestamp);
    `);
    }
    // @group RemoteMetricsDB : Delete records older than 30 days to keep DB lean
    purgeOld() {
        const cutoff = Date.now() - 30 * 24 * 60 * 60 * 1000;
        this.db.prepare('DELETE FROM remote_metrics WHERE timestamp < ?').run(cutoff);
    }
    // @group RemoteMetricsDB : Persist a single data-point
    insert(row) {
        this.db.prepare(`
      INSERT INTO remote_metrics
        (connection_id, connection_name, process_name, pm_id, timestamp, cpu, memory_bytes, memory_mb)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?)
    `).run(row.connection_id, row.connection_name, row.process_name, row.pm_id, row.timestamp, row.cpu, row.memory_bytes, row.memory_mb);
    }
    // @group RemoteMetricsDB : Bulk-insert multiple data-points in one transaction (efficient)
    insertBatch(rows) {
        const stmt = this.db.prepare(`
      INSERT INTO remote_metrics
        (connection_id, connection_name, process_name, pm_id, timestamp, cpu, memory_bytes, memory_mb)
      VALUES (?, ?, ?, ?, ?, ?, ?, ?)
    `);
        const tx = this.db.transaction(() => {
            for (const row of rows) {
                stmt.run(row.connection_id, row.connection_name, row.process_name, row.pm_id, row.timestamp, row.cpu, row.memory_bytes, row.memory_mb);
            }
        });
        tx();
    }
    // @group RemoteMetricsDB : Return all (connection_id, connection_name) pairs that have data
    getConnectionsWithData() {
        return this.db.prepare(`
      SELECT DISTINCT connection_id AS id, connection_name AS name
      FROM remote_metrics
      ORDER BY name
    `).all();
    }
    // @group RemoteMetricsDB : Return distinct process names recorded for a connection
    getProcessNames(connectionId) {
        const rows = this.db.prepare(`
      SELECT DISTINCT process_name
      FROM remote_metrics
      WHERE connection_id = ?
      ORDER BY process_name
    `).all(connectionId);
        return rows.map(r => r.process_name);
    }
    // @group RemoteMetricsDB : Return metric rows for a specific process within a time range
    getMetrics(connectionId, processName, from, to, maxPoints = 500) {
        // Bucket-reduce to avoid sending thousands of points for long ranges
        const total = this.db.prepare(`
      SELECT COUNT(*) AS cnt FROM remote_metrics
      WHERE connection_id = ? AND process_name = ? AND timestamp BETWEEN ? AND ?
    `).get(connectionId, processName, from, to).cnt;
        if (total <= maxPoints) {
            return this.db.prepare(`
        SELECT * FROM remote_metrics
        WHERE connection_id = ? AND process_name = ? AND timestamp BETWEEN ? AND ?
        ORDER BY timestamp ASC
      `).all(connectionId, processName, from, to);
        }
        // Downsample: pick every Nth row so we stay under maxPoints
        const step = Math.ceil(total / maxPoints);
        return this.db.prepare(`
      SELECT * FROM remote_metrics
      WHERE connection_id = ? AND process_name = ? AND timestamp BETWEEN ? AND ?
        AND (id % ?) = 0
      ORDER BY timestamp ASC
      LIMIT ?
    `).all(connectionId, processName, from, to, step, maxPoints);
    }
    // @group RemoteMetricsDB : Return latest snapshot for all processes on a connection
    getLatestSnapshot(connectionId) {
        return this.db.prepare(`
      SELECT r.*
      FROM remote_metrics r
      INNER JOIN (
        SELECT process_name, MAX(timestamp) AS max_ts
        FROM remote_metrics
        WHERE connection_id = ?
        GROUP BY process_name
      ) latest ON r.process_name = latest.process_name AND r.timestamp = latest.max_ts
      WHERE r.connection_id = ?
      ORDER BY r.process_name
    `).all(connectionId, connectionId);
    }
    close() {
        this.db.close();
    }
}
// @group Exports : Singleton used across the server
exports.remoteMetricsDB = new RemoteMetricsDB();
