"use strict";
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 __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
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;
    };
})();
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var IngestMetadataService_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.IngestMetadataService = void 0;
const common_1 = require("@nestjs/common");
const crypto = __importStar(require("crypto"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const module_helper_1 = require("../../helpers/module.helper");
const r2r_helper_service_1 = require("./r2r-helper.service");
let IngestMetadataService = IngestMetadataService_1 = class IngestMetadataService {
    constructor(r2rService) {
        this.r2rService = r2rService;
        this.logger = new common_1.Logger(IngestMetadataService_1.name);
    }
    _sha256OfJson(obj) {
        const s = JSON.stringify(obj);
        return crypto.createHash('sha256').update(s).digest('hex');
    }
    _oneLineBool(b) {
        return b ? 'yes' : 'no';
    }
    _shortList(arr, max = 10) {
        if (!Array.isArray(arr) || arr.length === 0)
            return 'none';
        const a = arr.slice(0, max);
        return `${a.join(', ')}${arr.length > max ? ', …' : ''}`;
    }
    async ingest() {
        this.ragClient = await this.r2rService.getClient();
        this.logger.debug(`getting dynamics modules`);
        const enabledModules = (0, module_helper_1.getDynamicModuleNamesBasedOnMetadata)();
        this.logger.log(`ingesting metadata`);
        for (let i = 0; i < enabledModules.length; i++) {
            const enabledModule = enabledModules[i];
            const fileName = `${enabledModule}-metadata.json`;
            const enabledModuleSeedFile = `module-metadata/${enabledModule}/${fileName}`;
            const fullPath = path.join(process.cwd(), enabledModuleSeedFile);
            const overallMetadata = JSON.parse(fs.readFileSync(fullPath, 'utf-8').toString());
            const moduleMetadata = overallMetadata.moduleMetadata;
            const enabledModulIngestionInfoFile = `module-metadata/${enabledModule}/genai/${enabledModule}-ingested-info.json`;
            const enabledModulIngestionInfoFullPath = path.join(process.cwd(), enabledModulIngestionInfoFile);
            const ingestionInfo = fs.existsSync(enabledModulIngestionInfoFullPath) ? JSON.parse(fs.readFileSync(enabledModulIngestionInfoFullPath, 'utf-8').toString()) : {};
            const enabledModulIngestionInfoDir = path.dirname(enabledModulIngestionInfoFullPath);
            if (!fs.existsSync(enabledModulIngestionInfoDir)) {
                fs.mkdirSync(enabledModulIngestionInfoDir, { recursive: true });
            }
            ingestionInfo.moduleName = enabledModule;
            this.logger.log(`[Start] Processing module metadata for ${moduleMetadata.name}`);
            const collectionId = await this.resolveRagCollectionForModule(ingestionInfo, enabledModule);
            ingestionInfo.collectionId = collectionId;
            await this.deleteInsertRagChunkForModule(ingestionInfo, moduleMetadata);
            for (const model of moduleMetadata.models) {
                await this.deleteInsertRagChunkForModel(ingestionInfo, enabledModule, model);
            }
            fs.writeFileSync(enabledModulIngestionInfoFullPath, JSON.stringify({ ...ingestionInfo }, null, 2), 'utf8');
        }
    }
    async resolveRagCollectionForModule(ingestionInfo, moduleName) {
        this.logger.debug(`Resolving RAG collection for module: ${moduleName}`);
        const collectionName = `${moduleName}-rag-collection`;
        const existingCollection = await this.ragClient.collections.retrieveByName({
            name: collectionName
        });
        if (existingCollection) {
            try {
                await this.ragClient.collections.delete({ id: existingCollection.results.id });
            }
            catch (e) {
                this.logger.warn(`[Warn] Failed deleteByFilter for collection': ${String(e)}`);
            }
        }
        const r = await this.ragClient.collections.create({
            name: collectionName,
            description: `Collection created to group all documents, chunks related to module: ${moduleName}`
        });
        return r.results.id;
    }
    async deleteInsertRagDocumentForModuleMetadataJsonFile(ingestionInfo, fullPath, fileName) {
        this.logger.debug(`Ingesting file: ${fullPath}`);
        const jsonStr = fs.readFileSync(fullPath, 'utf-8');
        const contentHash = this._sha256OfJson(JSON.parse(jsonStr));
        if (ingestionInfo.documentHash === contentHash && ingestionInfo.documentId) {
            this.logger.log(`[Skip] Unchanged: ${fileName} (hash=${contentHash.slice(0, 8)}…)`);
            return;
        }
        if (ingestionInfo.documentId) {
            try {
                await this.ragClient.documents.delete({ id: ingestionInfo.documentId });
            }
            catch (e) {
                this.logger.warn(`[Warn] Failed deleting prior document ${ingestionInfo.documentId}: ${String(e)}`);
            }
        }
        const ingestResult = await this.ragClient.documents.create({
            file: {
                path: fullPath,
                name: fileName
            },
            collectionIds: [ingestionInfo.collectionId],
            metadata: {
                contentHash,
                fileName
            },
        });
        const newId = ingestResult?.results?.documentId;
        if (!newId) {
            throw new Error(`R2R did not return a documentId for ${fileName}`);
        }
        ingestionInfo.documentId = newId;
        ingestionInfo.documentHash = contentHash;
        this.logger.log(`[OK] Ingested ${fileName} → id=${newId}, hash=${contentHash.slice(0, 8)}…`);
    }
    async deleteInsertRagChunkForModule(ingestionInfo, moduleMetadata) {
        const moduleName = moduleMetadata?.name;
        const schemaHash = this._sha256OfJson({
            name: moduleMetadata?.name ?? null,
            description: moduleMetadata?.description ?? null,
            models: (moduleMetadata?.models ?? []).map((m) => ({
                singularName: m?.singularName ?? null,
                description: m?.description ?? null,
            })),
        });
        if (ingestionInfo.moduleHash === schemaHash && ingestionInfo.moduleChunkId) {
            this.logger.log(`[Skip] Module unchanged: ${moduleName}`);
            return;
        }
        const models = moduleMetadata?.models ?? [];
        const modelLines = models.map((m) => {
            const name = m?.singularName;
            const desc = m?.description ? `: ${m.description}` : '';
            return `- ${name}${desc}`;
        });
        const text = `SolidX Module: ${moduleName}
Purpose: ${moduleMetadata?.description ?? 'N/A'}

Models (${models.length}):
${modelLines.join('\n')}

Usage: Use this chunk to choose the correct model/field chunks for code generation or metadata edits.`;
        const metadata = {
            collectionName: `${moduleName}-rag-collection`,
            kind: 'solidx-metadata',
            type: 'module',
            moduleName,
            modelCount: models.length,
            schemaHash,
            models: models.map((m) => m?.singularName).filter(Boolean),
        };
        try {
            const deleteResult = await this.ragClient.documents.deleteByFilter({
                filters: {
                    "$and": [
                        { 'metadata.kind': { $eq: 'solidx-metadata' } },
                        { 'metadata.type': { $eq: 'module' } },
                        { 'metadata.moduleName': { $eq: moduleName } },
                        { 'metadata.collectionName': { $eq: `${moduleName}-rag-collection` } },
                    ]
                },
            });
            this.logger.log(`Deleted existing module document for moduleName: ${moduleName}. Got response:`, deleteResult);
        }
        catch (e) {
            this.logger.warn(`[Warn] Failed deleteByFilter for module '${moduleName}': ${String(e)}`);
        }
        const r = await this.ragClient.documents.create({
            chunks: [text],
            metadata: metadata,
            collectionIds: [ingestionInfo.collectionId],
        });
        const newId = r?.results?.documentId;
        if (!newId) {
            throw new Error(`R2R did not return a documentId while creating module chunk for module name ${moduleName}`);
        }
        ingestionInfo.moduleChunkId = r.results?.documentId;
        ingestionInfo.moduleHash = schemaHash;
        this.logger.log(`[OK] Ingested module ${moduleName} → id=${newId}, hash=${schemaHash.slice(0, 8)}…`);
    }
    async deleteInsertRagChunkForModel(ingestionInfo, moduleName, model) {
        const modelName = model?.singularName;
        const schemaHash = this._sha256OfJson(model);
        const modelsArr = ingestionInfo.models ?? (ingestionInfo.models = []);
        let modelEntry = modelsArr.find(m => m.modelName === modelName);
        if (!modelEntry) {
            modelEntry = { modelName, fields: [] };
            modelsArr.push(modelEntry);
        }
        if (modelEntry.modelHash === schemaHash && modelEntry.modelChunkId) {
            this.logger.log(`[Skip] Model unchanged: ${moduleName}.${modelName}`);
            return;
        }
        const fields = Array.isArray(model?.fields) ? model.fields : [];
        const userkey = fields.find((f) => f?.isUserKey)?.name ?? 'id';
        const uniques = fields.filter((f) => f?.unique).map((f) => f.name);
        const required = fields.filter((f) => f?.required).map((f) => f.name);
        const rels = fields
            .filter((f) => f.type === 'relation')
            .map((f) => `${modelName}.${f.name} -> ${f.relationCoModelSingularName}.${f.relationCoModelColumnName ?? 'id'}`);
        const fieldSummaryLines = fields.slice(0, 30).map((f) => {
            const bits = [
                `${f.name}:${f.type}`,
                f.required ? 'req' : '',
                f.unique ? 'unique' : '',
                f.isUserKey ? 'userkey' : '',
                f.relationCoModelSingularName ? `rel->${f.relationCoModelSingularName}` : '',
            ].filter(Boolean).join('|');
            return `- ${bits}`;
        });
        const text = `SolidX Model: ${modelName}
Module: ${moduleName}
Purpose: ${model?.description ?? 'N/A'}

Signature:
- Primary: ${userkey}
- Unique: ${uniques.length ? uniques.join(', ') : 'none'}
- Required (${required.length}): ${required.slice(0, 12).join(', ')}${required.length > 12 ? '…' : ''}

Relations (${rels.length}):
${rels.length ? `- ${rels.join('\n- ')}` : 'None'}

Fields (${fields.length}) [name:type|flags]:
${fieldSummaryLines.join('\n')}

Usage: Use this chunk to generate DTOs, subscribers, custom service methods, and CRUD handlers for ${modelName}.

Full model metadata json: 
${JSON.stringify(model)}

`;
        const metadata = {
            collectionName: `${moduleName}-rag-collection`,
            kind: 'solidx-metadata',
            type: 'model',
            moduleName,
            modelName,
            fieldCount: fields.length,
            requiredCount: required.length,
            relationCount: rels.length,
            userkey: userkey,
            uniqueFields: uniques,
            hasTimestamps: !!fields.find((f) => ['time', 'date', 'datetime'].includes(f.type)),
            schemaHash,
        };
        try {
            const deleteResult = await this.ragClient.documents.deleteByFilter({
                filters: {
                    "$and": [
                        { 'metadata.kind': { $eq: 'solidx-metadata' } },
                        { 'metadata.type': { $eq: 'model' } },
                        { 'metadata.moduleName': { $eq: moduleName } },
                        { 'metadata.modelName': { $eq: modelName } },
                        { 'metadata.collectionName': { $eq: `${moduleName}-rag-collection` } },
                    ]
                },
            });
            this.logger.log(`Deleted existing model document for modelName: ${modelName}. Got response:`, deleteResult);
        }
        catch (e) {
            this.logger.warn(`[Warn] Failed deleteByFilter for module '${moduleName}': ${String(e)}`);
        }
        const r = await this.ragClient.documents.create({
            chunks: [text],
            metadata,
            collectionIds: [ingestionInfo.collectionId],
        });
        const newId = r?.results?.documentId;
        if (!newId) {
            throw new Error(`R2R did not return a documentId while creating model chunk for ${moduleName}.${modelName}`);
        }
        modelEntry.modelChunkId = newId;
        modelEntry.modelHash = schemaHash;
        this.logger.log(`[OK] Ingested model ${moduleName}.${modelName} → id=${newId}, hash=${schemaHash.slice(0, 8)}…`);
    }
    _buildFieldTextAndMetadata(moduleName, modelName, f) {
        const name = f?.name;
        const displayName = f?.displayName ?? name;
        const description = f?.description ?? 'N/A';
        const type = f?.type;
        const ormType = f?.ormType ?? null;
        const required = !!f?.required;
        const unique = !!f?.unique;
        const index = !!f?.index;
        const length = f?.length ?? null;
        const min = f?.min ?? null;
        const max = f?.max ?? null;
        const defaultValue = f?.defaultValue ?? null;
        const regex = f?.regexPattern ?? null;
        const regexErr = f?.regexPatternNotMatchingErrorMsg ?? null;
        const relationType = f?.relationType ?? null;
        const relModule = f?.relationModelModuleName ?? null;
        const relModel = f?.relationCoModelSingularName ?? null;
        const relField = f?.relationCoModelFieldName ?? 'id';
        const relCascade = f?.relationCascade ?? null;
        const relFixedFilter = f?.relationFieldFixedFilter ?? null;
        const selectionDynProvider = f?.selectionDynamicProvider ?? null;
        const selectionDynCtxt = f?.selectionDynamicProviderCtxt ?? null;
        const selectionStatic = Array.isArray(f?.selectionStaticValues) ? f.selectionStaticValues : null;
        const selectionValueType = f?.selectionValueType ?? null;
        const isMultiSelect = !!f?.isMultiSelect;
        const mediaTypes = Array.isArray(f?.mediaTypes) ? f.mediaTypes : null;
        const mediaMaxSizeKb = f?.mediaMaxSizeKb ?? null;
        const mediaStorageProvider = f?.mediaStorageProviderUserKey ?? null;
        const computedProvider = f?.computedFieldValueProvider ?? null;
        const computedProviderCtxt = f?.computedFieldValueProviderCtxt ?? null;
        const computedValueType = f?.computedFieldValueType ?? null;
        const computedTriggerCfg = f?.computedFieldTriggerConfig ?? null;
        const isPrivate = !!f?.private;
        const enableAuditTracking = !!f?.enableAuditTracking;
        const isUserKey = !!f?.isUserKey;
        const isSystem = !!f?.isSystem;
        const isMarkedForRemoval = !!f?.isMarkedForRemoval;
        const columnName = f?.columnName ?? null;
        const relCoModelColumn = f?.relationCoModelColumnName ?? null;
        const uuid = f?.uuid ?? null;
        const relationSummary = (() => {
            if (!relationType || !relModel)
                return 'none';
            const parts = [
                `type=${relationType}`,
                relModule ? `module=${relModule}` : null,
                `model=${relModel}`,
                relField ? `field=${relField}` : null,
                relCascade ? `cascade=${relCascade}` : null,
                relFixedFilter ? `fixedFilter=${relFixedFilter}` : null,
            ].filter(Boolean);
            return parts.join(', ');
        })();
        const selectionSummary = (() => {
            const parts = [];
            if (selectionDynProvider)
                parts.push(`dynamicProvider=${selectionDynProvider}`);
            if (selectionDynCtxt)
                parts.push(`dynamicCtxt=${selectionDynCtxt}`);
            if (selectionStatic?.length)
                parts.push(`static=[${this._shortList(selectionStatic, 12)}]`);
            if (selectionValueType)
                parts.push(`valueType=${selectionValueType}`);
            parts.push(`multiSelect=${this._oneLineBool(isMultiSelect)}`);
            return parts.length ? parts.join(', ') : 'none';
        })();
        const mediaSummary = (() => {
            const parts = [];
            if (mediaTypes?.length)
                parts.push(`types=[${this._shortList(mediaTypes, 12)}]`);
            if (mediaMaxSizeKb)
                parts.push(`maxSizeKb=${mediaMaxSizeKb}`);
            if (mediaStorageProvider)
                parts.push(`storageProvider=${typeof mediaStorageProvider === 'string' ? mediaStorageProvider : 'set'}`);
            return parts.length ? parts.join(', ') : 'none';
        })();
        const computedSummary = (() => {
            const parts = [];
            if (computedProvider)
                parts.push(`provider=${computedProvider}`);
            if (computedProviderCtxt)
                parts.push(`providerCtxt=${computedProviderCtxt}`);
            if (computedValueType)
                parts.push(`valueType=${computedValueType}`);
            if (computedTriggerCfg?.length)
                parts.push(`triggers=${computedTriggerCfg.length}`);
            return parts.length ? parts.join(', ') : 'none';
        })();
        const securitySummary = [
            `private=${this._oneLineBool(isPrivate)}`,
            `auditTracking=${this._oneLineBool(enableAuditTracking)}`
        ].filter(Boolean).join(', ');
        const constraintSummary = [
            `required=${this._oneLineBool(required)}`,
            `unique=${this._oneLineBool(unique)}`,
            `index=${this._oneLineBool(index)}`,
            length !== null ? `length=${length}` : null,
            min !== null ? `min=${min}` : null,
            max !== null ? `max=${max}` : null,
            defaultValue !== null ? `default=${defaultValue}` : null,
            regex ? `regex=${regex}${regexErr ? ` (${regexErr})` : ''}` : null
        ].filter(Boolean).join(', ');
        const mappingSummary = [
            columnName ? `column=${columnName}` : null,
            relCoModelColumn ? `relColumn=${relCoModelColumn}` : null,
            uuid ? `uuid=${uuid}` : null,
            `userKey=${this._oneLineBool(isUserKey)}`,
            `system=${this._oneLineBool(isSystem)}`,
            `markedForRemoval=${this._oneLineBool(isMarkedForRemoval)}`
        ].filter(Boolean).join(', ');
        const text = [
            `SolidX Field: ${name} (${displayName})`,
            `Model: ${modelName}`,
            `Module: ${moduleName}`,
            ``,
            `Type: ${type}${ormType ? ` (orm=${ormType})` : ''}`,
            `Description: ${description}`,
            ``,
            `Constraints: ${constraintSummary || 'none'}`,
            `Relation: ${relationSummary}`,
            `Selection: ${selectionSummary}`,
            `Media: ${mediaSummary}`,
            `Computed: ${computedSummary}`,
            `Security/Privacy/Audit: ${securitySummary}`,
            `Mapping/Flags: ${mappingSummary || 'none'}`,
            ``,
            `Usage: Use this chunk to generate exact field contracts (DTO, form control, DB column), `,
            `validation rules, relation wiring, and UI widgets (selection/media/computed).`,
        ].join('\n');
        const metadata = {
            kind: 'solidx-metadata',
            type: 'field',
            moduleName,
            modelName,
            fieldName: name,
            displayName,
            description,
            dataType: type,
            ormType,
            required,
            unique,
            index,
            defaultValue,
            length,
            min,
            max,
            regexPattern: regex,
            regexPatternNotMatchingErrorMsg: regexErr,
            relationType,
            relationModelModuleName: relModule,
            relationCoModelSingularName: relModel,
            relationCoModelFieldName: relField,
            relationCascade: relCascade,
            relationFieldFixedFilter: relFixedFilter,
            selectionDynProvider,
            selectionDynCtxt,
            selectionStaticValues: selectionStatic,
            selectionValueType,
            isMultiSelect,
            mediaTypes,
            mediaMaxSizeKb,
            mediaStorageProvider: mediaStorageProvider ? (typeof mediaStorageProvider === 'string' ? mediaStorageProvider : 'set') : null,
            computedFieldValueProvider: computedProvider,
            computedFieldValueProviderCtxt: computedProviderCtxt,
            computedFieldValueType: computedValueType,
            computedFieldTriggerConfigCount: Array.isArray(computedTriggerCfg) ? computedTriggerCfg.length : 0,
            private: isPrivate,
            enableAuditTracking,
            columnName,
            relationCoModelColumnName: relCoModelColumn,
            isUserKey,
            isSystem,
            isMarkedForRemoval,
        };
        return { text, metadata };
    }
    async deleteInsertRagChunkForField(ingestionInfo, moduleName, modelName, field) {
        const fieldName = field?.name ?? 'unknown_field';
        const schemaHash = this._sha256OfJson(field);
        const modelsArr = ingestionInfo.models ?? (ingestionInfo.models = []);
        let modelEntry = modelsArr.find(m => m.modelName === modelName);
        if (!modelEntry) {
            modelEntry = { modelName, fields: [] };
            modelsArr.push(modelEntry);
        }
        const fieldsArr = modelEntry.fields ?? (modelEntry.fields = []);
        let fieldEntry = fieldsArr.find(f => f.fieldName === fieldName);
        if (!fieldEntry) {
            fieldEntry = { fieldName };
            fieldsArr.push(fieldEntry);
        }
        if (fieldEntry.fieldHash === schemaHash && fieldEntry.fieldChunkId) {
            this.logger.log(`[Skip] Field unchanged: ${moduleName}.${modelName}.${fieldName}`);
            return fieldEntry.fieldChunkId;
        }
        const { text, metadata } = this._buildFieldTextAndMetadata(moduleName, modelName, field);
        metadata.schemaHash = schemaHash;
        if (fieldEntry.fieldChunkId) {
            try {
                await this.ragClient.documents.delete({ id: fieldEntry.fieldChunkId });
            }
            catch (e) {
                this.logger.warn(`[Warn] Failed deleting old field chunk (${fieldEntry.fieldChunkId}): ${String(e)}`);
            }
        }
        const r = await this.ragClient.documents.create({
            raw_text: text,
            metadata,
            collectionIds: [ingestionInfo.collectionId],
        });
        const newId = r?.results?.documentId;
        if (!newId) {
            throw new Error(`R2R did not return a documentId while creating field chunk for ${moduleName}.${modelName}.${fieldName}`);
        }
        fieldEntry.fieldChunkId = newId;
        fieldEntry.fieldHash = schemaHash;
        this.logger.log(`[OK] Ingested field ${moduleName}.${modelName}.${fieldName} → id=${newId}, hash=${schemaHash.slice(0, 8)}…`);
        return newId;
    }
};
exports.IngestMetadataService = IngestMetadataService;
exports.IngestMetadataService = IngestMetadataService = IngestMetadataService_1 = __decorate([
    (0, common_1.Injectable)(),
    __metadata("design:paramtypes", [r2r_helper_service_1.R2RHelperService])
], IngestMetadataService);
//# sourceMappingURL=ingest-metadata.service.js.map