"use strict";
// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2024-2026 Gracker (Chris)
// This file is part of SmartPerfetto. See LICENSE for details.
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CLAUDE_SESSION_MAP_RUNTIME_TYPE = void 0;
exports.loadClaudeSessionMapFromRuntimeSnapshots = loadClaudeSessionMapFromRuntimeSnapshots;
exports.saveClaudeSessionMapToRuntimeSnapshots = saveClaudeSessionMapToRuntimeSnapshots;
exports.deleteClaudeSessionMapRuntimeSnapshots = deleteClaudeSessionMapRuntimeSnapshots;
exports.deleteClaudeSessionMapRuntimeSnapshot = deleteClaudeSessionMapRuntimeSnapshot;
const crypto_1 = __importDefault(require("crypto"));
const enterpriseDb_1 = require("./enterpriseDb");
const enterpriseRepository_1 = require("./enterpriseRepository");
exports.CLAUDE_SESSION_MAP_RUNTIME_TYPE = 'claude-session-map';
const SAFE_RUNTIME_ID_RE = /^[a-zA-Z0-9._:-]+$/;
function assertSafeRuntimeId(value, label) {
    if (!SAFE_RUNTIME_ID_RE.test(value) || value === '.' || value === '..') {
        throw new Error(`Unsafe ${label}: ${value}`);
    }
    return value;
}
function fallbackTraceId(scope) {
    return scope.traceId || `trace-${scope.sessionId}-runtime-snapshot`;
}
function fallbackRunId(scope) {
    return scope.runId || `run-${scope.sessionId}-runtime-snapshot`;
}
function runtimeSnapshotId(tenantId, workspaceId, sessionMapKey) {
    const digest = crypto_1.default
        .createHash('sha256')
        .update(`${tenantId}\0${workspaceId}\0${sessionMapKey}`)
        .digest('hex')
        .slice(0, 32);
    return `claude-session-map-${digest}`;
}
function withRuntimeSnapshotDb(fn) {
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        return fn(db);
    }
    finally {
        db.close();
    }
}
function parseSnapshotJson(row) {
    try {
        const parsed = JSON.parse(row.snapshot_json);
        if (typeof parsed.sessionMapKey !== 'string' || typeof parsed.sdkSessionId !== 'string') {
            return null;
        }
        const updatedAt = typeof parsed.updatedAt === 'number' && Number.isFinite(parsed.updatedAt)
            ? parsed.updatedAt
            : row.created_at;
        const mode = parsed.mode === 'full' ? parsed.mode : undefined;
        return [parsed.sessionMapKey, { sdkSessionId: parsed.sdkSessionId, updatedAt, ...(mode ? { mode } : {}) }];
    }
    catch {
        return null;
    }
}
function ensureRuntimeSnapshotGraph(db, scope) {
    const tenantId = assertSafeRuntimeId(scope.tenantId, 'tenant id');
    const workspaceId = assertSafeRuntimeId(scope.workspaceId, 'workspace id');
    const sessionId = assertSafeRuntimeId(scope.sessionId, 'session id');
    const userId = scope.userId ? assertSafeRuntimeId(scope.userId, 'user id') : null;
    const traceId = assertSafeRuntimeId(fallbackTraceId(scope), 'trace id');
    const runId = assertSafeRuntimeId(fallbackRunId(scope), 'run id');
    const now = Date.now();
    db.prepare(`
    INSERT OR IGNORE INTO organizations (id, name, status, plan, created_at, updated_at)
    VALUES (?, ?, 'active', 'enterprise', ?, ?)
  `).run(tenantId, tenantId, now, now);
    db.prepare(`
    INSERT OR IGNORE INTO workspaces (id, tenant_id, name, created_at, updated_at)
    VALUES (?, ?, ?, ?, ?)
  `).run(workspaceId, tenantId, workspaceId, now, now);
    if (userId) {
        db.prepare(`
      INSERT INTO users (id, tenant_id, email, display_name, idp_subject, created_at, updated_at)
      VALUES (?, ?, ?, ?, ?, ?, ?)
      ON CONFLICT(id) DO UPDATE SET
        email = excluded.email,
        display_name = excluded.display_name,
        updated_at = excluded.updated_at
    `).run(userId, tenantId, `${userId}@runtime.local`, userId, `runtime:${userId}`, now, now);
    }
    db.prepare(`
    INSERT OR IGNORE INTO trace_assets
      (id, tenant_id, workspace_id, owner_user_id, local_path, size_bytes, status, metadata_json, created_at)
    VALUES
      (?, ?, ?, ?, ?, 0, 'metadata_only', ?, ?)
  `).run(traceId, tenantId, workspaceId, userId, `metadata-only:${traceId}`, JSON.stringify({ source: 'runtime_snapshot', sessionId }), now);
    db.prepare(`
    INSERT OR IGNORE INTO analysis_sessions
      (id, tenant_id, workspace_id, trace_id, created_by, title, visibility, status, created_at, updated_at)
    VALUES
      (?, ?, ?, ?, ?, ?, 'private', 'running', ?, ?)
  `).run(sessionId, tenantId, workspaceId, traceId, userId, `Runtime ${sessionId}`, now, now);
    db.prepare(`
    INSERT OR IGNORE INTO analysis_runs
      (id, tenant_id, workspace_id, session_id, mode, status, question, started_at, completed_at)
    VALUES
      (?, ?, ?, ?, 'agent', 'running', '', ?, NULL)
  `).run(runId, tenantId, workspaceId, sessionId, now);
    return { tenantId, workspaceId, userId, sessionId, traceId, runId };
}
function loadClaudeSessionMapFromRuntimeSnapshots(maxAgeMs, now = Date.now()) {
    return withRuntimeSnapshotDb((db) => {
        const rows = db.prepare(`
      SELECT *
      FROM runtime_snapshots
      WHERE runtime_type = ?
      ORDER BY created_at ASC
    `).all(exports.CLAUDE_SESSION_MAP_RUNTIME_TYPE);
        const map = new Map();
        for (const row of rows) {
            const parsed = parseSnapshotJson(row);
            if (!parsed)
                continue;
            const [sessionMapKey, entry] = parsed;
            if (now - entry.updatedAt > maxAgeMs)
                continue;
            map.set(sessionMapKey, entry);
        }
        return map;
    });
}
function saveClaudeSessionMapToRuntimeSnapshots(scope, sessionMapKey, entry) {
    withRuntimeSnapshotDb((db) => {
        const write = db.transaction(() => {
            const graph = ensureRuntimeSnapshotGraph(db, scope);
            const id = runtimeSnapshotId(graph.tenantId, graph.workspaceId, sessionMapKey);
            const snapshotJson = JSON.stringify({
                sessionMapKey,
                sdkSessionId: entry.sdkSessionId,
                updatedAt: entry.updatedAt,
                ...(entry.mode ? { mode: entry.mode } : {}),
                traceId: graph.traceId,
            });
            const repo = (0, enterpriseRepository_1.createEnterpriseWorkspaceRepository)(db, 'runtime_snapshots');
            const changes = repo.upsertById({ tenantId: graph.tenantId, workspaceId: graph.workspaceId }, id, {
                session_id: graph.sessionId,
                run_id: graph.runId,
                runtime_type: exports.CLAUDE_SESSION_MAP_RUNTIME_TYPE,
                snapshot_json: snapshotJson,
                created_at: entry.updatedAt,
            });
            if (changes === 0) {
                throw new Error('Runtime snapshot id already exists outside the repository scope');
            }
        });
        write();
    });
}
function deleteClaudeSessionMapRuntimeSnapshots(sessionId) {
    const safeSessionId = assertSafeRuntimeId(sessionId, 'session id');
    return withRuntimeSnapshotDb((db) => {
        const rows = db.prepare(`
      SELECT *
      FROM runtime_snapshots
      WHERE runtime_type = ? AND session_id = ?
    `).all(exports.CLAUDE_SESSION_MAP_RUNTIME_TYPE, safeSessionId);
        let deleted = 0;
        const byScope = new Map();
        for (const row of rows) {
            const key = `${row.tenant_id}\0${row.workspace_id}`;
            const scopedRows = byScope.get(key) ?? [];
            scopedRows.push(row);
            byScope.set(key, scopedRows);
        }
        for (const scopedRows of byScope.values()) {
            const [first] = scopedRows;
            const repo = (0, enterpriseRepository_1.createEnterpriseWorkspaceRepository)(db, 'runtime_snapshots');
            for (const row of scopedRows) {
                deleted += repo.deleteById({
                    tenantId: first.tenant_id,
                    workspaceId: first.workspace_id,
                }, row.id);
            }
        }
        return deleted;
    });
}
function deleteClaudeSessionMapRuntimeSnapshot(sessionId, sessionMapKey, scope) {
    const safeSessionId = assertSafeRuntimeId(sessionId, 'session id');
    const safeSessionMapKey = assertSafeRuntimeId(sessionMapKey, 'session map key');
    const safeTenantId = scope?.tenantId ? assertSafeRuntimeId(scope.tenantId, 'tenant id') : undefined;
    const safeWorkspaceId = scope?.workspaceId ? assertSafeRuntimeId(scope.workspaceId, 'workspace id') : undefined;
    return withRuntimeSnapshotDb((db) => {
        const scoped = safeTenantId && safeWorkspaceId;
        const rows = scoped
            ? db.prepare(`
          SELECT *
          FROM runtime_snapshots
          WHERE runtime_type = ? AND session_id = ? AND tenant_id = ? AND workspace_id = ?
        `).all(exports.CLAUDE_SESSION_MAP_RUNTIME_TYPE, safeSessionId, safeTenantId, safeWorkspaceId)
            : db.prepare(`
          SELECT *
          FROM runtime_snapshots
          WHERE runtime_type = ? AND session_id = ?
        `).all(exports.CLAUDE_SESSION_MAP_RUNTIME_TYPE, safeSessionId);
        let deleted = 0;
        const repo = (0, enterpriseRepository_1.createEnterpriseWorkspaceRepository)(db, 'runtime_snapshots');
        for (const row of rows) {
            const parsed = parseSnapshotJson(row);
            if (!parsed)
                continue;
            const [rowSessionMapKey] = parsed;
            if (rowSessionMapKey !== safeSessionMapKey)
                continue;
            deleted += repo.deleteById({
                tenantId: row.tenant_id,
                workspaceId: row.workspace_id,
            }, row.id);
        }
        return deleted;
    });
}
//# sourceMappingURL=runtimeSnapshotStore.js.map