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

// Test d'intégration pour le flux complet de chargement de contenu
describe('Flux de Chargement de Contenu - Tests d\'Intégration', () => {
    let ContentScanner, GameLoader, EnvConfig;
    let logCapture;

    // Helper pour simuler fetch vers le proxy
    function createProxyMockFetch() {
        return async (url) => {
            if (url.includes('localhost:8083/do-proxy/sbs-level-7-8-new.json')) {
                return {
                    ok: true,
                    status: 200,
                    json: async () => ({
                        name: "SBS Level 7-8 (New)",
                        description: "Test content from remote",
                        difficulty: "intermediate",
                        vocabulary: {
                            "central": "中心的；中央的",
                            "avenue": "大街；林荫道",
                            "refrigerator": "冰箱"
                        }
                    })
                };
            }

            if (url.includes('localhost:8083/do-proxy/english-class-demo.json')) {
                return {
                    ok: true,
                    status: 200,
                    json: async () => ({
                        name: "English Class Demo",
                        description: "Demo content",
                        vocabulary: {
                            "hello": "bonjour",
                            "world": "monde"
                        }
                    })
                };
            }

            // Local fallback
            if (url.includes('js/content/')) {
                return {
                    ok: true,
                    status: 200,
                    json: async () => ({
                        name: "Local Content",
                        vocabulary: { "local": "content" }
                    })
                };
            }

            return {
                ok: false,
                status: 404,
                json: async () => ({ error: 'Not found' })
            };
        };
    }

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

        // Charger tous les modules nécessaires
        const modules = [
            { name: 'EnvConfig', path: 'js/core/env-config.js' },
            { name: 'ContentScanner', path: 'js/core/content-scanner.js' },
            { name: 'GameLoader', path: 'js/core/game-loader.js' }
        ];

        modules.forEach(({ name, path: modulePath }) => {
            const fullPath = path.resolve(process.cwd(), modulePath);
            const code = readFileSync(fullPath, 'utf8');

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

            eval(testCode);
        });

        EnvConfig = global.EnvConfig;
        ContentScanner = global.ContentScanner;
        GameLoader = global.GameLoader;

        // Initialiser envConfig global
        global.envConfig = new EnvConfig();

        // Mock fetch
        global.fetch = createProxyMockFetch();

        // Mock GameModules
        global.GameModules = {
            WhackAMole: class {
                constructor(options) {
                    this.container = options.container;
                    this.content = options.content;
                    this.started = false;
                }
                start() { this.started = true; }
                destroy() { this.destroyed = true; }
            }
        };
    });

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

    describe('Flux complet: Scan → Load → Game', () => {
        test('devrait scanner et charger le contenu distant puis créer un jeu', async () => {
            // Étape 1: Scanner le contenu
            const scanner = new ContentScanner();
            const scanResults = await scanner.scanAllContent();

            assert.ok(scanResults);
            assert.ok(scanResults.found.length > 0, 'Should find some content');

            // Vérifier que les modules sont chargés
            assert.ok(global.ContentModules);
            assert.ok(global.ContentModules.SBSLevel78New || global.ContentModules.EnglishClassDemo,
                'Should load at least one remote module');

            // Étape 2: Charger un jeu avec le contenu trouvé
            const loader = new GameLoader();
            const container = { innerHTML: '' };

            const contentId = scanResults.found[0].id;
            const game = await loader.loadGame('whack-a-mole', contentId, container);

            assert.ok(game);
            assert.ok(game instanceof global.GameModules.WhackAMole);
            assert.ok(game.content);
            assert.ok(game.content.vocabulary);

            // Étape 3: Démarrer le jeu
            game.start();
            assert.equal(game.started, true);

            // Vérifier les logs
            const logs = logCapture.getLogs();
            assert.ok(logs.some(log => log.message.includes('Scan automatique')));
            assert.ok(logs.some(log => log.message.includes('chargé avec succès')));
        });

        test('devrait gérer le fallback local si le distant échoue', async () => {
            // Mock fetch qui échoue pour le distant
            global.fetch = async (url) => {
                if (url.includes('localhost:8083')) {
                    throw new Error('Network error');
                }
                if (url.includes('js/content/')) {
                    return {
                        ok: true,
                        json: async () => ({
                            name: "Local Fallback",
                            vocabulary: { "fallback": "secours" }
                        })
                    };
                }
                throw new Error('Unknown URL');
            };

            const scanner = new ContentScanner();

            // Forcer le chargement d'un fichier JSON
            await scanner.loadJsonContent('test-content.json');

            assert.ok(global.ContentModules.TestContent);
            assert.equal(global.ContentModules.TestContent.name, "Local Fallback");

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

    describe('Configuration et priorités', () => {
        test('devrait respecter la configuration TRY_REMOTE_FIRST', async () => {
            const config = new EnvConfig();
            config.set('TRY_REMOTE_FIRST', true);

            const scanner = new ContentScanner();

            // Compter les appels fetch
            let remoteAttempts = 0;
            let localAttempts = 0;

            global.fetch = async (url) => {
                if (url.includes('localhost:8083')) {
                    remoteAttempts++;
                    return {
                        ok: true,
                        json: async () => ({ name: "Remote", vocabulary: { "remote": "distant" } })
                    };
                }
                if (url.includes('js/content/')) {
                    localAttempts++;
                    return {
                        ok: true,
                        json: async () => ({ name: "Local", vocabulary: { "local": "local" } })
                    };
                }
                throw new Error('Unknown URL');
            };

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

            assert.ok(remoteAttempts > 0, 'Should try remote first');
            // Local ne devrait pas être appelé si remote réussit
            assert.equal(localAttempts, 0, 'Should not fallback to local if remote succeeds');
        });

        test('devrait désactiver le distant si configuré', async () => {
            const config = new EnvConfig();
            config.set('USE_REMOTE_CONTENT', false);

            const scanner = new ContentScanner();

            let remoteAttempts = 0;
            let localAttempts = 0;

            global.fetch = async (url) => {
                if (url.includes('localhost:8083')) {
                    remoteAttempts++;
                    throw new Error('Should not be called');
                }
                if (url.includes('js/content/')) {
                    localAttempts++;
                    return {
                        ok: true,
                        json: async () => ({ name: "Local Only", vocabulary: { "local": "only" } })
                    };
                }
                throw new Error('Unknown URL');
            };

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

            assert.equal(remoteAttempts, 0, 'Should not try remote when disabled');
            assert.ok(localAttempts > 0, 'Should use local');
        });
    });

    describe('Gestion des erreurs en cascade', () => {
        test('devrait propager les erreurs si tout échoue', async () => {
            global.fetch = async () => {
                throw new Error('All methods failed');
            };

            const scanner = new ContentScanner();

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

            const loader = new GameLoader();

            try {
                await loader.loadGame('whack-a-mole', 'nonexistent-content', {});
                assert.fail('Should have thrown an error');
            } catch (error) {
                assert.ok(error.message.includes('Contenu non trouvé'));
            }
        });

        test('devrait logger toutes les erreurs intermédiaires', async () => {
            global.fetch = async (url) => {
                if (url.includes('localhost:8083')) {
                    throw new Error('Remote connection failed');
                }
                if (url.includes('js/content/')) {
                    throw new Error('Local file not found');
                }
                throw new Error('Unknown error');
            };

            const scanner = new ContentScanner();

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

            const logs = logCapture.getLogs();
            const errorLogs = logs.filter(log => log.level === 'WARN' || log.level === 'ERROR');

            assert.ok(errorLogs.length > 0, 'Should log intermediate errors');
            assert.ok(errorLogs.some(log => log.message.includes('Remote connection failed')));
            assert.ok(errorLogs.some(log => log.message.includes('Local file not found')));
        });
    });

    describe('Performance et cache', () => {
        test('devrait éviter de recharger les modules déjà chargés', async () => {
            const scanner = new ContentScanner();

            // Précharger un module
            global.ContentModules = {
                TestContent: {
                    name: "Already Loaded",
                    vocabulary: { "cached": "mis en cache" }
                }
            };

            let fetchCalls = 0;
            global.fetch = async () => {
                fetchCalls++;
                throw new Error('Should not be called');
            };

            // Essayer de scanner un fichier pour un module déjà chargé
            const result = await scanner.scanContentFile('test-content.json');

            assert.ok(result);
            assert.equal(result.name, "Already Loaded");
            assert.equal(fetchCalls, 0, 'Should not fetch if module already loaded');
        });

        test('le chargement simultané de contenu devrait fonctionner', async () => {
            const scanner = new ContentScanner();

            const promises = [
                scanner.loadJsonContent('content1.json'),
                scanner.loadJsonContent('content2.json'),
                scanner.loadJsonContent('content3.json')
            ];

            let fetchCount = 0;
            global.fetch = async (url) => {
                fetchCount++;
                await new Promise(resolve => setTimeout(resolve, 10)); // Simule latence
                return {
                    ok: true,
                    json: async () => ({
                        name: `Content ${fetchCount}`,
                        vocabulary: { [`word${fetchCount}`]: `translation${fetchCount}` }
                    })
                };
            };

            const results = await Promise.allSettled(promises);

            assert.equal(results.length, 3);
            results.forEach((result, index) => {
                assert.equal(result.status, 'fulfilled', `Promise ${index} should succeed`);
            });

            // Vérifier que les modules sont chargés
            assert.ok(global.ContentModules.Content1);
            assert.ok(global.ContentModules.Content2);
            assert.ok(global.ContentModules.Content3);
        });
    });

    describe('Validation de l\'intégrité des données', () => {
        test('devrait valider la structure du contenu chargé', async () => {
            global.fetch = async () => ({
                ok: true,
                json: async () => ({
                    name: "Valid Content",
                    vocabulary: {
                        "valid": "valide",
                        "test": "test"
                    },
                    difficulty: "medium"
                })
            });

            const scanner = new ContentScanner();
            await scanner.loadJsonContent('valid-content.json');

            const loader = new GameLoader();

            // Devrait réussir avec un contenu valide
            assert.doesNotThrow(() => {
                loader.validateGameContent(global.ContentModules.ValidContent, 'whack-a-mole');
            });
        });

        test('devrait rejeter le contenu invalide', async () => {
            global.fetch = async () => ({
                ok: true,
                json: async () => ({
                    name: "Invalid Content",
                    // Pas de vocabulary
                })
            });

            const scanner = new ContentScanner();
            await scanner.loadJsonContent('invalid-content.json');

            const loader = new GameLoader();

            assert.throws(() => {
                loader.validateGameContent(global.ContentModules.InvalidContent, 'whack-a-mole');
            });
        });
    });
});