#include "core/input/global_inputs.hpp"

#include <SDL3/SDL.h>

#include <string>  // Para allocator, operator+, char_traits, string
#include <vector>  // Para vector

#include "core/input/input.hpp"            // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT
#include "core/locale/locale.hpp"          // Para Locale
#include "core/rendering/render_info.hpp"  // Para RenderInfo
#include "core/rendering/screen.hpp"       // Para Screen
#include "core/rendering/screenshot.hpp"   // Para Screenshot
#ifdef _DEBUG
#include "core/system/debug.hpp"  // Para Debug (persistencia de render_info en debug.yaml)
#endif
#include "game/options.hpp"        // Para Options, options, OptionsVideo, Section
#include "game/scene_manager.hpp"  // Para SceneManager
#include "game/ui/console.hpp"     // Para Console
#include "game/ui/notifier.hpp"    // Para Notifier, NotificationText
#include "utils/utils.hpp"         // Para stringInVector

namespace GlobalInputs {

    // Funciones internas
    namespace {
        void handleQuit() {
            // En la escena GAME el comportamiento es siempre el mismo (con o sin modo kiosko)
            if (SceneManager::current == SceneManager::Scene::GAME) {
                const std::string CODE = "PRESS AGAIN TO RETURN TO MENU";
                if (stringInVector(Notifier::get()->getCodes(), CODE)) {
                    SceneManager::current = SceneManager::Scene::TITLE;
                } else {
                    Notifier::get()->show({Locale::get()->get("ui.press_again_menu")}, Notifier::Style::DEFAULT, -1, true, CODE);
                }
                return;
            }

            // En modo kiosko, fuera de GAME: mostrar el texto del kiosko y no salir nunca
            if (Options::kiosk.enabled) {
                const std::string KIOSK_CODE = "KIOSK_EXIT";
                if (!stringInVector(Notifier::get()->getCodes(), KIOSK_CODE)) {
                    Notifier::get()->show({Options::kiosk.text}, Notifier::Style::DEFAULT, -1, true, KIOSK_CODE);
                }
                // Segunda pulsación: notificación ya activa → no hacer nada
                return;
            }

#ifdef __EMSCRIPTEN__
            // En la versión web no se puede salir del juego desde fuera de la escena GAME
            // (el navegador gestiona la pestaña; Escape no cierra nada).
            return;
#else
            // Comportamiento normal fuera del modo kiosko
            const std::string CODE = "PRESS AGAIN TO EXIT";
            if (stringInVector(Notifier::get()->getCodes(), CODE)) {
                SceneManager::current = SceneManager::Scene::QUIT;
            } else {
                Notifier::get()->show({Locale::get()->get("ui.press_again_exit")}, Notifier::Style::DEFAULT, -1, true, CODE);
            }
#endif  // __EMSCRIPTEN__
        }

        void handleSkipSection() {
            switch (SceneManager::current) {
                case SceneManager::Scene::LOGO:
                    SceneManager::current = SceneManager::Scene::TITLE;
                    SceneManager::options = SceneManager::Options::NONE;
                    break;

                default:
                    break;
            }
        }

        void handleToggleBorder() {
            Screen::get()->toggleBorder();
            Notifier::get()->show({Locale::get()->get(Options::video.border.enabled ? "ui.border_enabled" : "ui.border_disabled")});
        }

        void handleToggleVideoMode() {
            Screen::get()->toggleVideoMode();
            Notifier::get()->show({Locale::get()->get(static_cast<int>(Options::video.fullscreen) == 0 ? "ui.fullscreen_disabled" : "ui.fullscreen_enabled")});
        }

        void handleDecWindowZoom() {
            if (Screen::get()->decWindowZoom()) {
                Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)});
            }
        }

        void handleIncWindowZoom() {
            if (Screen::get()->incWindowZoom()) {
                Notifier::get()->show({Locale::get()->get("ui.window_zoom") + std::to_string(Options::window.zoom)});
            }
        }

        void handleToggleShaders() {
            Screen::get()->toggleShaders();
            Notifier::get()->show({Locale::get()->get(Options::video.shader.enabled ? "ui.shaders_enabled" : "ui.shaders_disabled")});
        }

        void handleNextShaderPreset() {
            if (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI) {
                if (!Options::crtpi_presets.empty()) {
                    Options::video.shader.current_crtpi_preset = (Options::video.shader.current_crtpi_preset + 1) % static_cast<int>(Options::crtpi_presets.size());
                    Screen::get()->reloadCrtPi();
                    Notifier::get()->show({Locale::get()->get("ui.crtpi") + " " + prettyName(Options::crtpi_presets[static_cast<size_t>(Options::video.shader.current_crtpi_preset)].name)});
                }
            } else {
                if (!Options::postfx_presets.empty()) {
                    Options::video.shader.current_postfx_preset = (Options::video.shader.current_postfx_preset + 1) % static_cast<int>(Options::postfx_presets.size());
                    Screen::get()->reloadPostFX();
                    Notifier::get()->show({Locale::get()->get("ui.postfx") + " " + prettyName(Options::postfx_presets[static_cast<size_t>(Options::video.shader.current_postfx_preset)].name)});
                }
            }
        }

        void handleNextShader() {
            Screen::get()->nextShader();
            Notifier::get()->show({Locale::get()->get("ui.shader") + " " +
                (Options::video.shader.current_shader == Rendering::ShaderType::CRTPI ? "CRTPI" : "POSTFX")});
        }

        void handleNextPalette() {
            Screen::get()->nextPalette();
            Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())});
        }

        void handlePreviousPalette() {
            Screen::get()->previousPalette();
            Notifier::get()->show({Locale::get()->get("ui.palette") + " " + toUpper(Screen::get()->getPalettePrettyName())});
        }

        void handleScreenshot() {
            std::vector<Uint32> buffer;
            int width = 0;
            int height = 0;
            Screen::get()->captureComposite(buffer, width, height);
            std::string filename = Screenshot::save(buffer.data(), width, height);
            if (!filename.empty()) {
                Notifier::get()->show({filename});
            }
        }

        void handleNextPaletteSortMode() {
            Screen::get()->nextPaletteSortMode();
            Notifier::get()->show({Locale::get()->get("ui.palette_sort") + " " + toUpper(Screen::get()->getPaletteSortModeName())});
        }

        void handleToggleIntegerScale() {
            Screen::get()->toggleIntegerScale();
            Screen::get()->setVideoMode(Options::video.fullscreen);
            Notifier::get()->show({Locale::get()->get(Options::video.integer_scale ? "ui.integer_scale_enabled" : "ui.integer_scale_disabled")});
        }

        void handleToggleVSync() {
            Screen::get()->toggleVSync();
            Notifier::get()->show({Locale::get()->get(Options::video.vertical_sync ? "ui.vsync_enabled" : "ui.vsync_disabled")});
        }

        // Detecta qué acción global ha sido presionada (si alguna)
        auto getPressedAction() -> InputAction {  // NOLINT(readability-function-cognitive-complexity)
            if (Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
                return InputAction::EXIT;
            }
            if (Input::get()->checkAction(InputAction::ACCEPT, Input::DO_NOT_ALLOW_REPEAT)) {
                return InputAction::ACCEPT;
            }
            if (Input::get()->checkAction(InputAction::TOGGLE_BORDER, Input::DO_NOT_ALLOW_REPEAT)) {
                return InputAction::TOGGLE_BORDER;
            }
            if (!Options::kiosk.enabled) {
                if (Input::get()->checkAction(InputAction::TOGGLE_FULLSCREEN, Input::DO_NOT_ALLOW_REPEAT)) {
                    return InputAction::TOGGLE_FULLSCREEN;
                }
                if (Input::get()->checkAction(InputAction::WINDOW_DEC_ZOOM, Input::DO_NOT_ALLOW_REPEAT)) {
                    return InputAction::WINDOW_DEC_ZOOM;
                }
                if (Input::get()->checkAction(InputAction::WINDOW_INC_ZOOM, Input::DO_NOT_ALLOW_REPEAT)) {
                    return InputAction::WINDOW_INC_ZOOM;
                }
            }
            if (Screen::get()->isHardwareAccelerated()) {
                if (Input::get()->checkAction(InputAction::TOGGLE_SHADER, Input::DO_NOT_ALLOW_REPEAT)) {
                    if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
                        return InputAction::TOGGLE_SUPERSAMPLING;  // Ctrl+F4
                    }
                    if (Options::video.shader.enabled && ((SDL_GetModState() & SDL_KMOD_SHIFT) != 0U)) {
                        return InputAction::NEXT_SHADER_PRESET;  // Shift+F4
                    }
                    return InputAction::TOGGLE_SHADER;  // F4
                }
            }
            if (Input::get()->checkAction(InputAction::NEXT_PALETTE, Input::DO_NOT_ALLOW_REPEAT)) {
                if ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U) {
                    return InputAction::PREVIOUS_PALETTE;  // Ctrl+F5
                }
                return InputAction::NEXT_PALETTE;  // F5
            }
            if (Input::get()->checkAction(InputAction::NEXT_PALETTE_SORT, Input::DO_NOT_ALLOW_REPEAT)) {
                return InputAction::NEXT_PALETTE_SORT;  // F6
            }
            if (Input::get()->checkAction(InputAction::TOGGLE_INTEGER_SCALE, Input::DO_NOT_ALLOW_REPEAT)) {
                return InputAction::TOGGLE_INTEGER_SCALE;
            }
            if (Input::get()->checkAction(InputAction::TOGGLE_VSYNC, Input::DO_NOT_ALLOW_REPEAT)) {
                return InputAction::TOGGLE_VSYNC;
            }
            if (Input::get()->checkAction(InputAction::TOGGLE_INFO, Input::DO_NOT_ALLOW_REPEAT)) {
                return InputAction::TOGGLE_INFO;
            }
            if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT)) {
                return InputAction::TOGGLE_CONSOLE;
            }
            if (Input::get()->checkAction(InputAction::SCREENSHOT, Input::DO_NOT_ALLOW_REPEAT) &&
                ((SDL_GetModState() & SDL_KMOD_CTRL) != 0U)) {
                return InputAction::SCREENSHOT;
            }
            return InputAction::NONE;
        }

    }  // namespace

    // Funciones públicas

    // Comprueba los inputs que se pueden introducir en cualquier sección del juego
    void handle() {
        const bool CONSOLE_ACTIVE = Console::get() != nullptr && Console::get()->isActive();

        if (CONSOLE_ACTIVE) {
            // TAB/ESC cierran la consola en lugar de ejecutar sus acciones normales
            if (Input::get()->checkAction(InputAction::TOGGLE_CONSOLE, Input::DO_NOT_ALLOW_REPEAT) ||
                Input::get()->checkAction(InputAction::EXIT, Input::DO_NOT_ALLOW_REPEAT)) {
                Console::get()->toggle();
                return;
            }
        } else {
            // Salida de administrador en modo kiosko (Ctrl+Shift+Alt+Q)
            if (Options::kiosk.enabled) {
                SDL_Keymod mod = SDL_GetModState();
                const bool* ks = SDL_GetKeyboardState(nullptr);
                if (((mod & SDL_KMOD_CTRL) != 0U) && ((mod & SDL_KMOD_SHIFT) != 0U) && ((mod & SDL_KMOD_ALT) != 0U) && ks[SDL_SCANCODE_Q]) {
                    SceneManager::current = SceneManager::Scene::QUIT;
                    return;
                }
            }
        }

        // Detectar qué acción global está siendo presionada
        InputAction action = getPressedAction();

        // Con consola activa, ACCEPT (saltar sección) y EXIT están bloqueados
        if (CONSOLE_ACTIVE && (action == InputAction::ACCEPT || action == InputAction::EXIT)) {
            return;
        }

        // Ejecutar el handler correspondiente usando switch statement
        switch (action) {
            case InputAction::EXIT:
                handleQuit();
                break;

            case InputAction::ACCEPT:
                handleSkipSection();
                break;

            case InputAction::TOGGLE_BORDER:
                handleToggleBorder();
                break;

            case InputAction::TOGGLE_FULLSCREEN:
                handleToggleVideoMode();
                break;

            case InputAction::WINDOW_DEC_ZOOM:
                handleDecWindowZoom();
                break;

            case InputAction::WINDOW_INC_ZOOM:
                handleIncWindowZoom();
                break;

            case InputAction::TOGGLE_SHADER:
                handleToggleShaders();
                break;

            case InputAction::NEXT_SHADER_PRESET:
                handleNextShaderPreset();
                break;

            case InputAction::TOGGLE_SUPERSAMPLING:
                handleNextShader();
                break;

            case InputAction::NEXT_PALETTE:
                handleNextPalette();
                break;

            case InputAction::PREVIOUS_PALETTE:
                handlePreviousPalette();
                break;

            case InputAction::NEXT_PALETTE_SORT:
                handleNextPaletteSortMode();
                break;

            case InputAction::TOGGLE_INTEGER_SCALE:
                handleToggleIntegerScale();
                break;

            case InputAction::TOGGLE_VSYNC:
                handleToggleVSync();
                break;

            case InputAction::TOGGLE_CONSOLE:
                if (Console::get() != nullptr) { Console::get()->toggle(); }
                break;

            case InputAction::SCREENSHOT:
                handleScreenshot();
                break;

            case InputAction::TOGGLE_INFO:
                if (RenderInfo::get() != nullptr) {
                    // Leemos la intención antes del toggle: isActive() incluye la
                    // animación VANISHING, así que justo después de un toggle ACTIVE→OFF
                    // seguiría devolviendo true y la persistencia no detectaría el cambio.
                    const bool WAS_ACTIVE = RenderInfo::get()->isActive();
                    RenderInfo::get()->toggle();
#ifdef _DEBUG
                    if (Debug::get() != nullptr) { Debug::get()->setRenderInfoEnabled(!WAS_ACTIVE); }
#endif
                }
                break;

            case InputAction::NONE:
            default:
                // No se presionó ninguna acción global
                break;
        }
    }
}  // namespace GlobalInputs