Source

services/investigation.service.js

import { AIProcessor } from "@helpers/ai_processor";
import { generateInvestigation as generateInvestigationImpl, generateInvestigationObjects as generateInvestigationObjectsImpl, generateInvestigationSteps as generateInvestigationStepsImpl, generateInvestigationWithoutStepsAndObjects as generateInvestigationWithoutStepsAndObjectsImpl, } from "./investigation/ai-generate";
import { regenerateOtherFields as regenerateOtherFieldsImpl, regenerateOtherFieldsObjects as regenerateOtherFieldsObjectsImpl, regenerateOtherFieldsSteps as regenerateOtherFieldsStepsImpl, regenerateOtherFieldsWithoutStepsAndObjects as regenerateOtherFieldsWithoutStepsAndObjectsImpl, } from "./investigation/ai-regenerate";
import { ensureInvestigationAuthor as ensureInvestigationAuthorImpl } from "./investigation/author";
import { cloneInvestigation as cloneInvestigationImpl } from "./investigation/clone";
import { addContradictionInPlace as addContradictionInPlaceImpl } from "./investigation/contradictions";
import { createInvestigation as createInvestigationImpl, getInvestigationById as getInvestigationByIdImpl, updateInvestigation as updateInvestigationImpl, } from "./investigation/crud";
import { deleteInvestigationById as deleteInvestigationByIdImpl, deleteInvestigationData as deleteInvestigationDataImpl, } from "./investigation/delete";
import { getFilterMetadata as getFilterMetadataImpl, getInvestigations as getInvestigationsImpl, } from "./investigation/listing";
import { checkAndUnlockStaleLocks as checkAndUnlockStaleLocksImpl, lockInvestigation as lockInvestigationImpl, toggleInvestigationDevelopment as toggleInvestigationDevelopmentImpl, unlockInvestigation as unlockInvestigationImpl, } from "./investigation/locks";
import { getFieldHistory as getFieldHistoryImpl, getInvestigationVersions as getInvestigationVersionsImpl, redoInvestigation as redoInvestigationImpl, undoInvestigation as undoInvestigationImpl, updateInvestigationByVersion as updateInvestigationByVersionImpl, } from "./investigation/versions";
/**
 * Investigation Service (facade)
 * @category Services
 */
export class InvestigationService {
    aiProcessor;
    /**
     * Constructor
     * @category Services
     */
    constructor() {
        this.aiProcessor = new AIProcessor();
    }
    /**
     * Creates a new investigation.
     * @param {ICreateInvestigationDto} createDto - DTO used to create the investigation.
     * @returns {Promise<IInvestigation>} The created investigation.
     */
    async createInvestigation(createDto) {
        return await createInvestigationImpl(createDto);
    }
    /**
     * Retrieves a single investigation by id (version-aware).
     * @param {string} investigationId - Investigation id.
     * @returns {Promise<IInvestigation>} The investigation document.
     */
    async getInvestigationById(investigationId) {
        return await getInvestigationByIdImpl(investigationId);
    }
    /**
     * Updates an investigation with the new API format.
     * @param {string} investigationId - Investigation id.
     * @param {IUpdateInvestigationDto} updateData - Update payload.
     * @param {object} [obj] - Optional update options.
     * @param {boolean} [obj.needsBuildUpdate] - Whether to rebuild update fields with the builder.
     * @param {(Types.ObjectId | null)} [obj.updatedBy] - User id performing the update.
     * @param {boolean} [obj.isAiGenerated] - Whether the update is AI-generated.
     * @param {boolean} [obj.skipVersioning] - Whether to skip versioning for this update.
     * @returns {Promise<IUpdateInvestigationResponse>} Update result.
     */
    async updateInvestigation(investigationId, updateData, obj) {
        return await updateInvestigationImpl(investigationId, updateData, obj);
    }
    /**
     * Gets history entries for a specific field.
     * @param {string} investigationId - Investigation id.
     * @param {string} fieldPath - Field path (e.g. "title", "steps.0.title").
     * @param {number} [limit] - Max number of entries (defaults to 50).
     * @returns {Promise<{ fieldPath: string, history: Array<*>, totalCount: number }>} Field history data.
     */
    async getFieldHistory(investigationId, fieldPath, limit = 50) {
        return await getFieldHistoryImpl(investigationId, fieldPath, limit);
    }
    /**
     * Lists versions for an investigation.
     * @param {string} investigationId - Investigation id.
     * @param {number} [limit] - Max number of versions (defaults to 20).
     * @returns {Promise<IInvestigationVersionListResponse>} Versions list response.
     */
    async getInvestigationVersions(investigationId, limit = 20) {
        return await getInvestigationVersionsImpl(investigationId, limit);
    }
    /**
     * Moves currentVersionIndex back (undo).
     * @param {string} investigationId - Investigation id.
     * @returns {Promise<IInvestigation>} Investigation restored to the previous version.
     */
    async undoInvestigation(investigationId) {
        return await undoInvestigationImpl(investigationId);
    }
    /**
     * Moves currentVersionIndex forward (redo).
     * @param {string} investigationId - Investigation id.
     * @returns {Promise<IInvestigation>} Investigation restored to the next version.
     */
    async redoInvestigation(investigationId) {
        return await redoInvestigationImpl(investigationId);
    }
    /**
     * Ensures author is set for an investigation.
     * @param {string} investigationId - Investigation id.
     * @param {Types.ObjectId} authorId - Author id to set when missing.
     * @returns {Promise<IInvestigation | null>} Updated investigation or null if no change.
     */
    async ensureInvestigationAuthor(investigationId, authorId) {
        return await ensureInvestigationAuthorImpl(investigationId, authorId);
    }
    /**
     * Adds contradiction metadata to a field on the investigation object (mutates in place).
     * @param {Partial<IInvestigation>} investigation - Investigation (partial) to mutate.
     * @param {IContradictionInput} contradiction - Contradiction descriptor.
     * @returns {Partial<IInvestigation>} The same investigation object, mutated in place.
     */
    addContradictionInPlace(investigation, contradiction) {
        return addContradictionInPlaceImpl(investigation, contradiction);
    }
    /**
     * Retrieves investigations with filtering/sorting/sectioning.
     * @param {IInvestigationSearchDto} searchDto - Search and pagination parameters.
     * @param {AuthGrpcService} [grpcClient] - Optional gRPC client for profile enrichment.
     * @returns {Promise<IInvestigationListResponseDto>} Investigation list response.
     */
    async getInvestigations(searchDto, grpcClient) {
        return await getInvestigationsImpl(searchDto, grpcClient);
    }
    /**
     * Retrieves available filter metadata for investigations list.
     * @returns {Promise<IInvestigationFilterMetadataDto>} Filter metadata response.
     */
    async getFilterMetadata() {
        return await getFilterMetadataImpl();
    }
    /**
     * AI: Generates investigation text fields without steps/objects.
     * @param {string} message - User message.
     * @param {string} history - Chat history.
     * @param {string} investigationMetadata - Guide/metadata YAML.
     * @returns {Promise<IAssistantInvestigationFormat | null>} AI response.
     */
    async generateInvestigationWithoutStepsAndObjects(message, history, investigationMetadata) {
        return await generateInvestigationWithoutStepsAndObjectsImpl(this.aiProcessor, message, history, investigationMetadata);
    }
    /**
     * AI: Generates objects for an investigation.
     * @param {string} message - User message.
     * @param {string} history - Chat history.
     * @param {string} investigationMetadata - Guide/metadata YAML.
     * @param {string} investigation - Investigation YAML.
     * @returns {Promise<IAssistantInvestigationFormat | null>} AI response.
     */
    async generateInvestigationObjects(message, history, investigationMetadata, investigation) {
        return await generateInvestigationObjectsImpl(this.aiProcessor, message, history, investigationMetadata, investigation);
    }
    /**
     * AI: Generates steps for an investigation.
     * @param {string} message - User message.
     * @param {string} history - Chat history.
     * @param {string} investigationMetadata - Guide/metadata YAML.
     * @param {string} investigation - Investigation YAML.
     * @returns {Promise<IAssistantInvestigationFormat | null>} AI response.
     */
    async generateInvestigationSteps(message, history, investigationMetadata, investigation) {
        return await generateInvestigationStepsImpl(this.aiProcessor, message, history, investigationMetadata, investigation);
    }
    /**
     * AI: Generates a full investigation (text + objects + steps).
     * @param {string} message - User message.
     * @param {FormattedHistory} history - Structured chat history.
     * @param {IMetadataDocument} investigationMetadata - Metadata document used as guide.
     * @returns {Promise<IAssistantInvestigationFormat | null>} AI response.
     */
    async generateInvestigation(message, history, investigationMetadata) {
        return await generateInvestigationImpl(this.aiProcessor, message, history, investigationMetadata);
    }
    /**
     * Creates a clone of an investigation.
     * @param {string} investigationId - Investigation id.
     * @param {string} [userId] - The ID of the user cloning the investigation (will be set as the new author).
     * @returns {Promise<IInvestigation>} The cloned investigation.
     */
    async cloneInvestigation(investigationId, userId) {
        return await cloneInvestigationImpl(investigationId, userId);
    }
    /**
     * Clears investigation content and marks it as empty (mutates in place).
     * @param {IInvestigation} investigation - Investigation to clear.
     * @returns {IInvestigation} The same investigation instance after mutation.
     */
    deleteInvestigationData(investigation) {
        return deleteInvestigationDataImpl(investigation);
    }
    /**
     * Permanently deletes an investigation and related chat history.
     * @param {string} investigationId - Investigation id.
     * @returns {Promise<IDeleteInvestigationByIdResponse>} Delete result.
     */
    async deleteInvestigationById(investigationId) {
        return await deleteInvestigationByIdImpl(investigationId);
    }
    /**
     * Adds a new version entry and updates currentVersionIndex (legacy flow).
     * @param {IInvestigation} oldInvestigation - Previous investigation state.
     * @param {IInvestigation} investigation - Current investigation document to update.
     * @param {(ObjectId | null)} [userId] - User id who made the change.
     * @param {boolean} [isUserChangeExist] - Whether the change was made by user (defaults to false).
     * @returns {Promise<IInvestigation>} Updated investigation document.
     */
    async updateInvestigationByVersion(oldInvestigation, investigation, userId, isUserChangeExist = false) {
        return await updateInvestigationByVersionImpl(oldInvestigation, investigation, userId, isUserChangeExist);
    }
    /**
     * AI regeneration step 1: text fields (no steps/objects).
     * @param {string} fieldName - Field name used as regeneration source.
     * @param {string} fieldValue - Source field value.
     * @param {string} investigation - Current investigation YAML.
     * @param {string} investigationMetadata - Guide/metadata YAML.
     * @param {string} [history] - Chat history YAML (defaults to "-").
     * @param {object} [contradictionInfo] - Optional contradiction info.
     * @param {(string | null)} [contradictionInfo.contradictionReason] - Contradiction reason (if any).
     * @param {(string | null)} [contradictionInfo.targetFieldName] - Target field name related to contradiction.
     * @param {boolean} [contradictionInfo.isContradicting] - Whether the source field is contradicting.
     * @returns {Promise<IAssistantInvestigationFormat | null>} AI response.
     */
    async regenerateOtherFieldsWithoutStepsAndObjects(fieldName, fieldValue, investigation, investigationMetadata, history = "-", contradictionInfo) {
        return await regenerateOtherFieldsWithoutStepsAndObjectsImpl(this.aiProcessor, fieldName, fieldValue, investigation, investigationMetadata, history, contradictionInfo);
    }
    /**
     * AI regeneration step 2: objects.
     * @param {string} fieldName - Field name used as regeneration source.
     * @param {string} fieldValue - Source field value.
     * @param {string} investigation - Current investigation YAML.
     * @param {string} investigationMetadata - Guide/metadata YAML.
     * @param {string} [history] - Chat history YAML (defaults to "-").
     * @param {object} [contradictionInfo] - Optional contradiction info.
     * @param {(string | null)} [contradictionInfo.contradictionReason] - Contradiction reason (if any).
     * @param {(string | null)} [contradictionInfo.targetFieldName] - Target field name related to contradiction.
     * @param {boolean} [contradictionInfo.isContradicting] - Whether the source field is contradicting.
     * @returns {Promise<IAssistantInvestigationFormat | null>} AI response.
     */
    async regenerateOtherFieldsObjects(fieldName, fieldValue, investigation, investigationMetadata, history = "-", contradictionInfo) {
        return await regenerateOtherFieldsObjectsImpl(this.aiProcessor, fieldName, fieldValue, investigation, investigationMetadata, history, contradictionInfo);
    }
    /**
     * AI regeneration step 3: steps.
     * @param {string} fieldName - Field name used as regeneration source.
     * @param {string} fieldValue - Source field value.
     * @param {string} investigation - Current investigation YAML.
     * @param {string} investigationMetadata - Guide/metadata YAML.
     * @param {string} [history] - Chat history YAML (defaults to "-").
     * @param {object} [contradictionInfo] - Optional contradiction info.
     * @param {(string | null)} [contradictionInfo.contradictionReason] - Contradiction reason (if any).
     * @param {(string | null)} [contradictionInfo.targetFieldName] - Target field name related to contradiction.
     * @param {boolean} [contradictionInfo.isContradicting] - Whether the source field is contradicting.
     * @returns {Promise<IAssistantInvestigationFormat | null>} AI response.
     */
    async regenerateOtherFieldsSteps(fieldName, fieldValue, investigation, investigationMetadata, history = "-", contradictionInfo) {
        return await regenerateOtherFieldsStepsImpl(this.aiProcessor, fieldName, fieldValue, investigation, investigationMetadata, history, contradictionInfo);
    }
    /**
     * Orchestrates 3-step regeneration and writes results back to DB.
     * @param {string} investigationId - Investigation id.
     * @param {string} fieldName - Field name used as regeneration source.
     * @param {(Types.ObjectId | null)} [updatedBy] - User id who triggered regeneration.
     * @param {string} [history] - Chat history YAML (defaults to "-").
     * @returns {Promise<IInvestigation>} Updated investigation.
     */
    async regenerateOtherFields(investigationId, fieldName, updatedBy = null, history = "-") {
        return await regenerateOtherFieldsImpl(this.aiProcessor, investigationId, fieldName, updatedBy, history);
    }
    /**
     * Toggles investigation status to/from IN_DEVELOPMENT.
     * @param {string} investigationId - Investigation id.
     * @returns {Promise<IInvestigation>} Updated investigation.
     */
    async toggleInvestigationDevelopment(investigationId) {
        return await toggleInvestigationDevelopmentImpl(investigationId);
    }
    /**
     * Locks investigation for editing.
     * @param {string} investigationId - Investigation id.
     * @param {string} userId - User id acquiring the lock.
     * @returns {Promise<IInvestigation>} Updated investigation.
     */
    async lockInvestigation(investigationId, userId) {
        return await lockInvestigationImpl(investigationId, userId);
    }
    /**
     * Unlocks investigation.
     * @param {string} investigationId - Investigation id.
     * @param {string} userId - User id releasing the lock.
     * @returns {Promise<IInvestigation>} Updated investigation.
     */
    async unlockInvestigation(investigationId, userId) {
        return await unlockInvestigationImpl(investigationId, userId);
    }
    /**
     * Unlocks stale locks (older than configured threshold).
     * @returns {Promise<number>} Count of unlocked investigations.
     */
    async checkAndUnlockStaleLocks() {
        return await checkAndUnlockStaleLocksImpl();
    }
}