#include "game/scenes/title.hpp"

#include <SDL3/SDL.h>

#include <algorithm>  // Para clamp

#include "core/audio/audio.hpp"               // Para Audio
#include "core/input/global_inputs.hpp"       // Para check
#include "core/input/input.hpp"               // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT, REP...
#include "core/locale/locale.hpp"             // Para Locale
#include "core/rendering/screen.hpp"          // Para Screen
#include "core/rendering/sprite/sprite.hpp"   // Para SSprite
#include "core/rendering/surface.hpp"         // Para Surface
#include "core/rendering/text.hpp"            // Para Text, Text::CENTER_FLAG, Text::COLOR_FLAG
#include "core/resources/resource_cache.hpp"  // Para Resource
#include "core/resources/resource_list.hpp"   // Para Asset
#include "core/system/event_buffer.hpp"       // Para EventBuffer
#include "core/system/global_events.hpp"      // Para check
#include "game/defaults.hpp"                  // Para Defaults::Music::Files
#include "game/options.hpp"                   // Para Options, options, SectionState, Section
#include "game/scene_manager.hpp"             // Para SceneManager
#include "game/ui/console.hpp"                // Para Console
#include "utils/defines.hpp"                  // Para PlayArea::CENTER_X, GameCanvas::WIDTH
#include "utils/utils.hpp"

// Constructor
Title::Title()
    : game_logo_surface_(Resource::Cache::get()->getSurface("title_logo.gif")),
      game_logo_sprite_(std::make_unique<Sprite>(game_logo_surface_, 29, 9, game_logo_surface_->getWidth(), game_logo_surface_->getHeight())),
      title_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)),
      delta_timer_(std::make_unique<DeltaTimer>()),
      menu_text_(Resource::Cache::get()->getText("gauntlet")) {
    // Inicializa arrays con valores por defecto
    temp_keys_.fill(SDL_SCANCODE_UNKNOWN);
    temp_buttons_.fill(-1);

    // Estado inicial: menú principal
    state_ = State::MAIN_MENU;

    // Establece SceneManager
    SceneManager::current = SceneManager::Scene::TITLE;
    SceneManager::options = SceneManager::Options::NONE;

    // Acciones iniciales
    Screen::get()->setBorderColor(0);                              // Cambia el color del borde
    Audio::get()->playMusic(Defaults::Music::Files::TITLE_TRACK);  // Inicia la musica
}

// Destructor
Title::~Title() {  // NOLINT(modernize-use-equals-default)
    title_surface_->resetSubPalette();
}

// Comprueba el manejador de eventos
void Title::handleEvents() {
    for (const auto& event : EventBuffer::events) {
        GlobalEvents::handle(event);

        // Manejo especial para captura de botones de gamepad
        if (is_remapping_joystick_ && !remap_completed_ &&
            (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN || event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION)) {
            handleJoystickRemap(event);
            continue;  // No procesar más este evento
        }

        if (event.type == SDL_EVENT_KEY_DOWN && !Console::get()->isActive()) {
            // Si estamos en modo remap de teclado, capturar tecla
            if (is_remapping_keyboard_ && !remap_completed_) {
                handleKeyboardRemap(event);
            }
            // Si estamos en el menú principal normal
            else if (state_ == State::MAIN_MENU && !is_remapping_keyboard_ && !is_remapping_joystick_) {
                handleMainMenuKeyPress(event.key.key);
            }
        }
    }
}

// Maneja las teclas del menu principal
void Title::handleMainMenuKeyPress(SDL_Keycode key) {
    switch (key) {
        case SDLK_1:
            // PLAY
            exit_scene_ = SceneManager::Scene::GAME;
            transitionToState(State::FADE_MENU);
            Audio::get()->fadeOutMusic(1000);
            break;

        case SDLK_2:
            // REDEFINE KEYBOARD
            is_remapping_keyboard_ = true;
            is_remapping_joystick_ = false;
            remap_step_ = 0;
            remap_completed_ = false;
            remap_error_message_.clear();
            state_time_ = 0.0F;
            break;

        case SDLK_3:
            // REDEFINE JOYSTICK (siempre visible, pero solo funciona si hay gamepad)
            if (Input::get()->gameControllerFound()) {
                is_remapping_keyboard_ = false;
                is_remapping_joystick_ = true;
                remap_step_ = 0;
                remap_completed_ = false;
                remap_error_message_.clear();
                axis_cooldown_ = 0.0F;
                state_time_ = 0.0F;
            }
            // Si no hay gamepad, simplemente no hacer nada
            break;

        default:
            break;
    }
}

// Comprueba las entradas
void Title::handleInput(float delta_time) {
    Input::get()->update();

    // Permitir cancelar remap con ESC/CANCEL
    if ((is_remapping_keyboard_ || is_remapping_joystick_) && !remap_completed_) {
        if (Input::get()->checkAction(InputAction::CANCEL, Input::DO_NOT_ALLOW_REPEAT)) {
            is_remapping_keyboard_ = false;
            is_remapping_joystick_ = false;
            remap_step_ = 0;
            remap_completed_ = false;
            remap_error_message_.clear();
        }
        // Durante el remap, no procesar otras entradas
        GlobalInputs::handle();
        return;
    }

    GlobalInputs::handle();
}

// Actualiza las variables
void Title::update() {
    const float DELTA_TIME = delta_timer_->tick();

    handleEvents();           // Comprueba los eventos
    handleInput(DELTA_TIME);  // Comprueba las entradas

    updateState(DELTA_TIME);  // Actualiza el estado actual

    Audio::update();                    // Actualiza el objeto Audio
    Screen::get()->update(DELTA_TIME);  // Actualiza el objeto Screen
}

// Actualiza el estado actual
void Title::updateState(float delta_time) {
    switch (state_) {
        case State::MAIN_MENU:
            updateMainMenu(delta_time);
            break;

        case State::FADE_MENU:
            updateFadeMenu(delta_time);
            break;

        case State::POST_FADE_MENU:
            updatePostFadeMenu(delta_time);
            break;

        default:
            break;
    }
}

// Transiciona a un nuevo estado
void Title::transitionToState(State new_state) {
    state_ = new_state;
    state_time_ = 0.0F;
    fade_accumulator_ = 0.0F;
}

// Actualiza el estado MAIN_MENU
void Title::updateMainMenu(float delta_time) {
    // Si estamos en modo remap, manejar la lógica específica
    if (is_remapping_keyboard_ || is_remapping_joystick_) {
        // Decrementar cooldown de ejes si estamos capturando botones de joystick
        if (is_remapping_joystick_ && axis_cooldown_ > 0.0F) {
            axis_cooldown_ -= delta_time;
            axis_cooldown_ = std::max(axis_cooldown_, 0.0F);
        }

        // Si el remap está completado, esperar antes de guardar
        if (remap_completed_) {
            state_time_ += delta_time;
            if (state_time_ >= KEYBOARD_REMAP_DISPLAY_DELAY) {
                if (is_remapping_keyboard_) {
                    applyKeyboardRemap();
                } else if (is_remapping_joystick_) {
                    applyJoystickRemap();
                }
                // Resetear estado de remap
                is_remapping_keyboard_ = false;
                is_remapping_joystick_ = false;
                remap_completed_ = false;
                state_time_ = 0.0F;
            }
        }
    } else {
        // Incrementa el temporizador solo en el menú principal normal
        state_time_ += delta_time;
    }
}

// Actualiza el estado FADE_MENU
void Title::updateFadeMenu(float delta_time) {
    fade_accumulator_ += delta_time;
    if (fade_accumulator_ >= FADE_STEP_INTERVAL) {
        fade_accumulator_ = 0.0F;
        if (title_surface_->fadeSubPalette()) {
            transitionToState(State::POST_FADE_MENU);
        }
    }
}

// Actualiza el estado POST_FADE_MENU
void Title::updatePostFadeMenu(float delta_time) {
    state_time_ += delta_time;
    if (state_time_ >= POST_FADE_DELAY) {
        SceneManager::current = exit_scene_;
        SceneManager::options = SceneManager::Options::NONE;
    }
}

// Dibuja en pantalla
void Title::render() {
    // Rellena la surface
    fillTitleSurface();

    // Prepara para empezar a dibujar en la textura de juego
    Screen::get()->start();
    Screen::get()->clearSurface(0);

    // Dibuja en pantalla la surface con la composicion
    title_surface_->render();

    // Vuelca el contenido del renderizador en pantalla
    Screen::get()->render();
}

// Dibuja el logo con el titulo del juego
void Title::renderGameLogo() {
    game_logo_sprite_->render();
}

// Dibuja el menu principal
void Title::renderMainMenu() {
    // Si estamos en modo remap, mostrar la pantalla correspondiente
    if (is_remapping_keyboard_) {
        renderKeyboardRemap();
        return;
    }
    if (is_remapping_joystick_) {
        renderJoystickRemap();
        return;
    }

    // Menú principal normal con 3 opciones centradas verticalmente en la zona
    const Uint8 COLOR = 8;
    const int TEXT_SIZE = menu_text_->getCharacterSize();
    const int MENU_CENTER_Y = MENU_ZONE_Y + (MENU_ZONE_HEIGHT / 2);
    const int SPACING = 2 * TEXT_SIZE;  // Espaciado entre opciones

    // Calcula posiciones centradas verticalmente (3 items con espaciado)
    const int TOTAL_HEIGHT = 2 * SPACING;  // 2 espacios entre 3 items
    const int START_Y = MENU_CENTER_Y - (TOTAL_HEIGHT / 2);

    const auto* loc = Locale::get();
    menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.menu.play"), 1, COLOR);
    menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + SPACING, loc->get("title.menu.keyboard"), 1, COLOR);
    menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + (2 * SPACING), loc->get("title.menu.joystick"), 1, COLOR);
}

// Dibuja los elementos en la surface
void Title::fillTitleSurface() {
    // Renderiza sobre la textura
    auto previuos_renderer = Screen::get()->getRendererSurface();
    Screen::get()->setRendererSurface(title_surface_);

    // Rellena la textura de color
    title_surface_->clear(0);

    switch (state_) {
        case State::MAIN_MENU:
        case State::FADE_MENU:
            renderGameLogo();
            renderMainMenu();

            break;

        default:
            break;
    }

    // Deja el renderizador como estaba
    Screen::get()->setRendererSurface(previuos_renderer);
}

// Maneja la captura de teclas para redefinir el teclado
void Title::handleKeyboardRemap(const SDL_Event& event) {
    SDL_Scancode scancode = event.key.scancode;

    // Valida la tecla
    if (!isKeyValid(scancode)) {
        remap_error_message_ = Locale::get()->get("title.keys.invalid");
        return;
    }

    // Verifica duplicados
    if (isKeyDuplicate(scancode, remap_step_)) {
        remap_error_message_ = Locale::get()->get("title.keys.already_used");
        return;
    }

    // Tecla valida, guardar
    temp_keys_[remap_step_] = scancode;
    remap_error_message_.clear();
    remap_step_++;

    // Si completamos los 3 pasos, mostrar resultado y esperar
    if (remap_step_ >= 3) {
        remap_completed_ = true;
        state_time_ = 0.0F;  // Resetear el timer para el delay de 1 segundo
    }
}

// Valida si una tecla es permitida
auto Title::isKeyValid(SDL_Scancode scancode) -> bool {
    // Prohibir ESC (reservado para cancelar)
    if (scancode == SDL_SCANCODE_ESCAPE) {
        return false;
    }

    // Prohibir teclas F1-F12 (reservadas para funciones del sistema)
    if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) {
        return false;
    }

    // Prohibir Enter/Return (reservado para confirmaciones)
    if (scancode == SDL_SCANCODE_RETURN || scancode == SDL_SCANCODE_RETURN2) {
        return false;
    }

    return true;
}

// Verifica si una tecla ya fue usada en pasos anteriores
auto Title::isKeyDuplicate(SDL_Scancode scancode, int current_step) -> bool {
    for (int i = 0; i < current_step; i++) {
        if (temp_keys_[i] == scancode) {
            return true;
        }
    }
    return false;
}

// Aplica y guarda las teclas redefinidas
void Title::applyKeyboardRemap() {
    // Guardar las nuevas teclas en Options::controls
    Options::keyboard_controls.key_left = temp_keys_[0];
    Options::keyboard_controls.key_right = temp_keys_[1];
    Options::keyboard_controls.key_jump = temp_keys_[2];

    // Aplicar los bindings al sistema de Input
    Input::get()->applyKeyboardBindingsFromOptions();

    // Guardar a archivo de configuracion
    Options::saveToFile();
}

// Dibuja la pantalla de redefinir teclado
void Title::renderKeyboardRemap() const {
    // Zona central del menu
    const Uint8 COLOR = 8;
    const Uint8 ERROR_COLOR = 4;
    const int TEXT_SIZE = menu_text_->getCharacterSize();
    const int MENU_CENTER_Y = MENU_ZONE_Y + (MENU_ZONE_HEIGHT / 2);

    // Calcula posiciones centradas verticalmente
    // Layout: Mensaje principal, espacio, 3 teclas (LEFT/RIGHT/JUMP), espacio, mensaje de error
    const int LINE_SPACING = TEXT_SIZE;
    const int START_Y = MENU_CENTER_Y - (2 * TEXT_SIZE);  // Centrado aproximado

    // Mensaje principal: "PRESS KEY FOR [ACTION]" o "KEYS DEFINED" si completado
    const auto* loc = Locale::get();
    if (remap_step_ >= 3) {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.keys.defined"), 1, COLOR);
    } else {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.keys.prompt" + std::to_string(remap_step_)), 1, COLOR);
    }

    // Mostrar teclas ya capturadas (con espaciado de 2 líneas desde el mensaje principal)
    const int KEYS_START_Y = START_Y + (2 * LINE_SPACING);
    if (remap_step_ > 0) {
        const std::string LEFT_KEY = SDL_GetScancodeName(temp_keys_[0]);
        const std::string LEFT_MSG = loc->get("title.keys.label0") + LEFT_KEY;
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y, LEFT_MSG, 1, COLOR);
    }
    if (remap_step_ > 1) {
        const std::string RIGHT_KEY = SDL_GetScancodeName(temp_keys_[1]);
        const std::string RIGHT_MSG = loc->get("title.keys.label1") + RIGHT_KEY;
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y + LINE_SPACING, RIGHT_MSG, 1, COLOR);
    }
    if (remap_step_ >= 3) {
        const std::string JUMP_KEY = SDL_GetScancodeName(temp_keys_[2]);
        const std::string JUMP_MSG = loc->get("title.keys.label2") + JUMP_KEY;
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y + (2 * LINE_SPACING), JUMP_MSG, 1, COLOR);
    }

    // Mensaje de error si existe (4 líneas después del inicio de las teclas)
    if (!remap_error_message_.empty()) {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y + (4 * LINE_SPACING), remap_error_message_, 1, ERROR_COLOR);
    }
}

// Dibuja la pantalla de redefinir joystick
void Title::renderJoystickRemap() const {
    const Uint8 COLOR = 8;
    const Uint8 ERROR_COLOR = 4;
    const int TEXT_SIZE = menu_text_->getCharacterSize();
    const int MENU_CENTER_Y = MENU_ZONE_Y + (MENU_ZONE_HEIGHT / 2);

    // Calcula posiciones centradas verticalmente
    // Layout: Mensaje principal, espacio, 3 botones (LEFT/RIGHT/JUMP), espacio, mensaje de error
    const int LINE_SPACING = TEXT_SIZE;
    const int START_Y = MENU_CENTER_Y - (2 * TEXT_SIZE);  // Centrado aproximado

    // Mensaje principal: "PRESS BUTTON FOR [ACTION]" o "BUTTONS DEFINED" si completado
    const auto* loc = Locale::get();
    if (remap_step_ >= 3) {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.buttons.defined"), 1, COLOR);
    } else {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.buttons.prompt" + std::to_string(remap_step_)), 1, COLOR);
    }

    // Mostrar botones ya capturados (con espaciado de 2 líneas desde el mensaje principal)
    const int BUTTONS_START_Y = START_Y + (2 * LINE_SPACING);
    if (remap_step_ > 0) {
        const std::string LEFT_BTN = getButtonName(temp_buttons_[0]);
        const std::string LEFT_MSG = loc->get("title.keys.label0") + LEFT_BTN;
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y, LEFT_MSG, 1, COLOR);
    }
    if (remap_step_ > 1) {
        const std::string RIGHT_BTN = getButtonName(temp_buttons_[1]);
        const std::string RIGHT_MSG = loc->get("title.keys.label1") + RIGHT_BTN;
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y + LINE_SPACING, RIGHT_MSG, 1, COLOR);
    }
    if (remap_step_ >= 3) {
        const std::string JUMP_BTN = getButtonName(temp_buttons_[2]);
        const std::string JUMP_MSG = loc->get("title.keys.label2") + JUMP_BTN;
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y + (2 * LINE_SPACING), JUMP_MSG, 1, COLOR);
    }

    // Mensaje de error si existe (4 líneas después del inicio de los botones)
    if (!remap_error_message_.empty()) {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y + (4 * LINE_SPACING), remap_error_message_, 1, ERROR_COLOR);
    }
}

// Maneja la captura de botones del gamepad para redefinir
void Title::handleJoystickRemap(const SDL_Event& event) {
    int captured_button = -1;

    // Capturar botones del gamepad
    if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
        captured_button = static_cast<int>(event.gbutton.button);
    }
    // Capturar triggers y ejes analógicos
    else if (event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
        // Si el cooldown está activo, ignorar eventos de ejes (evita múltiples capturas)
        if (axis_cooldown_ > 0.0F) {
            return;
        }

        // Capturar triggers como botones (usando valores especiales 100/101)
        if (event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER && event.gaxis.value > Defaults::Controls::JOYSTICK_AXIS_THRESHOLD) {
            captured_button = Input::TRIGGER_L2_AS_BUTTON;  // 100
            axis_cooldown_ = 0.5F;                          // Cooldown de medio segundo
        } else if (event.gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER && event.gaxis.value > Defaults::Controls::JOYSTICK_AXIS_THRESHOLD) {
            captured_button = Input::TRIGGER_R2_AS_BUTTON;  // 101
            axis_cooldown_ = 0.5F;
        }
        // Capturar ejes del stick analógico (usando valores especiales 200+)
        else if (event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX) {
            if (event.gaxis.value < -Defaults::Controls::JOYSTICK_AXIS_THRESHOLD) {
                captured_button = 200;  // Left stick izquierda
                axis_cooldown_ = 0.5F;
            } else if (event.gaxis.value > Defaults::Controls::JOYSTICK_AXIS_THRESHOLD) {
                captured_button = 201;  // Left stick derecha
                axis_cooldown_ = 0.5F;
            }
        }
    }

    // Si no se capturó ningún input válido, salir
    if (captured_button == -1) {
        return;
    }

    // Verifica duplicados
    if (isButtonDuplicate(captured_button, remap_step_)) {
        remap_error_message_ = Locale::get()->get("title.buttons.already_used");
        return;
    }

    // Botón válido, guardar
    temp_buttons_[remap_step_] = captured_button;
    remap_error_message_.clear();
    remap_step_++;

    // Si completamos los 3 pasos, mostrar resultado y esperar
    if (remap_step_ >= 3) {
        remap_completed_ = true;
        state_time_ = 0.0F;  // Resetear el timer para el delay
    }
}

// Valida si un botón está duplicado
auto Title::isButtonDuplicate(int button, int current_step) -> bool {
    for (int i = 0; i < current_step; ++i) {
        if (temp_buttons_[i] == button) {
            return true;
        }
    }
    return false;
}

// Aplica y guarda los botones del gamepad redefinidos
void Title::applyJoystickRemap() {
    // Guardar los nuevos botones en Options::gamepad_controls
    Options::gamepad_controls.button_left = temp_buttons_[0];
    Options::gamepad_controls.button_right = temp_buttons_[1];
    Options::gamepad_controls.button_jump = temp_buttons_[2];

    // Aplicar los bindings al sistema de Input
    Input::get()->applyGamepadBindingsFromOptions();

    // Guardar a archivo de configuracion
    Options::saveToFile();
}

// Retorna el nombre amigable del botón del gamepad
auto Title::getButtonName(int button) -> std::string {
    // Triggers especiales
    if (button == Input::TRIGGER_L2_AS_BUTTON) {
        return "L2";
    }
    if (button == Input::TRIGGER_R2_AS_BUTTON) {
        return "R2";
    }

    // Ejes del stick analógico
    if (button == 200) {
        return "LEFT STICK LEFT";
    }
    if (button == 201) {
        return "LEFT STICK RIGHT";
    }

    // Botones estándar SDL
    const auto GAMEPAD_BUTTON = static_cast<SDL_GamepadButton>(button);
    const char* button_name = SDL_GetGamepadStringForButton(GAMEPAD_BUTTON);
    return (button_name != nullptr) ? std::string(button_name) : "UNKNOWN";
}