"use strict";
/**
 * Enhanced Dependency Injection System for CLI Analyzers
 * Manages complex dependencies between analysis sections with caching and validation
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.DependencyChainValidator = exports.AnalyzerDependencyResolver = void 0;
exports.createDependencyResolver = createDependencyResolver;
const logger_1 = require("../utils/logger");
const types_1 = require("../core/types");
/**
 * Concrete implementation of dependency resolver with caching and validation
 */
class AnalyzerDependencyResolver {
    dependencies = new Map();
    resolutionOrder = [];
    resolverFunctions = new Map();
    context;
    constructor(context = {}) {
        this.context = context;
        this.setupResolutionOrder();
    }
    /**
     * Define the canonical dependency resolution order
     */
    setupResolutionOrder() {
        this.resolutionOrder = [
            'section1', // Overview - no dependencies
            'section2', // Quality - depends on section1 (optional)
            'section3', // EDA - depends on section1 (optional)
            'section4', // Visualization - depends on section1, section3
            'section5', // Engineering - depends on section1, section2, section3
            'section6', // Modeling - depends on section1, section2, section3, section5
        ];
    }
    /**
     * Register resolver function for a section
     */
    registerResolver(sectionName, resolver) {
        this.resolverFunctions.set(sectionName, resolver);
        logger_1.logger.debug(`Registered resolver for ${sectionName}`, this.context);
    }
    /**
     * Resolve a specific section dependency with error handling and retries
     */
    async resolve(sectionName) {
        const startTime = Date.now();
        const sectionContext = {
            ...this.context,
            section: sectionName,
            operation: 'dependency_resolution',
        };
        try {
            // Check if already cached
            if (this.dependencies.has(sectionName)) {
                const cached = this.dependencies.get(sectionName);
                logger_1.logger.debug(`Using cached result for ${sectionName} (cached at ${cached.timestamp.toISOString()})`, sectionContext);
                return cached.result;
            }
            // Validate resolver exists
            const resolver = this.resolverFunctions.get(sectionName);
            if (!resolver) {
                throw types_1.DataPilotError.analysis(`No resolver registered for section: ${sectionName}`, 'MISSING_RESOLVER', sectionContext, [
                    {
                        action: 'Register resolver',
                        description: `Call registerResolver('${sectionName}', resolver) before resolving`,
                        severity: types_1.ErrorSeverity.HIGH,
                    },
                ]);
            }
            logger_1.logger.info(`Resolving dependency: ${sectionName}`, sectionContext);
            // Execute resolver with timeout
            const result = await this.executeWithTimeout(resolver, 300000); // 5 minute timeout
            // Validate result
            this.validateSectionResult(sectionName, result);
            // Cache the result
            this.cache(sectionName, result);
            const duration = Date.now() - startTime;
            logger_1.logger.info(`Successfully resolved ${sectionName} in ${duration}ms`, sectionContext);
            return result;
        }
        catch (error) {
            const duration = Date.now() - startTime;
            logger_1.logger.error(`Failed to resolve ${sectionName} after ${duration}ms`, sectionContext, error);
            if (error instanceof types_1.DataPilotError) {
                throw error;
            }
            throw types_1.DataPilotError.analysis(`Failed to resolve dependency ${sectionName}: ${error instanceof Error ? error.message : 'Unknown error'}`, 'DEPENDENCY_RESOLUTION_FAILED', sectionContext, [
                {
                    action: 'Check analyzer implementation',
                    description: 'Verify the analyzer for this section is working correctly',
                    severity: types_1.ErrorSeverity.HIGH,
                },
                {
                    action: 'Check input data',
                    description: 'Ensure the input data is valid and accessible',
                    severity: types_1.ErrorSeverity.MEDIUM,
                },
            ]);
        }
    }
    /**
     * Execute resolver with timeout protection
     */
    async executeWithTimeout(resolver, timeoutMs) {
        return Promise.race([
            resolver(),
            new Promise((_, reject) => setTimeout(() => reject(new Error('Resolver timeout')), timeoutMs)),
        ]);
    }
    /**
     * Validate that a section result has the expected structure
     */
    validateSectionResult(sectionName, result) {
        if (!result) {
            throw types_1.DataPilotError.validation(`${sectionName} resolver returned null or undefined`, 'NULL_RESULT', { section: sectionName });
        }
        // Check for required properties based on section type
        const validationRules = this.getSectionValidationRules(sectionName);
        for (const rule of validationRules) {
            if (!this.hasProperty(result, rule.property)) {
                throw types_1.DataPilotError.validation(`${sectionName} result missing required property: ${rule.property}`, 'MISSING_PROPERTY', { section: sectionName }, [
                    {
                        action: 'Check analyzer implementation',
                        description: `Ensure ${sectionName} analyzer returns an object with ${rule.property}`,
                        severity: types_1.ErrorSeverity.HIGH,
                    },
                ]);
            }
        }
        // Check for warnings array
        if (result.warnings && !Array.isArray(result.warnings)) {
            throw types_1.DataPilotError.validation(`${sectionName} warnings must be an array`, 'INVALID_WARNINGS', { section: sectionName });
        }
    }
    /**
     * Get validation rules for a specific section
     */
    getSectionValidationRules(sectionName) {
        const rules = {
            section1: [
                { property: 'overview', required: true },
                { property: 'warnings', required: true },
                { property: 'performanceMetrics', required: false },
            ],
            section2: [
                { property: 'qualityAudit', required: true },
                { property: 'warnings', required: true },
                { property: 'performanceMetrics', required: false },
            ],
            section3: [
                { property: 'edaAnalysis', required: true },
                { property: 'warnings', required: true },
                { property: 'performanceMetrics', required: false },
            ],
            section4: [
                { property: 'visualizationAnalysis', required: true },
                { property: 'warnings', required: true },
                { property: 'performanceMetrics', required: false },
            ],
            section5: [
                { property: 'engineeringAnalysis', required: true },
                { property: 'warnings', required: true },
                { property: 'performanceMetrics', required: false },
            ],
            section6: [
                { property: 'modelingAnalysis', required: true },
                { property: 'warnings', required: true },
                { property: 'performanceMetrics', required: false },
            ],
        };
        return rules[sectionName] || [];
    }
    /**
     * Check if object has a property using safe property access
     */
    hasProperty(obj, property) {
        return obj && typeof obj === 'object' && property in obj;
    }
    /**
     * Cache a section result with metadata
     */
    cache(sectionName, result) {
        const dependency = {
            name: sectionName,
            result,
            timestamp: new Date(),
            cached: true,
        };
        this.dependencies.set(sectionName, dependency);
        logger_1.logger.debug(`Cached result for ${sectionName}`, {
            ...this.context,
            section: sectionName,
            cacheSize: this.dependencies.size,
        });
    }
    /**
     * Clear all cached dependencies
     */
    clear() {
        const cachedCount = this.dependencies.size;
        this.dependencies.clear();
        this.resolverFunctions.clear();
        logger_1.logger.debug(`Cleared ${cachedCount} cached dependencies`, this.context);
    }
    /**
     * Check if a section is already cached
     */
    has(sectionName) {
        return this.dependencies.has(sectionName);
    }
    /**
     * Get all cached section names
     */
    getCachedSections() {
        return Array.from(this.dependencies.keys());
    }
    /**
     * Get dependency resolution statistics
     */
    getStats() {
        const dependencies = Array.from(this.dependencies.values());
        const timestamps = dependencies.map((dep) => dep.timestamp);
        return {
            totalCached: this.dependencies.size,
            cachedSections: Array.from(this.dependencies.keys()),
            oldestCache: timestamps.length > 0
                ? new Date(Math.min(...timestamps.map((t) => t.getTime())))
                : undefined,
            newestCache: timestamps.length > 0
                ? new Date(Math.max(...timestamps.map((t) => t.getTime())))
                : undefined,
        };
    }
    /**
     * Resolve multiple dependencies in order
     */
    async resolveMultiple(sectionNames) {
        const results = {};
        // Sort by resolution order to ensure dependencies are resolved first
        const orderedSections = sectionNames.sort((a, b) => {
            const indexA = this.resolutionOrder.indexOf(a);
            const indexB = this.resolutionOrder.indexOf(b);
            return indexA - indexB;
        });
        for (const sectionName of orderedSections) {
            results[sectionName] = await this.resolve(sectionName);
        }
        return results;
    }
    /**
     * Get the recommended resolution order for given sections
     */
    getResolutionOrder(sectionNames) {
        return this.resolutionOrder.filter((section) => sectionNames.includes(section));
    }
    /**
     * Check if all dependencies for a section are available
     */
    canResolve(sectionName) {
        const dependencies = this.getSectionDependencies(sectionName);
        const missingDependencies = dependencies.filter((dep) => !this.has(dep));
        return {
            canResolve: missingDependencies.length === 0,
            missingDependencies,
        };
    }
    /**
     * Get the dependencies for a specific section
     */
    getSectionDependencies(sectionName) {
        const dependencyMap = {
            section1: [],
            section2: [], // Can optionally use section1 but not required
            section3: [], // Can optionally use section1 but not required
            section4: ['section1', 'section3'],
            section5: ['section1', 'section2', 'section3'],
            section6: ['section1', 'section2', 'section3'], // section5 is resolved internally
        };
        return dependencyMap[sectionName] || [];
    }
}
exports.AnalyzerDependencyResolver = AnalyzerDependencyResolver;
/**
 * Factory function to create a configured dependency resolver
 */
function createDependencyResolver(filePath, options, context = {}) {
    const resolver = new AnalyzerDependencyResolver({
        ...context,
        filePath,
        operation: 'dependency_management',
    });
    // The actual resolvers will be registered by the CLI orchestrator
    // based on the sections to be executed
    return resolver;
}
/**
 * Dependency chain validator for complex analysis pipelines
 */
class DependencyChainValidator {
    dependencies = new Map();
    constructor() {
        this.setupDependencyMap();
    }
    setupDependencyMap() {
        this.dependencies.set('section1', []);
        this.dependencies.set('section2', []);
        this.dependencies.set('section3', []);
        this.dependencies.set('section4', ['section1', 'section3']);
        this.dependencies.set('section5', ['section1', 'section2', 'section3']);
        this.dependencies.set('section6', ['section1', 'section2', 'section3']);
    }
    /**
     * Validate that all dependencies can be resolved for the given sections
     */
    validateChain(requestedSections) {
        const errors = [];
        const warnings = [];
        for (const section of requestedSections) {
            const deps = this.dependencies.get(section);
            if (!deps) {
                errors.push(`Unknown section: ${section}`);
                continue;
            }
            for (const dep of deps) {
                if (!requestedSections.includes(dep)) {
                    errors.push(`Section ${section} requires ${dep}, but ${dep} is not in the execution plan`);
                }
            }
        }
        // Check for potential performance issues
        if (requestedSections.includes('section6') && requestedSections.length === 1) {
            warnings.push('Running section6 alone will require executing sections 1, 2, 3, and 5 as dependencies');
        }
        return {
            isValid: errors.length === 0,
            errors,
            warnings,
        };
    }
    /**
     * Get the optimal execution order for the given sections
     */
    getExecutionOrder(requestedSections) {
        const allRequired = new Set();
        // Add all requested sections and their dependencies
        for (const section of requestedSections) {
            allRequired.add(section);
            const deps = this.dependencies.get(section) || [];
            deps.forEach((dep) => allRequired.add(dep));
        }
        // Sort by dependency order
        const ordered = ['section1', 'section2', 'section3', 'section4', 'section5', 'section6'];
        return ordered.filter((section) => allRequired.has(section));
    }
}
exports.DependencyChainValidator = DependencyChainValidator;
//# sourceMappingURL=dependency-resolver.js.map