#include "StateManager.h"
#include "../../graphics/renderers/RenderManager.h"
#include <SDL3/SDL.h>

StateManager::StateManager(AppState initial)
    : m_currentState(initial)
    , m_previousState(initial)
{
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "StateManager initialized with state: %s", getStateName(initial));
}

void StateManager::registerEventHandler(AppState state, EventHandler handler) {
    if (!isValidState(state)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for event handler registration");
        return;
    }
    
    m_eventHandlers[stateToInt(state)].push_back(std::move(handler));
    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Event handler registered for state: %s", getStateName(state));
}

void StateManager::registerUpdateHandler(AppState state, UpdateHandler handler) {
    if (!isValidState(state)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for update handler registration");
        return;
    }
    
    m_updateHandlers[stateToInt(state)].push_back(std::move(handler));
    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Update handler registered for state: %s", getStateName(state));
}

void StateManager::registerRenderHandler(AppState state, RenderHandler handler) {
    if (!isValidState(state)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for render handler registration");
        return;
    }
    
    m_renderHandlers[stateToInt(state)].push_back(std::move(handler));
    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Render handler registered for state: %s", getStateName(state));
}

void StateManager::registerOnEnter(AppState state, Hook hook) {
    if (!isValidState(state)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for enter hook registration");
        return;
    }
    
    m_onEnterHooks[stateToInt(state)].push_back(std::move(hook));
    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Enter hook registered for state: %s", getStateName(state));
}

void StateManager::registerOnExit(AppState state, Hook hook) {
    if (!isValidState(state)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid state for exit hook registration");
        return;
    }
    
    m_onExitHooks[stateToInt(state)].push_back(std::move(hook));
    SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Exit hook registered for state: %s", getStateName(state));
}

// Legacy compatibility: overload accepting a no-arg function as handler
void StateManager::registerHandler(AppState s, std::function<void()> h) {
    EventHandler wrapper = [h = std::move(h)](const SDL_Event&) { h(); };
    registerEventHandler(s, std::move(wrapper));
}

bool StateManager::setState(AppState newState) {
    if (!isValidState(newState)) {
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Attempted to set invalid state");
        return false;
    }

    if (newState == m_currentState) {
        SDL_LogDebug(SDL_LOG_CATEGORY_APPLICATION, "Attempted to set same state: %s", getStateName(newState));
        return true;
    }

    if (!canTransitionTo(newState)) {
        SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Invalid state transition from %s to %s", 
                   getStateName(m_currentState), getStateName(newState));
        return false;
    }

    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "State transition: %s -> %s", 
               getStateName(m_currentState), getStateName(newState));
    // Persistent trace for debugging abrupt exits
    {
        FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "setState start %s -> %s\n", getStateName(m_currentState), getStateName(newState)); fclose(f); }
    }

    // Execute exit hooks for current state
    executeExitHooks(m_currentState);

    // Update state
    m_previousState = m_currentState;
    m_currentState = newState;

    // Execute enter hooks for new state
    executeEnterHooks(m_currentState);

    // Trace completion
    {
        FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "setState end %s\n", getStateName(m_currentState)); fclose(f); }
    }

    return true;
}

void StateManager::handleEvent(const SDL_Event& event) {
    auto it = m_eventHandlers.find(stateToInt(m_currentState));
    if (it == m_eventHandlers.end()) {
        return;
    }

    for (auto& handler : it->second) {
        try {
            handler(event);
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in event handler for state %s: %s", 
                        getStateName(m_currentState), e.what());
        }
    }
}

void StateManager::update(float deltaTime) {
    auto it = m_updateHandlers.find(stateToInt(m_currentState));
    if (it == m_updateHandlers.end()) {
        return;
    }

    for (auto& handler : it->second) {
        try {
            handler(deltaTime);
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in update handler for state %s: %s", 
                        getStateName(m_currentState), e.what());
        }
    }
}

void StateManager::render(RenderManager& renderer) {
    auto it = m_renderHandlers.find(stateToInt(m_currentState));
    if (it == m_renderHandlers.end()) {
        return;
    }

    for (auto& handler : it->second) {
        try {
            handler(renderer);
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in render handler for state %s: %s", 
                        getStateName(m_currentState), e.what());
        }
    }
}

bool StateManager::isValidState(AppState state) const {
    switch (state) {
        case AppState::Loading:
        case AppState::Video:
        case AppState::Menu:
        case AppState::Options:
        case AppState::LevelSelector:
        case AppState::Playing:
        case AppState::LevelSelect:
        case AppState::GameOver:
            return true;
        default:
            return false;
    }
}

bool StateManager::canTransitionTo(AppState newState) const {
    // For now, allow all transitions. Can be enhanced later with state machine validation
    return isValidState(newState);
}

const char* StateManager::getStateName(AppState state) const {
    switch (state) {
        case AppState::Loading: return "Loading";
        case AppState::Video: return "Video";
        case AppState::Menu: return "Menu";
        case AppState::Options: return "Options";
        case AppState::LevelSelector: return "LevelSelector";
        case AppState::Playing: return "Playing";
        case AppState::LevelSelect: return "LevelSelect";
        case AppState::GameOver: return "GameOver";
        default: return "Unknown";
    }
}

void StateManager::executeEnterHooks(AppState state) {
    auto it = m_onEnterHooks.find(stateToInt(state));
    if (it == m_onEnterHooks.end()) {
        return;
    }

    int idx = 0;
    for (auto& hook : it->second) {
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Executing enter hook %d for state %s", idx, getStateName(state));
        // Also write to trace file for persistent record
        {
            FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "executeEnterHook %d %s\n", idx, getStateName(state)); fclose(f); }
        }
        try {
            hook();
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in enter hook for state %s: %s", 
                        getStateName(state), e.what());
        }
        ++idx;
    }
}

void StateManager::executeExitHooks(AppState state) {
    auto it = m_onExitHooks.find(stateToInt(state));
    if (it == m_onExitHooks.end()) {
        return;
    }

    int idx = 0;
    for (auto& hook : it->second) {
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Executing exit hook %d for state %s", idx, getStateName(state));
        {
            FILE* f = fopen("spacetris_trace.log", "a"); if (f) { fprintf(f, "executeExitHook %d %s\n", idx, getStateName(state)); fclose(f); }
        }
        try {
            hook();
        } catch (const std::exception& e) {
            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Exception in exit hook for state %s: %s", 
                        getStateName(state), e.what());
        }
        ++idx;
    }
}
