"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.enterpriseKnowledgeStoreEnabled = enterpriseKnowledgeStoreEnabled;
exports.enterpriseKnowledgeDbWritesEnabled = enterpriseKnowledgeDbWritesEnabled;
exports.legacyKnowledgeFilesystemWritesEnabled = legacyKnowledgeFilesystemWritesEnabled;
exports.knowledgeScopeFromRequestContext = knowledgeScopeFromRequestContext;
exports.resolveKnowledgeScope = resolveKnowledgeScope;
exports.scopedKnowledgeRowId = scopedKnowledgeRowId;
exports.upsertScopedKnowledgeRecord = upsertScopedKnowledgeRecord;
exports.getScopedKnowledgeRecord = getScopedKnowledgeRecord;
exports.removeScopedKnowledgeRecord = removeScopedKnowledgeRecord;
exports.listScopedKnowledgeRecords = listScopedKnowledgeRecords;
const crypto_1 = __importDefault(require("crypto"));
const enterpriseDb_1 = require("./enterpriseDb");
const enterpriseRepository_1 = require("./enterpriseRepository");
const enterpriseMigration_1 = require("./enterpriseMigration");
const DEFAULT_TENANT_ID = 'default-dev-tenant';
const DEFAULT_WORKSPACE_ID = 'default-workspace';
const DEFAULT_USER_ID = 'dev-user-123';
const SAFE_SCOPE_SEGMENT_RE = /^[a-zA-Z0-9._:-]+$/;
function enterpriseKnowledgeStoreEnabled(env = process.env) {
    return (0, enterpriseMigration_1.enterpriseDbReadAuthorityEnabled)(env);
}
function enterpriseKnowledgeDbWritesEnabled(env = process.env) {
    return (0, enterpriseMigration_1.enterpriseDbWritesEnabled)(env);
}
function legacyKnowledgeFilesystemWritesEnabled(env = process.env) {
    return (0, enterpriseMigration_1.legacyFilesystemWritesEnabled)(env);
}
function knowledgeScopeFromRequestContext(context) {
    return {
        tenantId: context.tenantId,
        workspaceId: context.workspaceId,
        userId: context.userId,
    };
}
function resolveKnowledgeScope(scope = {}) {
    const tenantId = sanitizeScopeSegment(scope.tenantId || DEFAULT_TENANT_ID, 'tenantId');
    const workspaceId = sanitizeScopeSegment(scope.workspaceId || DEFAULT_WORKSPACE_ID, 'workspaceId');
    const userId = scope.userId
        ? sanitizeScopeSegment(scope.userId, 'userId')
        : DEFAULT_USER_ID;
    const sourceRunId = scope.sourceRunId || scope.runId;
    return {
        tenantId,
        workspaceId,
        ...(userId ? { userId } : {}),
        ...(sourceRunId
            ? { sourceRunId: sanitizeScopeSegment(sourceRunId, 'sourceRunId') }
            : {}),
    };
}
function scopedKnowledgeRowId(kind, externalId, scope) {
    const digest = crypto_1.default
        .createHash('sha256')
        .update(`${scope.tenantId}\0${scope.workspaceId}\0${kind}\0${externalId}`)
        .digest('hex')
        .slice(0, 32);
    return `knowledge-${digest}`;
}
function upsertScopedKnowledgeRecord(kind, externalId, rowScope, record, scopeInput, opts = {}) {
    const scope = resolveKnowledgeScope(scopeInput);
    const now = Date.now();
    const createdAt = opts.createdAt ?? now;
    const updatedAt = opts.updatedAt ?? now;
    withKnowledgeDb((db) => {
        const tx = db.transaction(() => {
            ensureEnterpriseKnowledgeGraph(db, scope);
            const sourceRunId = resolveSourceRunId(db, opts.sourceRunId || scope.sourceRunId);
            const envelope = {
                schemaVersion: 1,
                kind,
                externalId,
                sourceTenantId: scope.tenantId,
                sourceWorkspaceId: scope.workspaceId,
                ...(sourceRunId ? { sourceRunId } : {}),
                record,
            };
            const repo = (0, enterpriseRepository_1.createEnterpriseWorkspaceRepository)(db, 'memory_entries');
            const rowId = scopedKnowledgeRowId(kind, externalId, scope);
            const updateValues = {
                scope: rowScope,
                source_run_id: sourceRunId,
                content_json: JSON.stringify(envelope),
                embedding_ref: opts.embeddingRef ?? null,
                updated_at: updatedAt,
            };
            const existing = repo.getById(scope, rowId);
            const changes = existing
                ? repo.updateById(scope, rowId, updateValues)
                : repo.upsertById(scope, rowId, {
                    ...updateValues,
                    created_at: createdAt,
                });
            if (changes === 0) {
                throw new Error('Knowledge entry id already exists outside the repository scope');
            }
        });
        tx();
    });
}
function getScopedKnowledgeRecord(kind, externalId, scopeInput) {
    const scope = resolveKnowledgeScope(scopeInput);
    return withKnowledgeDb((db) => {
        const repo = (0, enterpriseRepository_1.createEnterpriseWorkspaceRepository)(db, 'memory_entries');
        const row = repo.getById(scope, scopedKnowledgeRowId(kind, externalId, scope));
        return row ? parseKnowledgeRow(kind, row) : undefined;
    });
}
function removeScopedKnowledgeRecord(kind, externalId, scopeInput) {
    const scope = resolveKnowledgeScope(scopeInput);
    return withKnowledgeDb((db) => {
        const repo = (0, enterpriseRepository_1.createEnterpriseWorkspaceRepository)(db, 'memory_entries');
        return repo.deleteById(scope, scopedKnowledgeRowId(kind, externalId, scope)) > 0;
    });
}
function listScopedKnowledgeRecords(kind, scopeInput, opts = {}) {
    const scope = resolveKnowledgeScope(scopeInput);
    return withKnowledgeDb((db) => {
        const params = {
            tenantId: scope.tenantId,
            workspaceId: scope.workspaceId,
            systemTenantId: 'system',
            systemWorkspaceId: 'system',
        };
        const ownerClause = opts.includeSystem
            ? `((tenant_id = @tenantId AND workspace_id = @workspaceId)
          OR (tenant_id = @systemTenantId AND workspace_id = @systemWorkspaceId))`
            : `(tenant_id = @tenantId AND workspace_id = @workspaceId)`;
        let scopeClause = '';
        if (opts.rowScope !== undefined) {
            params.rowScope = opts.rowScope;
            scopeClause = 'AND scope = @rowScope';
        }
        else if (opts.rowScopePrefix !== undefined) {
            params.rowScopePrefix = `${opts.rowScopePrefix}%`;
            scopeClause = 'AND scope LIKE @rowScopePrefix';
        }
        const rows = db.prepare(`
      SELECT *
      FROM memory_entries
      WHERE ${ownerClause}
        ${scopeClause}
      ORDER BY updated_at DESC, id ASC
    `).all(params);
        return rows
            .map(row => parseKnowledgeRow(kind, row))
            .filter((record) => Boolean(record));
    });
}
function withKnowledgeDb(fn) {
    const db = (0, enterpriseDb_1.openEnterpriseDb)();
    try {
        return fn(db);
    }
    finally {
        db.close();
    }
}
function sanitizeScopeSegment(value, label) {
    if (!SAFE_SCOPE_SEGMENT_RE.test(value) || value === '.' || value === '..') {
        throw new Error(`Unsafe knowledge ${label}: ${value}`);
    }
    return value;
}
function ensureEnterpriseKnowledgeGraph(db, scope) {
    const now = Date.now();
    db.prepare(`
    INSERT OR IGNORE INTO organizations (id, name, status, plan, created_at, updated_at)
    VALUES (?, ?, 'active', 'enterprise', ?, ?)
  `).run(scope.tenantId, scope.tenantId, now, now);
    db.prepare(`
    INSERT OR IGNORE INTO workspaces (id, tenant_id, name, created_at, updated_at)
    VALUES (?, ?, ?, ?, ?)
  `).run(scope.workspaceId, scope.tenantId, scope.workspaceId, now, now);
    if (scope.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(scope.userId, scope.tenantId, `${scope.userId}@knowledge.local`, scope.userId, `knowledge:${scope.userId}`, now, now);
    }
}
function resolveSourceRunId(db, sourceRunId) {
    if (!sourceRunId)
        return null;
    const row = db.prepare(`
    SELECT id
    FROM analysis_runs
    WHERE id = ?
    LIMIT 1
  `).get(sourceRunId);
    return row ? sourceRunId : null;
}
function parseKnowledgeRow(kind, row) {
    try {
        const parsed = JSON.parse(row.content_json);
        if (parsed.schemaVersion !== 1 ||
            parsed.kind !== kind ||
            parsed.externalId === undefined) {
            return undefined;
        }
        return {
            externalId: parsed.externalId,
            rowScope: row.scope,
            record: parsed.record,
            ...(row.source_run_id ? { sourceRunId: row.source_run_id } : {}),
            createdAt: row.created_at,
            updatedAt: row.updated_at,
        };
    }
    catch {
        return undefined;
    }
}
//# sourceMappingURL=scopedKnowledgeStore.js.map