#include "core/system/global_events.hpp"

#include <SDL3/SDL.h>  // Para SDL_EventType, SDL_Event, SDL_LogInfo, SDL_LogCategory

#include <cstddef>   // Para size_t
#include <iostream>  // Para std::cout
#include <string>    // Para allocator, operator+, string
#include <vector>    // Para vector

#include "core/input/input.hpp"       // Para Input
#include "core/input/mouse.hpp"       // Para handleEvent
#include "core/locale/lang.hpp"       // Para getText
#include "core/rendering/screen.hpp"  // Para Screen
#include "core/system/section.hpp"    // Para Name, Options, name, options
#include "game/options.hpp"           // Para GamepadManager, gamepad_manager
#include "game/ui/notifier.hpp"       // Para Notifier
#include "game/ui/service_menu.hpp"   // Para ServiceMenu

namespace GlobalEvents {
    namespace {
        // Durante el arranque se drenan los SDL_EVENT_GAMEPAD_ADDED de los mandos
        // ya conectados. Esos eventos sí deben reasignar mandos a jugadores, pero
        // no deben mostrar notificación: no son hotplug, son detección inicial.
        bool startup_in_progress = true;
    }  // namespace

    // Comprueba los eventos de Input y muestra notificaciones
    void handleInputEvents(const SDL_Event& event) {
        if (event.type != SDL_EVENT_GAMEPAD_ADDED && event.type != SDL_EVENT_GAMEPAD_REMOVED) {
            return;
        }

        static auto* input_ = Input::get();
        auto message = input_->handleEvent(event);

        // Reasignar siempre: tanto en arranque como en hotplug en caliente.
        Options::gamepad_manager.assignAndLinkGamepads();
        Options::gamepad_manager.resyncGamepadsWithPlayers();

        // Durante el preload ServiceMenu aún no existe: solo refresca si está vivo.
        if (ServiceMenu::get() != nullptr) {
            ServiceMenu::get()->refresh();
        }

        if (startup_in_progress || message.empty()) {
            return;
        }

        // Reemplazo de palabras clave por texto localizado
        size_t pos;
        while ((pos = message.find(" CONNECTED")) != std::string::npos) {
            message.replace(pos, std::string(" CONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] CONNECTED"));
        }
        while ((pos = message.find(" DISCONNECTED")) != std::string::npos) {
            message.replace(pos, std::string(" DISCONNECTED").length(), " " + Lang::getText("[NOTIFICATIONS] DISCONNECTED"));
        }

        // Notifier también puede no existir todavía si la notificación se
        // disparase antes de finishBoot(). Protegido por si acaso.
        if (Notifier::get() != nullptr) {
            Notifier::get()->show({message});
        }
    }

    void markStartupComplete() {
        startup_in_progress = false;
    }

    // Comprueba los eventos que se pueden producir en cualquier sección del juego
    void handle(const SDL_Event& event) {
        switch (event.type) {
            case SDL_EVENT_QUIT:  // Evento de salida de la aplicación
                Section::name = Section::Name::QUIT;
                Section::options = Section::Options::NONE;
                return;

            case SDL_EVENT_RENDER_DEVICE_RESET:
            case SDL_EVENT_RENDER_TARGETS_RESET:
                std::cout << "SDL_RENDER_TARGETS_RESET" << '\n';
                break;

            case SDL_EVENT_WINDOW_RESIZED:
                Screen::initShaders();
                break;

            default:
                break;
        }

        // Durante el preload ServiceMenu aún no existe
        if (ServiceMenu::get() != nullptr) {
            ServiceMenu::get()->handleEvent(event);
        }
        Mouse::handleEvent(event);
        handleInputEvents(event);
    }
}  // namespace GlobalEvents