import { DatabaseSync } from "node:sqlite";
import path from "node:path";
import { getConfigDir } from "../../config/index.js";
import { logger } from "../../logger.js";
let db = null;
let stmtInsert = null;
let stmtListPending = null;
let stmtMarkInflight = null;
let stmtMarkAcked = null;
let stmtMarkRejected = null;
let stmtReleaseStale = null;
let stmtReleaseByIds = null;
let stmtMarkOldStale = null;
function requireStatement(stmt, name) {
    if (!stmt)
        throw new Error(`Usage report queue statement not initialized: ${name}`);
    return stmt;
}
function getDb() {
    if (db)
        return db;
    const dbPath = path.join(getConfigDir(), "usage.db");
    db = new DatabaseSync(dbPath);
    db.exec("PRAGMA journal_mode = WAL");
    db.exec(`
    CREATE TABLE IF NOT EXISTS usage_reports (
      wake_cycle_id TEXT PRIMARY KEY,
      report_json TEXT NOT NULL,
      status TEXT NOT NULL DEFAULT 'pending',
      attempt_count INTEGER NOT NULL DEFAULT 0,
      last_attempt_at TEXT,
      last_error TEXT,
      created_at TEXT NOT NULL,
      acked_at TEXT,
      report_schema_version INTEGER NOT NULL,
      session_type TEXT,
      started_at TEXT,
      ended_at TEXT,
      duration_seconds INTEGER,
      trigger_type TEXT,
      trigger_channel TEXT,
      trigger_message_id TEXT
    )
  `);
    db.exec("CREATE INDEX IF NOT EXISTS idx_usage_reports_status_created ON usage_reports(status, created_at)");
    try {
        db.exec("ALTER TABLE usage_reports ADD COLUMN session_type TEXT");
    }
    catch { /* already migrated */ }
    try {
        db.exec("ALTER TABLE usage_reports ADD COLUMN started_at TEXT");
    }
    catch { /* already migrated */ }
    try {
        db.exec("ALTER TABLE usage_reports ADD COLUMN ended_at TEXT");
    }
    catch { /* already migrated */ }
    try {
        db.exec("ALTER TABLE usage_reports ADD COLUMN duration_seconds INTEGER");
    }
    catch { /* already migrated */ }
    try {
        db.exec("ALTER TABLE usage_reports ADD COLUMN trigger_type TEXT");
    }
    catch { /* already migrated */ }
    try {
        db.exec("ALTER TABLE usage_reports ADD COLUMN trigger_channel TEXT");
    }
    catch { /* already migrated */ }
    try {
        db.exec("ALTER TABLE usage_reports ADD COLUMN trigger_message_id TEXT");
    }
    catch { /* already migrated */ }
    stmtInsert = db.prepare(`
    INSERT INTO usage_reports (
      wake_cycle_id,
      report_json,
      status,
      attempt_count,
      created_at,
      report_schema_version,
      session_type,
      started_at,
      ended_at,
      duration_seconds,
      trigger_type,
      trigger_channel,
      trigger_message_id
    ) VALUES (?, ?, 'pending', 0, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    ON CONFLICT(wake_cycle_id) DO NOTHING
  `);
    stmtListPending = db.prepare(`
    SELECT wake_cycle_id, report_json, status, attempt_count, last_attempt_at, last_error, created_at, acked_at, report_schema_version
    FROM usage_reports
    WHERE status IN ('pending', 'stale')
    ORDER BY created_at ASC
    LIMIT ?
  `);
    stmtMarkInflight = db.prepare(`
    UPDATE usage_reports
    SET status = 'inflight', attempt_count = attempt_count + 1, last_attempt_at = ?, last_error = NULL
    WHERE wake_cycle_id = ? AND status IN ('pending', 'stale')
  `);
    stmtMarkAcked = db.prepare("UPDATE usage_reports SET status = 'acked', acked_at = ?, last_error = NULL WHERE wake_cycle_id = ?");
    stmtMarkRejected = db.prepare("UPDATE usage_reports SET status = 'rejected', last_error = ? WHERE wake_cycle_id = ?");
    stmtReleaseStale = db.prepare(`
    UPDATE usage_reports
    SET status = 'pending', last_error = 'inflight_timeout'
    WHERE status = 'inflight' AND last_attempt_at <= ?
  `);
    stmtReleaseByIds = db.prepare(`
    UPDATE usage_reports
    SET status = 'pending', last_error = ?
    WHERE status = 'inflight' AND wake_cycle_id = ?
  `);
    stmtMarkOldStale = db.prepare(`
    UPDATE usage_reports
    SET status = 'stale', last_error = 'retention_expired'
    WHERE status IN ('pending', 'inflight') AND created_at <= ?
  `);
    return db;
}
export function enqueueUsageReport(report) {
    getDb();
    requireStatement(stmtInsert, "insert").run(report.wakeCycleId, JSON.stringify(report), new Date().toISOString(), report.schemaVersion, report.sessionType, report.startedAt, report.endedAt, report.durationSeconds, report.triggerType ?? null, report.triggerChannel ?? null, null);
}
export function listPendingUsageReports(limit) {
    getDb();
    const rows = requireStatement(stmtListPending, "listPending").all(Math.max(1, limit));
    return rows.map((row) => ({
        wakeCycleId: row.wake_cycle_id,
        report: JSON.parse(row.report_json),
        status: row.status,
        attemptCount: row.attempt_count,
        lastAttemptAt: row.last_attempt_at,
        lastError: row.last_error,
        createdAt: row.created_at,
        ackedAt: row.acked_at,
        reportSchemaVersion: row.report_schema_version,
    }));
}
export function markUsageReportsInflight(ids) {
    getDb();
    const now = new Date().toISOString();
    const stmt = requireStatement(stmtMarkInflight, "markInflight");
    for (const id of ids)
        stmt.run(now, id);
}
export function markUsageReportsAcked(ids) {
    getDb();
    const now = new Date().toISOString();
    const stmt = requireStatement(stmtMarkAcked, "markAcked");
    for (const id of ids)
        stmt.run(now, id);
}
export function markUsageReportsRejected(rejections) {
    getDb();
    const stmt = requireStatement(stmtMarkRejected, "markRejected");
    for (const rejection of rejections)
        stmt.run(rejection.reason, rejection.wakeCycleId);
}
export function releaseStaleInflightUsageReports(timeoutMs) {
    getDb();
    const cutoff = new Date(Date.now() - timeoutMs).toISOString();
    requireStatement(stmtReleaseStale, "releaseStale").run(cutoff);
}
export function releaseInflightUsageReports(ids, reason) {
    getDb();
    const stmt = requireStatement(stmtReleaseByIds, "releaseByIds");
    for (const id of ids)
        stmt.run(reason, id);
}
export function markUsageReportsStaleOlderThan(days) {
    getDb();
    const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
    requireStatement(stmtMarkOldStale, "markOldStale").run(cutoff);
}
export function closeUsageReportQueue() {
    if (!db)
        return;
    try {
        db.close();
    }
    catch (err) {
        logger.warn(`Failed to close usage report queue: ${err instanceof Error ? err.message : String(err)}`);
    }
    finally {
        db = null;
        stmtInsert = null;
        stmtListPending = null;
        stmtMarkInflight = null;
        stmtMarkAcked = null;
        stmtMarkRejected = null;
        stmtReleaseStale = null;
        stmtReleaseByIds = null;
        stmtMarkOldStale = null;
    }
}
//# sourceMappingURL=usage-report-queue.js.map