"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 __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.__TEST_ONLY__ = exports.AppSourceIngester = void 0;
const crypto_1 = require("crypto");
const fsPromises = __importStar(require("fs/promises"));
const path = __importStar(require("path"));
const secretPatterns_1 = require("../security/secretPatterns");
const pathSecurityGate_1 = require("../codebase/pathSecurityGate");
const baseIngester_1 = require("./baseIngester");
const DEFAULT_MAX_CHUNK_CHARS = 2200;
function makeChunkId(codebaseId, indexGeneration, relativePath, startLine) {
    return (0, crypto_1.createHash)('sha256')
        .update(`${codebaseId}|${indexGeneration}|${relativePath}|${startLine}`)
        .digest('hex')
        .slice(0, 16);
}
const chunkSource = baseIngester_1.chunkSourceBySymbols;
function filterPreviewFiles(preview, ref, opts) {
    const prefixes = [
        ...(ref.pathFilters ?? []),
        ...(opts.pathPrefix ? [opts.pathPrefix] : []),
    ].filter(Boolean);
    if (prefixes.length === 0)
        return preview.acceptedFiles;
    return preview.acceptedFiles.filter(file => prefixes.some(prefix => file.relativePath.startsWith(prefix.replace(/^\//, ''))));
}
class AppSourceIngester {
    constructor(store, registry, gate = new pathSecurityGate_1.PathSecurityGate()) {
        this.store = store;
        this.registry = registry;
        this.gate = gate;
    }
    async ingest(codebaseId, opts = {}) {
        const ref = this.registry.get(codebaseId);
        if (!ref) {
            throw new Error(`Codebase '${codebaseId}' not found`);
        }
        if (ref.kind !== 'app_source') {
            throw new Error(`Codebase '${codebaseId}' is kind=${ref.kind}; app source ingestion requires app_source`);
        }
        const preview = await this.gate.preview(ref.rootRealpath);
        if (preview.blocked) {
            this.registry.updateIngestStatus(codebaseId, {
                lastIngestStatus: 'blocked_by_security',
                lastIngestAt: Date.now(),
                lastIngestError: preview.blockedReason,
                blockedFileCount: preview.skippedFiles.length,
            });
            return {
                codebaseId,
                filesProcessed: 0,
                chunksAdded: 0,
                chunksSkipped: 0,
                blockedFileCount: preview.skippedFiles.length,
                redactionHitCount: 0,
                errors: [{ filePath: ref.displayName, reason: preview.blockedReason ?? 'blocked' }],
            };
        }
        const maxChars = opts.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS;
        const result = {
            codebaseId,
            filesProcessed: 0,
            chunksAdded: 0,
            chunksSkipped: 0,
            blockedFileCount: preview.skippedFiles.length,
            redactionHitCount: 0,
            errors: [],
        };
        for (const file of filterPreviewFiles(preview, ref, opts)) {
            result.filesProcessed++;
            await new Promise(r => setImmediate(r));
            try {
                const absolutePath = path.join(ref.rootRealpath, file.relativePath);
                const content = await fsPromises.readFile(absolutePath, 'utf-8');
                const chunks = chunkSource(content, maxChars);
                if (chunks.length === 0) {
                    result.chunksSkipped++;
                    continue;
                }
                for (const chunk of chunks) {
                    const redaction = (0, secretPatterns_1.redactSecrets)(chunk.text);
                    result.redactionHitCount += redaction.redactedCount;
                    this.store.addChunk({
                        chunkId: makeChunkId(codebaseId, ref.indexGeneration, file.relativePath, chunk.startLine),
                        kind: 'app_source',
                        uri: `codebase://${codebaseId}/${file.relativePath}`,
                        title: file.relativePath.split('/').pop(),
                        snippet: chunk.text,
                        tokenCount: (0, baseIngester_1.estimateTokenCount)(chunk.text),
                        indexedAt: Date.now(),
                        filePath: file.relativePath,
                        lineRange: { start: chunk.startLine, end: chunk.endLine },
                        ...(chunk.symbol ? { symbol: chunk.symbol } : {}),
                        language: (0, baseIngester_1.languageForPath)(file.relativePath),
                        ...(ref.commitHash ? { commitHash: ref.commitHash } : {}),
                        ...(ref.vendor ? { vendor: ref.vendor } : {}),
                        ...(ref.buildId ? { buildId: ref.buildId } : {}),
                        codebaseId,
                        registryOrigin: 'codebase_registry',
                    });
                    result.chunksAdded++;
                }
            }
            catch (error) {
                result.chunksSkipped++;
                result.errors.push({
                    filePath: file.relativePath,
                    reason: error instanceof Error ? error.message : String(error),
                });
            }
        }
        this.store.flush();
        this.registry.updateIngestStatus(codebaseId, {
            lastIngestStatus: result.errors.length > 0 ? 'partial' : 'ok',
            lastIngestAt: Date.now(),
            lastIngestError: result.errors[0]?.reason,
            chunkCount: result.chunksAdded,
            blockedFileCount: result.blockedFileCount,
            redactionHitCount: result.redactionHitCount,
        });
        return result;
    }
}
exports.AppSourceIngester = AppSourceIngester;
exports.__TEST_ONLY__ = {
    chunkSource,
    detectSymbol: baseIngester_1.detectSourceSymbol,
    makeChunkId,
    languageFor: baseIngester_1.languageForPath,
};
//# sourceMappingURL=appSourceIngester.js.map