/**
 * ExerciseFactory - Factory for creating DRS exercise modules
 *
 * Creates exercise modules that implement DRSExerciseInterface
 * Provides unified creation, initialization, and lifecycle management
 */

import DRSExerciseInterface from '../interfaces/DRSExerciseInterface.js';

class ExerciseFactory {
    constructor() {
        // Registry of available exercise modules
        this._moduleRegistry = new Map();

        // Module type to class name mapping
        this._typeMap = {
            'vocabulary-flashcards': 'VocabularyModule',
            'vocabulary-discovery': 'WordDiscoveryModule',
            'text': 'TextModule',
            'text-analysis': 'TextAnalysisModule',
            'reading': 'TextModule',
            'reading-comprehension': 'TextModule',
            'reading-comprehension-AI': 'TextAnalysisModule',
            'grammar': 'GrammarModule',
            'grammar-analysis': 'GrammarAnalysisModule',
            'grammar-practice': 'GrammarModule',
            'grammar-practice-AI': 'GrammarAnalysisModule',
            'translation': 'TranslationModule',
            'open-response': 'OpenResponseModule',
            'phrase': 'TextAnalysisModule', // REPLACED: was PhraseModule, now using TextAnalysisModule
            'phrase-practice': 'TextAnalysisModule', // REPLACED: was PhraseModule, now using TextAnalysisModule
            'audio': 'AudioModule',
            'listening-comprehension': 'AudioModule',
            'listening-comprehension-AI': 'AudioModule',
            'visual-description': 'ImageModule',
            'visual-description-AI': 'ImageModule'
        };

        // Cache for loaded module classes
        this._classCache = new Map();
    }

    /**
     * Create an exercise module by type
     * @param {string} exerciseType - Type of exercise to create
     * @param {Object} dependencies - Dependencies (orchestrator, llmValidator, prerequisiteEngine, contextMemory)
     * @param {Object} config - Exercise configuration
     * @param {Object} content - Exercise content (optional, can be passed later to init)
     * @returns {Promise<DRSExerciseInterface>} - Exercise module instance
     */
    async create(exerciseType, dependencies, config = {}, content = null) {
        console.log(`🏭 ExerciseFactory creating: ${exerciseType}`);

        // Validate exercise type
        if (!this._typeMap[exerciseType]) {
            throw new Error(`Unknown exercise type: ${exerciseType}. Available types: ${Object.keys(this._typeMap).join(', ')}`);
        }

        // Get module class name
        const className = this._typeMap[exerciseType];
        console.log(`📦 Resolved to module class: ${className}`);

        // Load module class (with caching)
        const ModuleClass = await this._loadModuleClass(className);

        // Validate dependencies
        this._validateDependencies(className, dependencies);

        // Create instance
        const instance = new ModuleClass(
            dependencies.orchestrator,
            dependencies.llmValidator,
            dependencies.prerequisiteEngine,
            dependencies.contextMemory
        );

        // Validate interface implementation
        this._validateInterface(instance, className);

        // Initialize the module with config and content
        await instance.init(config, content);

        console.log(`✅ ${className} created and initialized`);
        return instance;
    }

    /**
     * Load module class (with caching)
     * @param {string} className - Name of the module class
     * @returns {Promise<Class>} - Module class
     */
    async _loadModuleClass(className) {
        // Check cache first
        if (this._classCache.has(className)) {
            console.log(`📦 Using cached class: ${className}`);
            return this._classCache.get(className);
        }

        // Determine file path based on class name
        let modulePath;
        if (className === 'WordDiscoveryModule') {
            modulePath = '../exercise-modules/WordDiscoveryModule.js';
        } else if (className.endsWith('AnalysisModule')) {
            modulePath = `../exercise-modules/${className}.js`;
        } else {
            modulePath = `../exercise-modules/${className}.js`;
        }

        console.log(`📂 Loading module from: ${modulePath}`);

        try {
            const module = await import(modulePath);
            const ModuleClass = module.default;

            // Cache the class
            this._classCache.set(className, ModuleClass);

            return ModuleClass;
        } catch (error) {
            throw new Error(`Failed to load module class ${className} from ${modulePath}: ${error.message}`);
        }
    }

    /**
     * Validate dependencies
     * @param {string} className - Module class name
     * @param {Object} dependencies - Dependencies object
     */
    _validateDependencies(className, dependencies) {
        const required = ['orchestrator', 'prerequisiteEngine'];

        // Modules that don't need AI validation
        const noAIModules = ['VocabularyModule', 'WordDiscoveryModule'];

        // Check required dependencies
        for (const dep of required) {
            if (!dependencies[dep]) {
                throw new Error(`${className} requires dependency: ${dep}`);
            }
        }

        // Check optional dependencies (warn if missing, except for no-AI modules)
        if (!dependencies.llmValidator && !noAIModules.includes(className)) {
            console.warn(`⚠️ ${className} missing optional dependency: llmValidator`);
        }

        if (!dependencies.contextMemory) {
            console.warn(`⚠️ ${className} missing optional dependency: contextMemory`);
        }
    }

    /**
     * Validate that instance implements DRSExerciseInterface
     * @param {Object} instance - Module instance
     * @param {string} className - Class name for error messages
     */
    _validateInterface(instance, className) {
        const requiredMethods = [
            'init', 'render', 'destroy', 'validate',
            'getResults', 'handleUserInput', 'markCompleted',
            'getProgress', 'getExerciseType', 'getExerciseConfig'
        ];

        const missing = [];
        for (const method of requiredMethods) {
            if (typeof instance[method] !== 'function') {
                missing.push(method);
            }
        }

        if (missing.length > 0) {
            throw new Error(
                `${className} does not properly implement DRSExerciseInterface.\n` +
                `Missing methods: ${missing.join(', ')}\n` +
                `All DRS modules MUST implement the full interface.`
            );
        }

        console.log(`✅ ${className} implements DRSExerciseInterface correctly`);
    }

    /**
     * Get all supported exercise types
     * @returns {string[]} - Array of exercise type strings
     */
    getSupportedTypes() {
        return Object.keys(this._typeMap);
    }

    /**
     * Check if exercise type is supported
     * @param {string} exerciseType - Type to check
     * @returns {boolean} - True if supported
     */
    isSupported(exerciseType) {
        return this._typeMap.hasOwnProperty(exerciseType);
    }

    /**
     * Get module class name for exercise type
     * @param {string} exerciseType - Exercise type
     * @returns {string} - Class name
     */
    getModuleClassName(exerciseType) {
        return this._typeMap[exerciseType] || null;
    }

    /**
     * Create exercise and render it immediately
     * @param {string} exerciseType - Type of exercise
     * @param {HTMLElement} container - Container element
     * @param {Object} dependencies - Dependencies
     * @param {Object} config - Configuration
     * @param {Object} content - Exercise content
     * @returns {Promise<DRSExerciseInterface>} - Created and rendered module
     */
    async createAndRender(exerciseType, container, dependencies, config, content) {
        console.log(`🎨 Creating and rendering ${exerciseType}...`);

        // Create the module with config and content
        const module = await this.create(exerciseType, dependencies, config, content);

        // Render it (content already passed in init)
        await module.render(container);

        console.log(`✅ ${exerciseType} created and rendered`);
        return module;
    }

    /**
     * Batch create multiple modules (for preloading)
     * @param {Array} exerciseTypes - Array of exercise types to create
     * @param {Object} dependencies - Shared dependencies
     * @returns {Promise<Map>} - Map of type -> instance
     */
    async batchCreate(exerciseTypes, dependencies) {
        console.log(`🏭 Batch creating ${exerciseTypes.length} modules...`);

        const instances = new Map();
        const errors = [];

        for (const type of exerciseTypes) {
            try {
                const instance = await this.create(type, dependencies);
                instances.set(type, instance);
            } catch (error) {
                errors.push({ type, error: error.message });
                console.error(`❌ Failed to create ${type}:`, error);
            }
        }

        if (errors.length > 0) {
            console.warn(`⚠️ ${errors.length} modules failed to create:`, errors);
        }

        console.log(`✅ Batch created ${instances.size}/${exerciseTypes.length} modules`);
        return instances;
    }

    /**
     * Destroy module and clean up
     * @param {DRSExerciseInterface} module - Module to destroy
     */
    async destroy(module) {
        if (!module) return;

        try {
            await module.destroy();
            console.log(`🧹 Module destroyed: ${module.getExerciseType()}`);
        } catch (error) {
            console.error('Failed to destroy module:', error);
        }
    }

    /**
     * Get factory statistics
     * @returns {Object} - Factory stats
     */
    getStats() {
        return {
            supportedTypes: this.getSupportedTypes().length,
            cachedClasses: this._classCache.size,
            typeMapping: Object.keys(this._typeMap).length
        };
    }

    /**
     * Clear class cache (for hot reload during development)
     */
    clearCache() {
        this._classCache.clear();
        console.log('🧹 ExerciseFactory class cache cleared');
    }
}

// Export singleton instance
const exerciseFactory = new ExerciseFactory();
export default exerciseFactory;
