/**
 * DRSModuleTests - Tests for DRS exercise modules
 * Validates TextModule, AudioModule, ImageModule, GrammarModule
 */

import { TestFramework } from '../TestFramework.js';

// Mock dependencies for DRS modules
const mockOrchestrator = {
    sessionId: 'test-session',
    bookId: 'test-book',
    chapterId: 'test-chapter',
    _eventBus: {
        emit: (event, data, sender) => {
            console.log(`Event emitted: ${event}`, data);
        }
    }
};

const mockLLMValidator = {
    testConnectivity: async () => ({ success: true, provider: 'openai' }),
    iaEngine: {
        validateEducationalContent: async (prompt, options) => {
            // Mock AI response
            return '[answer]yes [explanation]This is a mock validation response for testing purposes.';
        }
    }
};

const mockPrerequisiteEngine = {
    canUnlock: (type, content) => ({ canUnlock: true, reason: 'test' }),
    markWordMastered: (word, metadata) => console.log(`Word mastered: ${word}`, metadata),
    markPhraseMastered: (phrase, metadata) => console.log(`Phrase mastered: ${phrase}`, metadata),
    markGrammarMastered: (rule, metadata) => console.log(`Grammar mastered: ${rule}`, metadata)
};

const mockContextMemory = {
    recordInteraction: (interaction) => console.log('Interaction recorded:', interaction)
};

const testFramework = new TestFramework();

// Test ExerciseModuleInterface
testFramework.describe('ExerciseModuleInterface - Contract Validation', function() {

    let ExerciseModuleInterface;

    this.beforeAll(async () => {
        try {
            ExerciseModuleInterface = (await import('../../DRS/interfaces/ExerciseModuleInterface.js')).default;
        } catch (error) {
            console.warn('Could not import ExerciseModuleInterface, using mock');
            ExerciseModuleInterface = class MockExerciseModuleInterface {
                canRun() { throw new Error('ExerciseModuleInterface.canRun() must be implemented by subclass'); }
                async present() { throw new Error('ExerciseModuleInterface.present() must be implemented by subclass'); }
                async validate() { throw new Error('ExerciseModuleInterface.validate() must be implemented by subclass'); }
                getProgress() { throw new Error('ExerciseModuleInterface.getProgress() must be implemented by subclass'); }
                cleanup() { throw new Error('ExerciseModuleInterface.cleanup() must be implemented by subclass'); }
                getMetadata() { throw new Error('ExerciseModuleInterface.getMetadata() must be implemented by subclass'); }
            };
        }
    });

    this.it('should enforce implementation of all required methods', (assert) => {
        const interface_ = new ExerciseModuleInterface();

        // All methods should throw errors when not implemented
        assert.assertThrows(
            () => interface_.canRun([], {}),
            'ExerciseModuleInterface.canRun() must be implemented by subclass'
        );

        assert.assertThrows(
            () => interface_.present(null, {}),
            'ExerciseModuleInterface.present() must be implemented by subclass'
        );

        assert.assertThrows(
            () => interface_.validate('', {}),
            'ExerciseModuleInterface.validate() must be implemented by subclass'
        );

        assert.assertThrows(
            () => interface_.getProgress(),
            'ExerciseModuleInterface.getProgress() must be implemented by subclass'
        );

        assert.assertThrows(
            () => interface_.cleanup(),
            'ExerciseModuleInterface.cleanup() must be implemented by subclass'
        );

        assert.assertThrows(
            () => interface_.getMetadata(),
            'ExerciseModuleInterface.getMetadata() must be implemented by subclass'
        );
    });
});

// Test TextModule
testFramework.describe('TextModule - Reading Comprehension', function() {

    let TextModule;
    let textModule;

    this.beforeAll(async () => {
        try {
            TextModule = (await import('../../DRS/exercise-modules/TextModule.js')).default;
        } catch (error) {
            console.warn('Could not import TextModule, skipping tests');
            TextModule = null;
        }
    });

    this.beforeEach(() => {
        if (TextModule) {
            textModule = new TextModule(mockOrchestrator, mockLLMValidator, mockPrerequisiteEngine, mockContextMemory);
        }
    });

    this.afterEach(() => {
        if (textModule) {
            try {
                textModule.cleanup();
            } catch (error) {
                // Ignore cleanup errors in tests
            }
        }
    });

    this.it('should initialize with correct dependencies', (assert) => {
        if (!TextModule) {
            console.log('Skipping TextModule test - module not available');
            return;
        }

        assert.assertDefined(textModule.orchestrator, 'Should have orchestrator dependency');
        assert.assertDefined(textModule.llmValidator, 'Should have llmValidator dependency');
        assert.assertDefined(textModule.prerequisiteEngine, 'Should have prerequisiteEngine dependency');
        assert.assertDefined(textModule.contextMemory, 'Should have contextMemory dependency');
        assert.assertFalse(textModule.initialized, 'Should not be initialized initially');
    });

    this.it('should reject initialization without required dependencies', (assert) => {
        if (!TextModule) return;

        assert.assertThrows(
            () => new TextModule(null, mockLLMValidator, mockPrerequisiteEngine, mockContextMemory),
            'TextModule requires all service dependencies'
        );

        assert.assertThrows(
            () => new TextModule(mockOrchestrator, null, mockPrerequisiteEngine, mockContextMemory),
            'TextModule requires all service dependencies'
        );
    });

    this.it('should initialize correctly', async (assert) => {
        if (!TextModule) return;

        await textModule.init();
        assert.assertTrue(textModule.initialized, 'Should be initialized after init()');
    });

    this.it('should implement all required interface methods', (assert) => {
        if (!TextModule) return;

        // Check that methods exist and don't throw "not implemented" errors
        assert.assertDefined(textModule.canRun, 'Should have canRun method');
        assert.assertDefined(textModule.present, 'Should have present method');
        assert.assertDefined(textModule.validate, 'Should have validate method');
        assert.assertDefined(textModule.getProgress, 'Should have getProgress method');
        assert.assertDefined(textModule.cleanup, 'Should have cleanup method');
        assert.assertDefined(textModule.getMetadata, 'Should have getMetadata method');
    });

    this.it('should return correct metadata', (assert) => {
        if (!TextModule) return;

        const metadata = textModule.getMetadata();

        assert.assertEqual(metadata.name, 'TextModule');
        assert.assertEqual(metadata.type, 'text');
        assert.assertDefined(metadata.version);
        assert.assertDefined(metadata.description);
        assert.assertTrue(Array.isArray(metadata.capabilities));
        assert.assertContains(metadata.capabilities, 'text_comprehension');
    });

    this.it('should handle canRun logic correctly', (assert) => {
        if (!TextModule) return;

        // Test with empty content
        const emptyContent = { texts: [] };
        assert.assertFalse(textModule.canRun([], emptyContent), 'Should return false for empty texts');

        // Test with available content
        const validContent = {
            texts: [
                { id: 'text1', title: 'Test Text', content: 'Sample content' }
            ]
        };
        assert.assertTrue(textModule.canRun([], validContent), 'Should return true for available texts');
    });

    this.it('should validate user input correctly', async (assert) => {
        if (!TextModule) return;

        await textModule.init();

        // Set up mock exercise data
        textModule.currentText = { title: 'Test', content: 'Test content' };
        textModule.currentQuestion = { question: 'Test question?', keywords: ['test'] };
        textModule.questionIndex = 0;
        textModule.questions = [textModule.currentQuestion];

        const result = await textModule.validate('This is a test answer', {});

        assert.assertDefined(result.score, 'Should return a score');
        assert.assertDefined(result.correct, 'Should return correct status');
        assert.assertDefined(result.feedback, 'Should return feedback');
        assert.assertTrue(typeof result.score === 'number', 'Score should be a number');
        assert.assertTrue(typeof result.correct === 'boolean', 'Correct should be boolean');
    });

    this.it('should track progress correctly', (assert) => {
        if (!TextModule) return;

        textModule.questions = [
            { question: 'Q1' },
            { question: 'Q2' },
            { question: 'Q3' }
        ];
        textModule.questionResults = [
            { correct: true, score: 90 },
            { correct: false, score: 60 }
        ];

        const progress = textModule.getProgress();

        assert.assertEqual(progress.type, 'text');
        assert.assertEqual(progress.totalQuestions, 3);
        assert.assertEqual(progress.completedQuestions, 2);
        assert.assertEqual(progress.correctAnswers, 1);
        assert.assertEqual(progress.progressPercentage, 67); // 2/3 * 100, rounded
        assert.assertEqual(progress.comprehensionRate, 50); // 1/2 * 100
    });
});

// Test AudioModule
testFramework.describe('AudioModule - Listening Exercises', function() {

    let AudioModule;
    let audioModule;

    this.beforeAll(async () => {
        try {
            AudioModule = (await import('../../DRS/exercise-modules/AudioModule.js')).default;
        } catch (error) {
            console.warn('Could not import AudioModule, skipping tests');
            AudioModule = null;
        }
    });

    this.beforeEach(() => {
        if (AudioModule) {
            audioModule = new AudioModule(mockOrchestrator, mockLLMValidator, mockPrerequisiteEngine, mockContextMemory);
        }
    });

    this.afterEach(() => {
        if (audioModule) {
            try {
                audioModule.cleanup();
            } catch (error) {
                // Ignore cleanup errors
            }
        }
    });

    this.it('should initialize correctly and track playback', async (assert) => {
        if (!AudioModule) return;

        await audioModule.init();

        assert.assertEqual(audioModule.playCount, 0, 'Should start with 0 playbacks');
        assert.assertFalse(audioModule.initialized === false, 'Should be initialized');
    });

    this.it('should handle audio-specific validation with playback penalties', async (assert) => {
        if (!AudioModule) return;

        await audioModule.init();

        // Set up mock data
        audioModule.currentAudio = { title: 'Test Audio', duration: 30 };
        audioModule.currentQuestion = { question: 'What did you hear?', keywords: ['audio'] };
        audioModule.playCount = 6; // Exceeds maxPlaybacks (5)
        audioModule.questionIndex = 0;
        audioModule.questions = [audioModule.currentQuestion];

        const result = await audioModule.validate('I heard test audio', {});

        // Should apply penalty for excessive playbacks
        assert.assertTrue(result.feedback.includes('excessive playbacks') || result.score < 90,
            'Should apply penalty for too many playbacks');
    });

    this.it('should return audio-specific progress data', (assert) => {
        if (!AudioModule) return;

        audioModule.currentAudio = { title: 'Test Audio' };
        audioModule.playCount = 3;
        audioModule.questions = [{ question: 'Q1' }];
        audioModule.questionResults = [{ correct: true, score: 85 }];

        const progress = audioModule.getProgress();

        assert.assertEqual(progress.type, 'audio');
        assert.assertEqual(progress.playbackCount, 3);
        assert.assertDefined(progress.audioTitle);
    });
});

// Test ImageModule
testFramework.describe('ImageModule - Visual Comprehension', function() {

    let ImageModule;
    let imageModule;

    this.beforeAll(async () => {
        try {
            ImageModule = (await import('../../DRS/exercise-modules/ImageModule.js')).default;
        } catch (error) {
            console.warn('Could not import ImageModule, skipping tests');
            ImageModule = null;
        }
    });

    this.beforeEach(() => {
        if (ImageModule) {
            imageModule = new ImageModule(mockOrchestrator, mockLLMValidator, mockPrerequisiteEngine, mockContextMemory);
        }
    });

    this.afterEach(() => {
        if (imageModule) {
            try {
                imageModule.cleanup();
            } catch (error) {
                // Ignore cleanup errors
            }
        }
    });

    this.it('should initialize and track viewing time', async (assert) => {
        if (!ImageModule) return;

        await imageModule.init();

        assert.assertEqual(imageModule.viewingTime, 0, 'Should start with 0 viewing time');
        assert.assertNull(imageModule.startViewTime, 'Should start with null view timer');
    });

    this.it('should handle image-specific metadata', (assert) => {
        if (!ImageModule) return;

        const metadata = imageModule.getMetadata();

        assert.assertEqual(metadata.name, 'ImageModule');
        assert.assertEqual(metadata.type, 'image');
        assert.assertContains(metadata.capabilities, 'image_comprehension');
        assert.assertContains(metadata.capabilities, 'visual_analysis');
        assert.assertTrue(metadata.aiRequired, 'Image module should require AI for vision analysis');
    });

    this.it('should track observation time in progress', (assert) => {
        if (!ImageModule) return;

        imageModule.viewingTime = 15; // 15 seconds
        imageModule.currentImage = { title: 'Test Image' };

        const progress = imageModule.getProgress();

        assert.assertEqual(progress.type, 'image');
        assert.assertEqual(progress.observationTime, 15);
    });
});

// Test GrammarModule
testFramework.describe('GrammarModule - Grammar Exercises', function() {

    let GrammarModule;
    let grammarModule;

    this.beforeAll(async () => {
        try {
            GrammarModule = (await import('../../DRS/exercise-modules/GrammarModule.js')).default;
        } catch (error) {
            console.warn('Could not import GrammarModule, skipping tests');
            GrammarModule = null;
        }
    });

    this.beforeEach(() => {
        if (GrammarModule) {
            grammarModule = new GrammarModule(mockOrchestrator, mockLLMValidator, mockPrerequisiteEngine, mockContextMemory);
        }
    });

    this.afterEach(() => {
        if (grammarModule) {
            try {
                grammarModule.cleanup();
            } catch (error) {
                // Ignore cleanup errors
            }
        }
    });

    this.it('should handle different exercise types', (assert) => {
        if (!GrammarModule) return;

        const exerciseTypes = grammarModule.exerciseTypes;

        assert.assertDefined(exerciseTypes.fill_blank, 'Should support fill in the blank');
        assert.assertDefined(exerciseTypes.correction, 'Should support error correction');
        assert.assertDefined(exerciseTypes.transformation, 'Should support sentence transformation');
        assert.assertDefined(exerciseTypes.multiple_choice, 'Should support multiple choice');
        assert.assertDefined(exerciseTypes.conjugation, 'Should support verb conjugation');
        assert.assertDefined(exerciseTypes.construction, 'Should support sentence construction');
    });

    this.it('should track attempts and hints', (assert) => {
        if (!GrammarModule) return;

        grammarModule.attempts = 2;
        grammarModule.hintUsed = true;

        const progress = grammarModule.getProgress();

        assert.assertEqual(progress.currentAttempts, 2);
        assert.assertTrue(progress.hintUsed);
    });

    this.it('should have strict grammar validation settings', (assert) => {
        if (!GrammarModule) return;

        const config = grammarModule.config;

        assert.assertEqual(config.temperature, 0.1, 'Should use low temperature for grammar accuracy');
        assert.assertTrue(config.maxAttempts > 0, 'Should allow multiple attempts');
        assert.assertTrue(config.showHints, 'Should support hints');
    });
});

export { testFramework as DRSModuleTests };