// engine/context-merger.ts — Context ingestion business logic (SPEC-036)
// Pure functions: detectMissingAreas, buildRequestPrompt, mergeContext, detectConflicts.
// ---------------------------------------------------------------------------
// Missing area detection
// ---------------------------------------------------------------------------
/** ArchitecturePattern values that indicate "not yet known". */
const UNKNOWN_ARCH_PATTERNS = new Set(['custom']);
/**
 * Inspect the current knowledge and return a list of areas that need
 * additional documentation from the user.
 */
export function detectMissingAreas(knowledge) {
    const missing = [];
    if (knowledge.language === 'unknown') {
        missing.push('programming language / tech stack');
    }
    // 'custom' is the closest sentinel for "undetected architecture"
    if (UNKNOWN_ARCH_PATTERNS.has(knowledge.architecture.primary)) {
        missing.push('architecture diagram / design decisions');
    }
    if (knowledge.architecture.boundaries.length === 0) {
        missing.push('module boundaries / bounded contexts');
    }
    if (knowledge.apiContracts.length === 0) {
        missing.push('API contracts (REST, GraphQL, etc.)');
    }
    const hasEntities = knowledge.conventions.entities !== undefined || knowledge.conventions.models !== undefined;
    if (!hasEntities) {
        missing.push('data models / entities');
    }
    if (knowledge.externalServices.length === 0) {
        missing.push('third-party integrations / external services');
    }
    if (knowledge.environments.length === 0) {
        missing.push('deployment environments');
    }
    return missing;
}
// ---------------------------------------------------------------------------
// Prompt builder
// ---------------------------------------------------------------------------
function detectAgentStyle(conventions) {
    const keys = Object.keys(conventions);
    if (keys.some((k) => k.includes('claude'))) {
        return 'claude-code';
    }
    if (keys.some((k) => k.includes('cursor'))) {
        return 'cursor';
    }
    if (keys.some((k) => k.includes('windsurf'))) {
        return 'windsurf';
    }
    return 'generic';
}
/**
 * Build an agent-idiomatic prompt that asks the user to share reference docs.
 * The prompt explains what types of files are accepted and what areas need coverage.
 */
export function buildRequestPrompt(missingAreas, knowledge) {
    const style = detectAgentStyle(knowledge.conventions);
    const areasList = missingAreas.map((a) => `  - ${a}`).join('\n');
    const formatNote = 'You can share any format I can read: images (screenshots, wireframes), ' +
        'PDFs, Word/Excel documents, Markdown files, plain text, or URLs.';
    switch (style) {
        case 'claude-code':
            return [
                'I need some reference documentation to understand your project better.',
                '',
                'Please attach or paste any of the following:',
                areasList,
                '',
                formatNote,
                '',
                'Once you share these documents, I will extract the relevant knowledge ' +
                    'and store it so you never need to re-explain it.',
            ].join('\n');
        case 'cursor':
        case 'windsurf':
            return [
                'To work with your project effectively, I need some context documents.',
                '',
                'Please open and attach from your workspace:',
                areasList,
                '',
                formatNote,
                '',
                'After you share them, I will persist the extracted knowledge for future sessions.',
            ].join('\n');
        case 'generic':
        default:
            return [
                'To get started, could you share some documentation about your project?',
                '',
                'Things that would help:',
                areasList,
                '',
                formatNote,
            ].join('\n');
    }
}
// ---------------------------------------------------------------------------
// Conflict detection
// ---------------------------------------------------------------------------
/**
 * Compare auto-detected knowledge with contributed context and return
 * a list of conflicting fields that the agent must resolve.
 */
export function detectConflicts(existing, contributed) {
    const conflicts = [];
    // Detect API contract type conflicts.
    // ProjectKnowledge.apiContracts uses 'openapi'|'graphql'|'trpc'|'grpc'|'rest-informal'.
    // ContributedContext.apiContracts uses 'rest'|'graphql'|'trpc'|'grpc'|'websocket'.
    // We compare as strings to allow cross-union comparison without type errors.
    if (contributed.apiContracts && contributed.apiContracts.length > 0) {
        const existingTypes = existing.apiContracts.map((c) => c.type);
        for (const contrib of contributed.apiContracts) {
            const contribType = contrib.type;
            if (existingTypes.length > 0 && !existingTypes.includes(contribType)) {
                conflicts.push({
                    field: 'apiContracts.type',
                    autoDetectedValue: existingTypes.join(', '),
                    contributedValue: contribType,
                    recommendation: `Auto-detection found ${existingTypes.join(', ')} API contracts but the ` +
                        `contributed context mentions ${contribType}. ` +
                        `Verify which is authoritative and update init_project if needed.`,
                });
                break; // One conflict per field is enough
            }
        }
    }
    // Architecture primary pattern conflict.
    // Skip when architecture is 'custom' (undetected) — no conflict there.
    if (contributed.architectureNotes && !UNKNOWN_ARCH_PATTERNS.has(existing.architecture.primary)) {
        const notes = contributed.architectureNotes.toLowerCase();
        const detected = existing.architecture.primary.toLowerCase();
        // Simple heuristic: if the notes mention a different prominent pattern
        const patterns = ['hexagonal', 'clean', 'layered', 'microservices', 'monolith', 'event-driven'];
        for (const pattern of patterns) {
            if (notes.includes(pattern) && !detected.includes(pattern)) {
                conflicts.push({
                    field: 'architecture.primary',
                    autoDetectedValue: existing.architecture.primary,
                    contributedValue: pattern,
                    recommendation: `Auto-detection classified the architecture as "${existing.architecture.primary}" ` +
                        `but the contributed notes mention "${pattern}". ` +
                        `Review and consider re-running init_project with additional context.`,
                });
                break;
            }
        }
    }
    return conflicts;
}
// ---------------------------------------------------------------------------
// Context merge
// ---------------------------------------------------------------------------
/**
 * Merge contributed context into existing knowledge.
 * Auto-detected values are NEVER overwritten — conflicts are reported instead.
 * Returns the list of fields that were successfully merged and any conflicts found.
 */
export function mergeContext(existing, contributed) {
    const merged = [];
    const conflicts = detectConflicts(existing, contributed);
    // Entities
    if (contributed.entities && contributed.entities.length > 0) {
        merged.push('entities');
    }
    // API contracts: only add types not already detected
    if (contributed.apiContracts && contributed.apiContracts.length > 0) {
        const hasConflict = conflicts.some((c) => c.field === 'apiContracts.type');
        if (!hasConflict) {
            merged.push('apiContracts');
        }
    }
    // Architecture notes: only if no primary pattern already detected
    if (contributed.architectureNotes) {
        const hasConflict = conflicts.some((c) => c.field === 'architecture.primary');
        if (!hasConflict) {
            merged.push('architectureNotes');
        }
    }
    if (contributed.techDecisions && contributed.techDecisions.length > 0) {
        merged.push('techDecisions');
    }
    if (contributed.userFlows && contributed.userFlows.length > 0) {
        merged.push('userFlows');
    }
    if (contributed.nonFunctionalRequirements && contributed.nonFunctionalRequirements.length > 0) {
        merged.push('nonFunctionalRequirements');
    }
    if (contributed.additionalContext) {
        merged.push('additionalContext');
    }
    return { merged, conflicts };
}
//# sourceMappingURL=context-merger.js.map