"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.stableStringify = stableStringify;
exports.buildTenantExportBundle = buildTenantExportBundle;
const crypto_1 = __importDefault(require("crypto"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const SENSITIVE_KEY_RE = /(api[_-]?key|token|secret|password|credential|bearer|authorization)/i;
function parseJson(value) {
    if (!value)
        return null;
    try {
        return sanitizeJson(JSON.parse(value));
    }
    catch {
        return null;
    }
}
function sanitizeJson(value) {
    if (value === null)
        return null;
    if (Array.isArray(value))
        return value.map(item => sanitizeJson(item));
    if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
        return value;
    }
    if (typeof value !== 'object')
        return null;
    const out = {};
    for (const [key, child] of Object.entries(value)) {
        out[key] = SENSITIVE_KEY_RE.test(key) ? '[redacted]' : sanitizeJson(child);
    }
    return out;
}
function canonicalize(value) {
    if (Array.isArray(value))
        return value.map(canonicalize);
    if (!value || typeof value !== 'object')
        return value;
    const input = value;
    const out = {};
    for (const key of Object.keys(input).sort()) {
        const child = input[key];
        if (child !== undefined)
            out[key] = canonicalize(child);
    }
    return out;
}
function stableStringify(value) {
    return JSON.stringify(canonicalize(value));
}
function sha256(value) {
    return `sha256:${crypto_1.default.createHash('sha256').update(value).digest('hex')}`;
}
function toIso(value) {
    return typeof value === 'number' ? new Date(value).toISOString() : null;
}
async function tryReadText(filePath) {
    try {
        return await promises_1.default.readFile(filePath, 'utf8');
    }
    catch {
        return null;
    }
}
function organizationExport(row) {
    if (!row)
        return null;
    return {
        id: row.id,
        name: row.name,
        status: row.status,
        plan: row.plan,
        createdAt: toIso(row.created_at),
        updatedAt: toIso(row.updated_at),
    };
}
function workspaceExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        name: row.name,
        retentionPolicy: parseJson(row.retention_policy),
        quotaPolicy: parseJson(row.quota_policy),
        createdAt: toIso(row.created_at),
        updatedAt: toIso(row.updated_at),
    };
}
function userExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        email: row.email,
        displayName: row.display_name,
        idpSubject: row.idp_subject,
        createdAt: toIso(row.created_at),
        updatedAt: toIso(row.updated_at),
    };
}
function membershipExport(row) {
    return {
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        userId: row.user_id,
        role: row.role,
        createdAt: toIso(row.created_at),
    };
}
function traceManifestExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        ownerUserId: row.owner_user_id,
        sha256: row.sha256,
        sizeBytes: row.size_bytes,
        status: row.status,
        metadata: parseJson(row.metadata_json),
        createdAt: toIso(row.created_at),
        expiresAt: toIso(row.expires_at),
        fileIncluded: false,
    };
}
async function reportExport(row) {
    const html = await tryReadText(row.local_path);
    const jsonPath = path_1.default.join(path_1.default.dirname(row.local_path), 'report.json');
    const reportJson = parseJson(await tryReadText(jsonPath));
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        sessionId: row.session_id,
        runId: row.run_id,
        contentHash: row.content_hash,
        visibility: row.visibility,
        createdBy: row.created_by,
        createdAt: toIso(row.created_at),
        expiresAt: toIso(row.expires_at),
        html,
        json: reportJson,
    };
}
function sessionExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        traceId: row.trace_id,
        createdBy: row.created_by,
        providerSnapshotId: row.provider_snapshot_id,
        title: row.title,
        visibility: row.visibility,
        status: row.status,
        createdAt: toIso(row.created_at),
        updatedAt: toIso(row.updated_at),
    };
}
function runExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        sessionId: row.session_id,
        mode: row.mode,
        status: row.status,
        question: row.question,
        startedAt: toIso(row.started_at),
        completedAt: toIso(row.completed_at),
        heartbeatAt: toIso(row.heartbeat_at),
        updatedAt: toIso(row.updated_at),
        error: parseJson(row.error_json),
    };
}
function turnExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        sessionId: row.session_id,
        runId: row.run_id,
        role: row.role,
        content: parseJson(row.content_json),
        createdAt: toIso(row.created_at),
    };
}
function memoryExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        scope: row.scope,
        sourceRunId: row.source_run_id,
        content: parseJson(row.content_json),
        embeddingRef: row.embedding_ref,
        createdAt: toIso(row.created_at),
        updatedAt: toIso(row.updated_at),
    };
}
function providerCredentialExport(row) {
    const policy = parseJson(row.policy_json);
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        ownerUserId: row.owner_user_id,
        scope: row.scope,
        name: row.name,
        type: row.type,
        models: parseJson(row.models_json),
        policy,
        secretConfigured: true,
        createdAt: toIso(row.created_at),
        updatedAt: toIso(row.updated_at),
    };
}
function providerSnapshotExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        providerId: row.provider_id,
        snapshotHash: row.snapshot_hash,
        runtimeKind: row.runtime_kind,
        resolvedConfig: parseJson(row.resolved_config_json),
        secretVersion: row.secret_version,
        createdAt: toIso(row.created_at),
    };
}
function auditExport(row) {
    return {
        id: row.id,
        tenantId: row.tenant_id,
        workspaceId: row.workspace_id,
        actorUserId: row.actor_user_id,
        action: row.action,
        resourceType: row.resource_type,
        resourceId: row.resource_id,
        metadata: parseJson(row.metadata_json),
        createdAt: toIso(row.created_at),
    };
}
function tenantRows(db, table, tenantId, orderBy) {
    return db.prepare(`
    SELECT *
    FROM ${table}
    WHERE tenant_id = ?
    ORDER BY ${orderBy}
  `).all(tenantId);
}
async function buildTenantExportBundle(db, context) {
    const generatedAt = new Date().toISOString();
    const organization = db.prepare(`
    SELECT *
    FROM organizations
    WHERE id = ?
    LIMIT 1
  `).get(context.tenantId) ?? null;
    const workspaces = tenantRows(db, 'workspaces', context.tenantId, 'id ASC');
    const workspaceIds = workspaces.map(workspace => workspace.id);
    const reports = await Promise.all(tenantRows(db, 'report_artifacts', context.tenantId, 'workspace_id ASC, id ASC')
        .map(reportExport));
    const traces = tenantRows(db, 'trace_assets', context.tenantId, 'workspace_id ASC, id ASC')
        .map(traceManifestExport);
    const sessions = tenantRows(db, 'analysis_sessions', context.tenantId, 'workspace_id ASC, created_at ASC, id ASC').map(sessionExport);
    const runs = tenantRows(db, 'analysis_runs', context.tenantId, 'workspace_id ASC, started_at ASC, id ASC').map(runExport);
    const turns = tenantRows(db, 'conversation_turns', context.tenantId, 'workspace_id ASC, created_at ASC, id ASC').map(turnExport);
    const memoryEntries = tenantRows(db, 'memory_entries', context.tenantId, 'workspace_id ASC, updated_at ASC, id ASC').map(memoryExport);
    const auditEvents = tenantRows(db, 'audit_events', context.tenantId, 'created_at ASC, id ASC').map(auditExport);
    const providerCredentials = tenantRows(db, 'provider_credentials', context.tenantId, 'COALESCE(workspace_id, \'\') ASC, id ASC').map(providerCredentialExport);
    const providerSnapshots = tenantRows(db, 'provider_snapshots', context.tenantId, 'created_at ASC, id ASC').map(providerSnapshotExport);
    const tenant = {
        organization: organizationExport(organization),
        workspaces: workspaces.map(workspaceExport),
        users: tenantRows(db, 'users', context.tenantId, 'id ASC').map(userExport),
        memberships: tenantRows(db, 'memberships', context.tenantId, 'workspace_id ASC, user_id ASC').map(membershipExport),
    };
    const organizationHash = sha256(stableStringify(tenant.organization));
    const proofPayload = {
        tenantId: context.tenantId,
        organizationHash,
        workspaceIds,
        generatedAt,
        generatedBy: context.userId,
        requestId: context.requestId,
    };
    const bundle = {
        schemaVersion: 1,
        generatedAt,
        tenantIdentityProof: {
            tenantId: context.tenantId,
            organizationHash,
            workspaceIds,
            generatedBy: context.userId,
            requestId: context.requestId,
            proofHash: sha256(stableStringify(proofPayload)),
        },
        manifest: {
            traceFilesIncluded: false,
            traceCount: traces.length,
            reportCount: reports.length,
            sessionCount: sessions.length,
            runCount: runs.length,
            turnCount: turns.length,
            memoryRecordCount: memoryEntries.length,
            auditEventCount: auditEvents.length,
            providerCredentialCount: providerCredentials.length,
            providerSnapshotCount: providerSnapshots.length,
        },
        tenant,
        traces,
        reports,
        sessions,
        runs,
        turns,
        knowledge: {
            memoryEntries,
        },
        auditEvents,
        providers: {
            credentials: providerCredentials,
            snapshots: providerSnapshots,
        },
    };
    const canonicalPayload = stableStringify(bundle);
    const bundleSha256 = sha256(canonicalPayload);
    const generatedForFilename = generatedAt.replace(/[:.]/g, '-');
    return {
        bundle,
        bundleSha256,
        canonicalPayload,
        filename: `smartperfetto-tenant-${context.tenantId}-${generatedForFilename}.json`,
    };
}
//# sourceMappingURL=enterpriseTenantExportService.js.map