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 { sampleJSONContent, gameTestData } from '../fixtures/content-samples.js';
import { readFileSync } from 'fs';
import path from 'path';

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

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

        // Mock GameModules avec des jeux de test
        global.GameModules = {
            WhackAMole: class {
                constructor(options) {
                    this.container = options.container;
                    this.content = options.content;
                    this.onScoreUpdate = options.onScoreUpdate;
                    this.onGameEnd = options.onGameEnd;
                }
                start() { this.started = true; }
                destroy() { this.destroyed = true; }
                restart() { this.restarted = true; }
            },
            MemoryMatch: class {
                constructor(options) {
                    this.container = options.container;
                    this.content = options.content;
                }
                start() { this.started = true; }
                destroy() { this.destroyed = true; }
            },
            QuizGame: class {
                constructor(options) {
                    this.container = options.container;
                    this.content = options.content;
                }
                start() { this.started = true; }
                destroy() { this.destroyed = true; }
            }
        };

        // Mock ContentModules
        global.ContentModules = {
            TestContent: sampleJSONContent
        };

        // Charger GameLoader
        const loaderPath = path.resolve(process.cwd(), 'js/core/game-loader.js');
        const code = readFileSync(loaderPath, 'utf8');

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

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

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

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

            assert.ok(loader instanceof GameLoader);
            assert.equal(loader.currentGame, null);
            assert.equal(loader.currentGameType, null);
        });
    });

    describe('Chargement de jeux', () => {
        test('loadGame devrait charger un jeu avec succès', async () => {
            const loader = new GameLoader();
            const container = { innerHTML: '' };

            const scoreCallback = (score) => {};
            const endCallback = () => {};

            const game = await loader.loadGame(
                'whack-a-mole',
                'test-content',
                container,
                scoreCallback,
                endCallback
            );

            assert.ok(game);
            assert.ok(game instanceof global.GameModules.WhackAMole);
            assert.equal(game.container, container);
            assert.equal(game.content, sampleJSONContent);
            assert.equal(loader.currentGame, game);
            assert.equal(loader.currentGameType, 'whack-a-mole');
        });

        test('loadGame devrait nettoyer le jeu précédent', async () => {
            const loader = new GameLoader();
            const container = { innerHTML: '' };

            // Charger un premier jeu
            const game1 = await loader.loadGame('whack-a-mole', 'test-content', container);
            game1.start();

            // Charger un second jeu
            const game2 = await loader.loadGame('memory-match', 'test-content', container);

            assert.equal(game1.destroyed, true);
            assert.equal(loader.currentGame, game2);
            assert.ok(game2 instanceof global.GameModules.MemoryMatch);
        });

        test('loadGame devrait échouer si le jeu n\'existe pas', async () => {
            const loader = new GameLoader();
            const container = { innerHTML: '' };

            try {
                await loader.loadGame('nonexistent-game', 'test-content', container);
                assert.fail('Should have thrown an error');
            } catch (error) {
                assert.ok(error.message.includes('Jeu non trouvé'));
            }
        });

        test('loadGame devrait échouer si le contenu n\'existe pas', async () => {
            const loader = new GameLoader();
            const container = { innerHTML: '' };

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

    describe('Conversion de noms', () => {
        test('getGameClassName devrait convertir les noms de jeu', () => {
            const loader = new GameLoader();

            assert.equal(loader.getGameClassName('whack-a-mole'), 'WhackAMole');
            assert.equal(loader.getGameClassName('memory-match'), 'MemoryMatch');
            assert.equal(loader.getGameClassName('quiz-game'), 'QuizGame');
            assert.equal(loader.getGameClassName('fill-the-blank'), 'FillTheBlank');
        });

        test('getContentModuleName devrait convertir les noms de contenu', () => {
            const loader = new GameLoader();

            assert.equal(loader.getContentModuleName('test-content'), 'TestContent');
            assert.equal(loader.getContentModuleName('sbs-level-7-8-new'), 'SBSLevel78New');
            assert.equal(loader.getContentModuleName('english-class-demo'), 'EnglishClassDemo');
        });

        test('toPascalCase devrait convertir correctement', () => {
            const loader = new GameLoader();

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

    describe('Gestion du cycle de vie des jeux', () => {
        test('cleanup devrait détruire le jeu actuel', () => {
            const loader = new GameLoader();
            const mockGame = {
                destroy: () => { mockGame.destroyed = true; },
                destroyed: false
            };

            loader.currentGame = mockGame;
            loader.currentGameType = 'test-game';

            loader.cleanup();

            assert.equal(mockGame.destroyed, true);
            assert.equal(loader.currentGame, null);
            assert.equal(loader.currentGameType, null);
        });

        test('cleanup devrait être sûr si pas de jeu actuel', () => {
            const loader = new GameLoader();

            // Ne devrait pas lever d'erreur
            loader.cleanup();

            assert.equal(loader.currentGame, null);
            assert.equal(loader.currentGameType, null);
        });

        test('restartCurrentGame devrait redémarrer le jeu actuel', () => {
            const loader = new GameLoader();
            const mockGame = {
                restart: () => { mockGame.restarted = true; },
                restarted: false
            };

            loader.currentGame = mockGame;

            loader.restartCurrentGame();

            assert.equal(mockGame.restarted, true);
        });

        test('restartCurrentGame devrait échouer si pas de jeu actuel', () => {
            const loader = new GameLoader();

            try {
                loader.restartCurrentGame();
                assert.fail('Should have thrown an error');
            } catch (error) {
                assert.ok(error.message.includes('Aucun jeu actuel'));
            }
        });
    });

    describe('Validation et contraintes', () => {
        test('validateGameContent devrait valider le contenu minimal', () => {
            const loader = new GameLoader();

            const validContent = { vocabulary: { 'test': 'test' } };
            const invalidContent1 = {};
            const invalidContent2 = { vocabulary: {} };
            const invalidContent3 = null;

            assert.doesNotThrow(() => loader.validateGameContent(validContent, 'test-game'));

            assert.throws(() => loader.validateGameContent(invalidContent1, 'test-game'));
            assert.throws(() => loader.validateGameContent(invalidContent2, 'test-game'));
            assert.throws(() => loader.validateGameContent(invalidContent3, 'test-game'));
        });

        test('checkGameRequirements devrait vérifier les exigences spécifiques', () => {
            const loader = new GameLoader();

            // Contenu avec vocabulaire suffisant
            const goodContent = {
                vocabulary: gameTestData.whackAMole.vocabulary
            };

            // Contenu avec vocabulaire insuffisant
            const poorContent = {
                vocabulary: { 'only': 'one' }
            };

            assert.doesNotThrow(() => loader.checkGameRequirements(goodContent, 'whack-a-mole'));
            assert.throws(() => loader.checkGameRequirements(poorContent, 'whack-a-mole'));
        });
    });

    describe('Gestion des erreurs', () => {
        test('devrait logger les erreurs de chargement', async () => {
            const loader = new GameLoader();
            const container = { innerHTML: '' };

            try {
                await loader.loadGame('nonexistent-game', 'test-content', container);
            } catch (error) {
                // Expected
            }

            const errorLogs = logCapture.getLogs('ERROR');
            assert.ok(errorLogs.length > 0);
            assert.ok(errorLogs.some(log => log.message.includes('Erreur lors du chargement')));
        });

        test('devrait gérer les erreurs de construction de jeu', async () => {
            const loader = new GameLoader();
            const container = { innerHTML: '' };

            // Mock un jeu qui lève une erreur à la construction
            global.GameModules.BrokenGame = class {
                constructor() {
                    throw new Error('Construction failed');
                }
            };

            try {
                await loader.loadGame('broken-game', 'test-content', container);
                assert.fail('Should have thrown an error');
            } catch (error) {
                assert.ok(error.message.includes('Erreur lors de la création'));
            }
        });
    });

    describe('État et informations', () => {
        test('getCurrentGameInfo devrait retourner les informations du jeu actuel', () => {
            const loader = new GameLoader();
            const mockGame = {};

            loader.currentGame = mockGame;
            loader.currentGameType = 'test-game';

            const info = loader.getCurrentGameInfo();

            assert.equal(info.gameType, 'test-game');
            assert.equal(info.game, mockGame);
            assert.equal(typeof info.loadTime, 'number');
        });

        test('getCurrentGameInfo devrait retourner null si pas de jeu', () => {
            const loader = new GameLoader();

            const info = loader.getCurrentGameInfo();

            assert.equal(info, null);
        });

        test('isGameLoaded devrait retourner l\'état correct', () => {
            const loader = new GameLoader();

            assert.equal(loader.isGameLoaded(), false);

            loader.currentGame = {};
            assert.equal(loader.isGameLoaded(), true);

            loader.currentGame = null;
            assert.equal(loader.isGameLoaded(), false);
        });
    });
});