-- addon_game_mode_tests.lua
-- Unit tests for addon_game_mode.lua

local TestHelper = require("libraries/test_helper")
local MockHttpClient = require("libraries/mock_http_client")
local MockHeroController = require("libraries/mock_hero_controller")

local tests = TestHelper:new()

-- Setup mock environment
local function setupMockEnvironment()
    -- Mock Dota 2 constants
    _G.DOTA_GAMERULES_STATE_GAME_IN_PROGRESS = 4
    _G.DOTA_GAMERULES_STATE_HERO_SELECTION = 2
    _G.DOTA_GAMERULES_STATE_POST_GAME = 5
    
    -- Mock global functions
    _G.SendMessageToUI = function(msg, type) end
    
    _G.CustomNetTables = {
        SetTableValue = function(self, tableName, key, value)
            if tableName == "hello_world" and key == "game_info" then
                return true
            end
        end
    }
    
    _G.IsValidEntity = function(entity) 
        return entity ~= nil 
    end
    
    -- Create mock GameRules with working GameModeEntity
    local mockGameMode = {
        thinks = {},
        SetThink = function(self, func, name, interval)
            self.thinks[name] = {func = func, interval = interval}
            -- Execute once for testing
            local result = func()
            if type(result) == "function" then
                return result()
            end
            return result or interval
        end
    }
    
    local mockGameRules = {
        _gameState = _G.DOTA_GAMERULES_STATE_GAME_IN_PROGRESS,
        _gameMode = mockGameMode,
        
        GetGameModeEntity = function(self)
            return self._gameMode
        end,
        
        GetDOTATime = function(realTime, gamePaused)
            return 123.45
        end,
        
        State_Get = function(self)
            return self._gameState
        end,
        
        SetState = function(self, state)
            self._gameState = state
        end
    }
    
    return mockGameRules
end

-- Load the game mode module
require("addon_game_mode")

tests:describe("Unified Initialization", function()
    
    tests:test("InitializeGameMode creates context and registers thinks", function()
        local mockRules = setupMockEnvironment()
        local pingCalled = false
        
        MockHttpClient.Ping = function(callback)
            pingCalled = true
            callback(true, {message = "Test pong"})
        end
        
        local context = InitializeGameMode(MockHttpClient, MockHeroController, mockRules)
        
        assert(context ~= nil, "Context should be created")
        assert(pingCalled, "Server connection should be tested")
        
        -- Check that think functions were registered
        local gameMode = mockRules:GetGameModeEntity()
        assert(gameMode.thinks["PollForCommands"] ~= nil, "PollForCommands should be registered")
        assert(gameMode.thinks["SendGameStateUpdate"] ~= nil, "SendGameStateUpdate should be registered")
        assert(gameMode.thinks["OnThink"] ~= nil, "OnThink should be registered")
    end)
   
    tests:test("Activate function works like Dota expects", function()
        -- Mock the Dota environment
        _G.GameRules = setupMockEnvironment()
        local originalRequire = require
        
        -- Mock require to return our test modules
        _G.require = function(module)
            if module == "libraries/http_client" then
                -- Return MockHttpClient instead of loading the real one
                MockHttpClient.Init = MockHttpClient.Init or function() end
                return MockHttpClient
            elseif module == "libraries/mock_hero_controller" then
                return MockHeroController
            elseif module == "libraries/util" or module == "libraries/custom_events" then
                -- These are safe to load
                return originalRequire(module)
            else
                -- Don't load other dependencies
                return {}
            end
        end
        
        -- Mock the ping to succeed
        MockHttpClient.Ping = function(callback)
            callback(true, {message = "Test pong from Activate"})
        end
        
        -- Call Activate as Dota would
        Activate()
        
        -- Verify initialization happened
        assert(g_GameContext ~= nil, "Global context should be set after Activate")
        assert(g_GameContext.http ~= nil, "HTTP client should be set")
        assert(g_GameContext.heroes ~= nil, "Hero controller should be set")
        
        -- Cleanup
        _G.require = originalRequire
        _G.GameRules = nil
    end)
    
end)

tests:describe("Command Processing", function()
    
    tests:test("Commands are processed correctly", function()
        local mockRules = setupMockEnvironment()
        local context = InitializeGameMode(MockHttpClient, MockHeroController, mockRules)
        context.rpcConnected = true
        
        local commandProcessed = false
        MockHttpClient.SendRequest = function(path, data, callback)
            if path == "/commands" then
                callback(true, {
                    commands = {{
                        id = "test_cmd",
                        type = "move"
                    }}
                })
            end
        end
        
        MockHeroController.QueueCommand = function(self, command)
            commandProcessed = (command.id == "test_cmd")
        end
        
        PollForCommands(context)
        assert(commandProcessed, "Command should be processed")
    end)
    
end)

tests:describe("State Updates", function()
    
    tests:test("Game state is sent to server", function()
        local mockRules = setupMockEnvironment()
        local context = InitializeGameMode(MockHttpClient, MockHeroController, mockRules)
        context.rpcConnected = true
        
        local stateSent = false
        MockHttpClient.SendEvent = function(eventType, eventData)
            if eventType == "game_state_update" then
                stateSent = true
                assert(eventData.game_time == 123.45, "Game time should be included")
            end
        end
        
        SendGameStateUpdate(context)
        assert(stateSent, "State should be sent")
    end)
    
end)

-- Run all tests
local success = tests:summary()
os.exit(success and 0 or 1)
