"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.
Object.defineProperty(exports, "__esModule", { value: true });
exports.__TEST_ONLY__ = exports.OemSdkKnowledgeIngester = void 0;
/**
 * OemSdkKnowledgeIngester — pulls vendor-published SDK / tuning
 * docs and writes them to a `RagStore` under the `oem_sdk` source
 * kind. Plan 55 M2 entry layer.
 *
 * Like AOSP, OEM SDK docs require an explicit license string at
 * ingestion. Unlike AOSP, the typical license is `proprietary` —
 * the agent must surface "source unavailable" if license policy
 * later blocks retrieval (§5.2 contract).
 *
 * M2 scope:
 * - Pluggable fetcher; production wiring is operator-driven (the
 *   admin route lets curators upload pre-redacted vendor docs).
 * - Markdown / plain-text ingestion only for M2. PDF parsing is
 *   intentionally out of scope — operators should pre-extract.
 * - vendor field carried on every chunk via a deterministic
 *   `vendor::path` URI prefix so retrieval can scope by vendor.
 *
 * @module oemSdkKnowledgeIngester
 */
const crypto_1 = require("crypto");
const DEFAULT_MAX_CHUNK_CHARS = 1500;
/** Compose a stable URI from vendor + docPath so retrieval can
 * filter by vendor via prefix match. */
function uriFor(vendor, docPath) {
    return `oem://${vendor}/${docPath.replace(/^\//, '')}`;
}
function makeChunkId(uri, offset) {
    return (0, crypto_1.createHash)('sha256')
        .update(`${uri}|${offset}`)
        .digest('hex')
        .slice(0, 16);
}
function estimateTokenCount(text) {
    return Math.max(1, Math.round(text.length / 4));
}
/** Paragraph-based chunker; same shape as the blog ingester's. */
function chunkText(text, maxChars) {
    const trimmed = text.trim();
    if (trimmed.length === 0)
        return [];
    const paragraphs = trimmed
        .split(/\n\s*\n+/)
        .map(p => p.trim())
        .filter(p => p.length > 0);
    const out = [];
    let cursor = 0;
    let buf = '';
    let bufStart = 0;
    for (const p of paragraphs) {
        if (buf.length === 0) {
            buf = p;
            bufStart = cursor;
        }
        else if (buf.length + 2 + p.length <= maxChars) {
            buf += '\n\n' + p;
        }
        else {
            out.push({ text: buf, offset: bufStart });
            buf = p;
            bufStart = cursor;
        }
        cursor += p.length + 2;
    }
    if (buf.length > 0)
        out.push({ text: buf, offset: bufStart });
    return out;
}
class OemSdkKnowledgeIngester {
    constructor(store, fetcher) {
        this.store = store;
        this.fetcher = fetcher;
    }
    async ingest(opts = {}) {
        const maxChars = opts.maxChunkChars ?? DEFAULT_MAX_CHUNK_CHARS;
        const result = {
            docsProcessed: 0,
            chunksAdded: 0,
            chunksSkipped: 0,
            errors: [],
        };
        let docs;
        try {
            docs = await this.fetcher.fetchDocs({ vendor: opts.vendor });
        }
        catch (err) {
            result.errors.push({
                vendor: '<fetcher>',
                docPath: '<fetcher>',
                reason: err instanceof Error ? err.message : String(err),
            });
            return result;
        }
        for (const doc of docs) {
            result.docsProcessed++;
            if (!doc.license || doc.license.trim().length === 0) {
                result.errors.push({
                    vendor: doc.vendor,
                    docPath: doc.docPath,
                    reason: 'license required for kind=oem_sdk; entry rejected',
                });
                result.chunksSkipped++;
                continue;
            }
            if (!doc.vendor || doc.vendor.trim().length === 0) {
                result.errors.push({
                    vendor: '<missing>',
                    docPath: doc.docPath,
                    reason: 'vendor field required',
                });
                result.chunksSkipped++;
                continue;
            }
            try {
                const uri = uriFor(doc.vendor, doc.docPath);
                const packed = chunkText(doc.content, maxChars);
                for (const p of packed) {
                    const chunk = {
                        chunkId: makeChunkId(uri, p.offset),
                        kind: 'oem_sdk',
                        uri,
                        title: doc.title ?? doc.docPath.split('/').pop(),
                        snippet: p.text,
                        tokenCount: estimateTokenCount(p.text),
                        license: doc.license,
                        indexedAt: doc.fetchedAt,
                        verifiedAt: doc.fetchedAt,
                        author: doc.author,
                    };
                    this.store.addChunk(chunk);
                    result.chunksAdded++;
                }
            }
            catch (err) {
                result.errors.push({
                    vendor: doc.vendor,
                    docPath: doc.docPath,
                    reason: err instanceof Error ? err.message : String(err),
                });
                result.chunksSkipped++;
            }
        }
        return result;
    }
}
exports.OemSdkKnowledgeIngester = OemSdkKnowledgeIngester;
exports.__TEST_ONLY__ = { chunkText, makeChunkId, uriFor };
//# sourceMappingURL=oemSdkKnowledgeIngester.js.map