import { test, describe, beforeEach, afterEach } from 'node:test';
import { strict as assert } from 'node:assert';
import { createMockDOM, cleanupMockDOM, createMockFetch, createLogCapture } from '../utils/test-helpers.js';
import { sampleJSONContent, moduleNameMappingTests, networkTestResponses } from '../fixtures/content-samples.js';
import { readFileSync } from 'fs';
import path from 'path';

describe('ContentScanner - Tests Unitaires', () => {
    let ContentScanner;
    let logCapture;

    beforeEach(() => {
        createMockDOM();
        logCapture = createLogCapture();

        // Mock EnvConfig
        global.envConfig = {
            isRemoteContentEnabled: () => true,
            get: (key) => {
                const defaults = {
                    'REMOTE_TIMEOUT': 3000,
                    'TRY_REMOTE_FIRST': true,
                    'FALLBACK_TO_LOCAL': true
                };
                return defaults[key];
            }
        };

        // Charger ContentScanner
        const scannerPath = path.resolve(process.cwd(), 'js/core/content-scanner.js');
        const code = readFileSync(scannerPath, 'utf8');

        const testCode = code
            .replace(/window\./g, 'global.')
            .replace(/typeof window !== 'undefined'/g, 'true');

        eval(testCode);
        ContentScanner = global.ContentScanner;
    });

    afterEach(() => {
        logCapture.restore();
        cleanupMockDOM();
    });

    describe('Construction et initialisation', () => {
        test('devrait créer une instance ContentScanner', () => {
            const scanner = new ContentScanner();

            assert.ok(scanner instanceof ContentScanner);
            assert.ok(scanner.discoveredContent instanceof Map);
            assert.equal(scanner.contentDirectory, 'js/content/');
        });

        test('devrait initialiser avec envConfig', () => {
            const scanner = new ContentScanner();

            assert.ok(scanner.envConfig);
            assert.equal(scanner.envConfig.isRemoteContentEnabled(), true);
        });
    });

    describe('Conversion de noms de modules', () => {
        test('jsonFilenameToModuleName devrait convertir correctement', () => {
            const scanner = new ContentScanner();

            moduleNameMappingTests.forEach(({ filename, expected }) => {
                const result = scanner.jsonFilenameToModuleName(`http://example.com/${filename}`);
                assert.equal(result, expected, `Failed for ${filename} -> ${expected}`);
            });
        });

        test('toPascalCase devrait convertir les chaînes avec tirets', () => {
            const scanner = new ContentScanner();

            assert.equal(scanner.toPascalCase('hello-world'), 'HelloWorld');
            assert.equal(scanner.toPascalCase('test-file-name'), 'TestFileName');
            assert.equal(scanner.toPascalCase('simple'), 'Simple');
        });

        test('extractContentId devrait extraire l\'ID du fichier', () => {
            const scanner = new ContentScanner();

            assert.equal(scanner.extractContentId('test-content.js'), 'test-content');
            assert.equal(scanner.extractContentId('test-content.json'), 'test-content');
            assert.equal(scanner.extractContentId('simple.js'), 'simple');
        });
    });

    describe('Chargement de contenu JSON', () => {
        test('loadJsonContent devrait charger depuis le distant en priorité', async () => {
            const scanner = new ContentScanner();

            global.fetch = createMockFetch(networkTestResponses);

            await scanner.loadJsonContent('sbs-level-7-8-new.json');

            // Vérifier que le module est chargé
            assert.ok(global.ContentModules);
            assert.ok(global.ContentModules.SBSLevel78New);
            assert.equal(global.ContentModules.SBSLevel78New.name, sampleJSONContent.name);

            // Vérifier les logs
            const logs = logCapture.getLogs('INFO');
            assert.ok(logs.some(log => log.message.includes('Chargement JSON: sbs-level-7-8-new.json')));
        });

        test('loadJsonContent devrait fallback vers local si distant échoue', async () => {
            const scanner = new ContentScanner();

            // Mock fetch qui échoue pour le distant mais réussit pour le local
            global.fetch = async (url) => {
                if (url.includes('localhost:8083')) {
                    throw new Error('Network error');
                }
                if (url.includes('js/content/')) {
                    return {
                        ok: true,
                        json: async () => sampleJSONContent
                    };
                }
                throw new Error('Unknown URL');
            };

            await scanner.loadJsonContent('test-content.json');

            // Vérifier que le module est chargé depuis local
            assert.ok(global.ContentModules.TestContent);

            const logs = logCapture.getLogs('WARN');
            assert.ok(logs.some(log => log.message.includes('Distant échoué')));
        });

        test('loadJsonContent devrait échouer si toutes les méthodes échouent', async () => {
            const scanner = new ContentScanner();

            global.fetch = async () => {
                throw new Error('All methods failed');
            };

            try {
                await scanner.loadJsonContent('nonexistent.json');
                assert.fail('Should have thrown an error');
            } catch (error) {
                assert.ok(error.message.includes('Impossible de charger JSON'));
            }
        });
    });

    describe('Test de connectivité distante', () => {
        test('shouldTryRemote devrait retourner true si configuré', () => {
            const scanner = new ContentScanner();

            // Mock window.location pour protocol http
            global.window.location.protocol = 'http:';

            assert.equal(scanner.shouldTryRemote(), true);
        });

        test('shouldTryRemote devrait retourner false si pas configuré', () => {
            const scanner = new ContentScanner();

            global.envConfig.isRemoteContentEnabled = () => false;

            assert.equal(scanner.shouldTryRemote(), false);
        });

        test('tryRemoteLoad devrait utiliser le proxy local', async () => {
            const scanner = new ContentScanner();

            global.fetch = createMockFetch(networkTestResponses);

            const result = await scanner.tryRemoteLoad('sbs-level-7-8-new.json');

            assert.equal(result.success, true);
            assert.equal(result.source, 'remote');
        });
    });

    describe('Scan de fichiers de contenu', () => {
        test('scanContentFile devrait traiter un fichier JSON', async () => {
            const scanner = new ContentScanner();

            global.fetch = createMockFetch(networkTestResponses);

            const result = await scanner.scanContentFile('sbs-level-7-8-new.json');

            assert.ok(result);
            assert.equal(result.id, 'sbs-level-7-8-new');
            assert.equal(result.filename, 'sbs-level-7-8-new.json');
            assert.ok(result.name);
        });

        test('scanContentFile devrait traiter un fichier JS', async () => {
            const scanner = new ContentScanner();

            // Mock pour loadScript
            scanner.loadScript = async () => {
                global.ContentModules = global.ContentModules || {};
                global.ContentModules.TestContent = {
                    name: 'Test Content',
                    vocabulary: { 'test': 'test' }
                };
            };

            const result = await scanner.scanContentFile('test-content.js');

            assert.ok(result);
            assert.equal(result.id, 'test-content');
            assert.equal(result.filename, 'test-content.js');
        });

        test('scanContentFile devrait échouer si module non trouvé', async () => {
            const scanner = new ContentScanner();

            global.fetch = async () => ({ ok: true, json: async () => ({}) });

            try {
                await scanner.scanContentFile('nonexistent.json');
                assert.fail('Should have thrown an error');
            } catch (error) {
                assert.ok(error.message.includes('Impossible de charger'));
            }
        });
    });

    describe('Extraction d\'informations de contenu', () => {
        test('extractContentInfo devrait extraire les métadonnées', () => {
            const scanner = new ContentScanner();

            const module = {
                name: 'Test Module',
                description: 'Test description',
                difficulty: 'easy',
                version: '2.0'
            };

            const info = scanner.extractContentInfo(module, 'test-id', 'test.js');

            assert.equal(info.id, 'test-id');
            assert.equal(info.filename, 'test.js');
            assert.equal(info.name, 'Test Module');
            assert.equal(info.description, 'Test description');
            assert.equal(info.difficulty, 'easy');
            assert.equal(info.enabled, true);
            assert.equal(info.metadata.version, '2.0');
        });

        test('extractContentInfo devrait utiliser des valeurs par défaut', () => {
            const scanner = new ContentScanner();

            const module = {};
            const info = scanner.extractContentInfo(module, 'test-id', 'test.js');

            assert.equal(info.difficulty, 'medium');
            assert.equal(info.description, 'Contenu automatiquement détecté');
            assert.equal(info.metadata.version, '1.0');
            assert.equal(info.metadata.format, 'legacy');
        });
    });

    describe('Gestion des erreurs et logs', () => {
        test('updateConnectionStatus devrait émettre un événement', () => {
            const scanner = new ContentScanner();

            let eventReceived = false;
            global.window.dispatchEvent = (event) => {
                if (event.type === 'contentConnectionStatus') {
                    eventReceived = true;
                }
            };

            scanner.updateConnectionStatus('online', 'Test connection');

            assert.equal(eventReceived, true);
        });

        test('devrait logger les erreurs appropriées', async () => {
            const scanner = new ContentScanner();

            global.fetch = async () => {
                throw new Error('Test error');
            };

            try {
                await scanner.loadJsonContent('test.json');
            } catch (error) {
                // Expected
            }

            const errorLogs = logCapture.getLogs('WARN');
            assert.ok(errorLogs.length > 0);
            assert.ok(errorLogs.some(log => log.message.includes('Test error')));
        });
    });

    describe('Discovery de fichiers', () => {
        test('tryCommonFiles devrait essayer les fichiers connus', async () => {
            const scanner = new ContentScanner();

            global.fetch = createMockFetch({
                'http://localhost:8083/do-proxy/sbs-level-7-8-new.json': { ok: true },
                'http://localhost:8083/do-proxy/english-class-demo.json': { ok: true }
            });

            const files = await scanner.tryCommonFiles();

            assert.ok(Array.isArray(files));
            assert.ok(files.includes('sbs-level-7-8-new.json'));
            assert.ok(files.includes('english-class-demo.json'));
        });
    });
});