import { Core } from '../src/Core';
import { Scene } from '../src/ECS/Scene';
import { DebugManager } from '../src/Utils/Debug/DebugManager';
import { DebugConfigService } from '../src/Utils/Debug/DebugConfigService';
import { IECSDebugConfig } from '../src/Types';
import { createLogger } from '../src/Utils/Logger';

const logger = createLogger('DebugManagerTest');

class TestScene extends Scene {
    public initializeCalled = false;

    public override initialize(): void {
        this.initializeCalled = true;
    }

    public override begin(): void {
    }

    public override end(): void {
    }
}

describe('DebugManager DI Architecture Tests', () => {
    let originalConsoleWarn: typeof console.warn;
    let originalConsoleError: typeof console.error;

    beforeEach(() => {
        (Core as any)._instance = null;
        originalConsoleWarn = console.warn;
        originalConsoleError = console.error;
        console.warn = jest.fn();
        console.error = jest.fn();
    });

    afterEach(() => {
        console.warn = originalConsoleWarn;
        console.error = originalConsoleError;
        if (Core.Instance) {
            Core.destroy();
        }
    });

    describe('DebugConfigService - Configuration Service', () => {
        test('should create with default configuration', () => {
            const configService = new DebugConfigService();
            const config = configService.getConfig();

            expect(config).toBeDefined();
            expect(config.enabled).toBe(false);
            expect(config.websocketUrl).toBe('');
            expect(config.debugFrameRate).toBe(30);
            expect(config.autoReconnect).toBe(true);
            expect(config.channels).toBeDefined();
        });

        test('should set and get configuration', () => {
            const configService = new DebugConfigService();
            const newConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 60,
                autoReconnect: false,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            configService.setConfig(newConfig);
            const retrievedConfig = configService.getConfig();

            expect(retrievedConfig).toEqual(newConfig);
            expect(retrievedConfig.enabled).toBe(true);
            expect(retrievedConfig.websocketUrl).toBe('ws://localhost:9229');
            expect(retrievedConfig.debugFrameRate).toBe(60);
        });

        test('should return correct enabled status', () => {
            const configService = new DebugConfigService();
            expect(configService.isEnabled()).toBe(false);

            configService.setConfig({
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            });

            expect(configService.isEnabled()).toBe(true);
        });

        test('should implement dispose method', () => {
            const configService = new DebugConfigService();
            expect(() => configService.dispose()).not.toThrow();
        });
    });

    describe('DebugManager - DI Initialization', () => {
        test('should initialize DebugManager through DI when debug config is enabled', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            const core = Core.create({
                debug: true,
                debugConfig: debugConfig
            });

            expect((core as any)._debugManager).toBeDefined();
            expect((core as any)._debugManager).toBeInstanceOf(DebugManager);
        });

        test('should not create DebugManager when debug config is disabled', () => {
            const core = Core.create({
                debug: true,
                debugConfig: {
                    enabled: false,
                    websocketUrl: '',
                    debugFrameRate: 30,
                    autoReconnect: true,
                    channels: {
                        entities: true,
                        systems: true,
                        performance: true,
                        components: true,
                        scenes: true
                    }
                }
            });

            expect((core as any)._debugManager).toBeUndefined();
        });

        test('should not create DebugManager when no debug config provided', () => {
            const core = Core.create({ debug: true });
            expect((core as any)._debugManager).toBeUndefined();
        });

        test('should register DebugConfigService in ServiceContainer', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({
                debug: true,
                debugConfig: debugConfig
            });

            const configService = Core.services.resolve(DebugConfigService);
            expect(configService).toBeDefined();
            expect(configService).toBeInstanceOf(DebugConfigService);
        });

        test('should inject all dependencies correctly', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            const core = Core.create({
                debug: true,
                debugConfig: debugConfig
            });

            const debugManager = (core as any)._debugManager as DebugManager;
            expect(debugManager).toBeDefined();

            const sceneManager = (debugManager as any).sceneManager;
            const performanceMonitor = (debugManager as any).performanceMonitor;
            const config = (debugManager as any).config;

            expect(sceneManager).toBeDefined();
            expect(performanceMonitor).toBeDefined();
            expect(config).toBeDefined();
            expect(config.enabled).toBe(true);
            expect(config.websocketUrl).toBe('ws://localhost:9229');
        });
    });

    describe('Core.enableDebug - Runtime Activation', () => {
        test('should enable debug at runtime', () => {
            const core = Core.create({ debug: true });
            expect(Core.isDebugEnabled).toBe(false);

            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.enableDebug(debugConfig);

            expect(Core.isDebugEnabled).toBe(true);
            expect((core as any)._debugManager).toBeDefined();
        });

        test('should create DebugConfigService when enabling debug at runtime', () => {
            Core.create({ debug: true });

            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.enableDebug(debugConfig);

            const configService = Core.services.resolve(DebugConfigService);
            expect(configService).toBeDefined();
            expect(configService.getConfig()).toEqual(debugConfig);
        });

        test('should update existing DebugManager config when already enabled', () => {
            const initialConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({ debug: true, debugConfig: initialConfig });

            const updatedConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:8080',
                debugFrameRate: 60,
                autoReconnect: false,
                channels: {
                    entities: false,
                    systems: true,
                    performance: true,
                    components: false,
                    scenes: true
                }
            };

            Core.enableDebug(updatedConfig);

            expect(Core.isDebugEnabled).toBe(true);
            const debugManager = (Core.Instance as any)._debugManager;
            expect(debugManager).toBeDefined();
        });

        test('should show warning when enabling debug without Core instance', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.enableDebug(debugConfig);

            expect(console.warn).toHaveBeenCalledWith(
                expect.stringContaining('Core实例未创建，请先调用Core.create()')
            );
        });
    });

    describe('Core.disableDebug', () => {
        test('should disable debug functionality', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({ debug: true, debugConfig: debugConfig });
            expect(Core.isDebugEnabled).toBe(true);

            Core.disableDebug();

            expect(Core.isDebugEnabled).toBe(false);
            expect((Core.Instance as any)._debugManager).toBeUndefined();
        });

        test('should handle disabling when Core instance does not exist', () => {
            expect(() => Core.disableDebug()).not.toThrow();
        });

        test('should handle disabling when debug was never enabled', () => {
            Core.create({ debug: true });
            expect(() => Core.disableDebug()).not.toThrow();
        });
    });

    describe('DebugManager - Auto Update Integration', () => {
        test('should be registered as updatable service', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({ debug: true, debugConfig: debugConfig });

            const serviceContainer = (Core.Instance as any)._serviceContainer;
            const updatableCount = serviceContainer.getUpdatableCount();

            expect(updatableCount).toBeGreaterThan(0);
        });

        test('should be updated during Core.update cycle', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            const core = Core.create({ debug: true, debugConfig: debugConfig });
            const debugManager = (core as any)._debugManager as DebugManager;
            const updateSpy = jest.spyOn(debugManager, 'update');

            Core.update(0.016);

            expect(updateSpy).toHaveBeenCalledWith(0.016);
        });
    });

    describe('DebugManager - Scene Integration', () => {
        test('should respond to scene changes', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            const core = Core.create({ debug: true, debugConfig: debugConfig });
            const debugManager = (core as any)._debugManager as DebugManager;
            const sceneChangeSpy = jest.spyOn(debugManager, 'onSceneChanged');

            const testScene = new TestScene();
            Core.setScene(testScene);

            expect(sceneChangeSpy).toHaveBeenCalled();
        });

        test('should collect debug data from current scene', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({ debug: true, debugConfig: debugConfig });
            const testScene = new TestScene();
            Core.setScene(testScene);

            const debugData = Core.getDebugData();

            expect(debugData).toBeDefined();
            expect(debugData).toHaveProperty('timestamp');
            expect(debugData).toHaveProperty('frameworkVersion');
            expect(debugData).toHaveProperty('currentScene');
        });
    });

    describe('DebugManager - Configuration Management', () => {
        test('should use correct debug frame rate', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 60,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            const core = Core.create({ debug: true, debugConfig: debugConfig });
            const debugManager = (core as any)._debugManager as DebugManager;
            const sendInterval = (debugManager as any).sendInterval;

            expect(sendInterval).toBe(1000 / 60);
        });

        test('should handle channel configuration correctly', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: false,
                    performance: false,
                    components: true,
                    scenes: false
                }
            };

            Core.create({ debug: true, debugConfig: debugConfig });
            const testScene = new TestScene();
            Core.setScene(testScene);

            const debugData = Core.getDebugData() as any;

            expect(debugData.entities).toBeDefined();
            expect(debugData.components).toBeDefined();
            expect(debugData.systems).toBeUndefined();
            expect(debugData.performance).toBeUndefined();
            expect(debugData.scenes).toBeUndefined();
        });
    });

    describe('DebugManager - Lifecycle', () => {
        test('should start automatically on creation', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            const core = Core.create({ debug: true, debugConfig: debugConfig });
            const debugManager = (core as any)._debugManager as DebugManager;
            const isRunning = (debugManager as any).isRunning;

            expect(isRunning).toBe(true);
        });

        test('should stop when disabling debug', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({ debug: true, debugConfig: debugConfig });
            const debugManager = (Core.Instance as any)._debugManager as DebugManager;
            const stopSpy = jest.spyOn(debugManager, 'stop');

            Core.disableDebug();

            expect(stopSpy).toHaveBeenCalled();
        });

        test('should dispose properly on Core.destroy', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({ debug: true, debugConfig: debugConfig });
            const debugManager = (Core.Instance as any)._debugManager as DebugManager;
            const stopSpy = jest.spyOn(debugManager, 'stop');

            Core.destroy();

            expect(stopSpy).toHaveBeenCalled();
        });
    });

    describe('DebugManager - Pure DI Architecture Validation', () => {
        test('should resolve all dependencies through ServiceContainer', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({ debug: true, debugConfig: debugConfig });

            const debugConfigService = Core.services.resolve(DebugConfigService);
            expect(debugConfigService).toBeDefined();

            const config = debugConfigService.getConfig();
            expect(config).toEqual(debugConfig);
        });

        test('should not have mixed DI patterns', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            const core = Core.create({ debug: true, debugConfig: debugConfig });
            const debugManager = (core as any)._debugManager as DebugManager;

            const sceneManager = (debugManager as any).sceneManager;
            const performanceMonitor = (debugManager as any).performanceMonitor;
            const config = (debugManager as any).config;

            expect(sceneManager).toBeDefined();
            expect(performanceMonitor).toBeDefined();
            expect(config).toBeDefined();
            expect(config.enabled).toBe(true);
        });

        test('should use factory pattern for registration', () => {
            const debugConfig: IECSDebugConfig = {
                enabled: true,
                websocketUrl: 'ws://localhost:9229',
                debugFrameRate: 30,
                autoReconnect: true,
                channels: {
                    entities: true,
                    systems: true,
                    performance: true,
                    components: true,
                    scenes: true
                }
            };

            Core.create({ debug: true, debugConfig: debugConfig });

            const debugManager1 = (Core.Instance as any)._debugManager;
            const debugManager2 = Core.services.resolve(DebugManager);

            expect(debugManager1).toBe(debugManager2);
        });
    });
});
