#include "core/input/define_buttons.hpp"

#include <algorithm>  // Para __all_of_fn, all_of
#include <memory>     // Para unique_ptr, allocator, shared_ptr, operator==, make_unique

#include "core/input/input.hpp"         // Para Input
#include "core/input/input_types.hpp"   // Para InputAction
#include "core/locale/lang.hpp"         // Para getText
#include "core/resources/resource.hpp"  // Para Resource
#include "game/options.hpp"             // Para Gamepad
#include "game/ui/window_message.hpp"   // Para WindowMessage
#include "utils/param.hpp"              // Para Param, param, ParamGame, ParamServiceMenu
#include "utils/utils.hpp"              // Para Zone

DefineButtons::DefineButtons()
    : input_(Input::get()) {
    clearButtons();

    auto gamepads = input_->getGamepads();
    for (const auto& gamepad : gamepads) {
        controller_names_.emplace_back(Input::getControllerName(gamepad));
    }

    // Crear la ventana de mensaje
    WindowMessage::Config config(param.service_menu.window_message);
    auto text_renderer = Resource::get()->getText("04b_25_flat");
    window_message_ = std::make_unique<WindowMessage>(
        text_renderer,
        Lang::getText("[DEFINE_BUTTONS] TITLE"),
        config);
    window_message_->setPosition(param.game.game_area.center_x, param.game.game_area.center_y, WindowMessage::PositionMode::CENTERED);
}

void DefineButtons::render() {
    if (enabled_ && window_message_) {
        window_message_->render();
    }
}

void DefineButtons::update(float delta_time) {
    if (!enabled_) {
        return;
    }

    // Actualizar la ventana siempre
    if (window_message_) {
        window_message_->update(delta_time);
    }

    // Manejar la secuencia de cierre si ya terminamos
    if (finished_ && message_shown_) {
        message_timer_ += delta_time;

        // Después del delay, iniciar animación de cierre (solo una vez)
        if (message_timer_ >= MESSAGE_DISPLAY_DURATION_S && !closing_) {
            if (window_message_) {
                window_message_->hide();  // Iniciar animación de cierre
            }
            closing_ = true;
        }
    }
}

void DefineButtons::handleEvents(const SDL_Event& event) {
    if (enabled_) {
        switch (event.type) {
            case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
                doControllerButtonDown(event.gbutton);
                break;
            case SDL_EVENT_GAMEPAD_BUTTON_UP:
                checkEnd();
                break;
            case SDL_EVENT_GAMEPAD_AXIS_MOTION:
                doControllerAxisMotion(event.gaxis);
                break;
            default:
                break;
        }
    }
}

auto DefineButtons::enable(Options::Gamepad* options_gamepad) -> bool {
    if (options_gamepad != nullptr) {
        options_gamepad_ = options_gamepad;
        enabled_ = true;
        finished_ = false;
        index_button_ = 0;
        message_shown_ = false;
        closing_ = false;
        l2_was_pressed_ = false;
        r2_was_pressed_ = false;
        clearButtons();
        updateWindowMessage();

        if (window_message_) {
            window_message_->autoSize();
            window_message_->show();
        }

        return true;
    }
    return false;
}

void DefineButtons::disable() {
    enabled_ = false;
    finished_ = false;
    message_shown_ = false;
    closing_ = false;
    l2_was_pressed_ = false;
    r2_was_pressed_ = false;

    if (window_message_) {
        window_message_->hide();
    }
}

void DefineButtons::doControllerButtonDown(const SDL_GamepadButtonEvent& event) {
    auto gamepad = input_->getGamepad(event.which);

    if (!gamepad || gamepad != options_gamepad_->instance) {
        return;
    }

    const auto BUTTON = static_cast<SDL_GamepadButton>(event.button);
    if (checkButtonNotInUse(BUTTON)) {
        buttons_.at(index_button_).button = static_cast<int>(BUTTON);
        incIndexButton();
        updateWindowMessage();
    }
}

void DefineButtons::doControllerAxisMotion(const SDL_GamepadAxisEvent& event) {
    auto gamepad = input_->getGamepad(event.which);

    if (!gamepad || gamepad != options_gamepad_->instance) {
        return;
    }

    // Solo manejamos L2 y R2 como botones con lógica de transición
    if (event.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER) {
        bool l2_is_pressed_now = event.value > 16384;

        // Solo actuar en la transición de no presionado a presionado
        if (l2_is_pressed_now && !l2_was_pressed_) {
            const auto TRIGGER_BUTTON = Input::TRIGGER_L2_AS_BUTTON;
            if (checkTriggerNotInUse(TRIGGER_BUTTON)) {
                buttons_.at(index_button_).button = TRIGGER_BUTTON;
                incIndexButton();
                updateWindowMessage();
            }
        }

        // Detectar liberación del trigger para llamar checkEnd()
        if (!l2_is_pressed_now && l2_was_pressed_) {
            checkEnd();
        }

        l2_was_pressed_ = l2_is_pressed_now;

    } else if (event.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER) {
        bool r2_is_pressed_now = event.value > 16384;

        // Solo actuar en la transición de no presionado a presionado
        if (r2_is_pressed_now && !r2_was_pressed_) {
            const auto TRIGGER_BUTTON = Input::TRIGGER_R2_AS_BUTTON;
            if (checkTriggerNotInUse(TRIGGER_BUTTON)) {
                buttons_.at(index_button_).button = TRIGGER_BUTTON;
                incIndexButton();
                updateWindowMessage();
            }
        }

        // Detectar liberación del trigger para llamar checkEnd()
        if (!r2_is_pressed_now && r2_was_pressed_) {
            checkEnd();
        }

        r2_was_pressed_ = r2_is_pressed_now;
    }
}

void DefineButtons::bindButtons(Options::Gamepad* options_gamepad) {
    for (const auto& button : buttons_) {
        Input::bindGameControllerButton(options_gamepad->instance, button.action, static_cast<SDL_GamepadButton>(button.button));
    }

    Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_SELECT, Input::Action::FIRE_LEFT);
    Input::bindGameControllerButton(options_gamepad->instance, Input::Action::SM_BACK, Input::Action::FIRE_CENTER);
}

void DefineButtons::incIndexButton() {
    if (index_button_ < buttons_.size() - 1) {
        ++index_button_;
    } else {
        finished_ = true;
    }
}

auto DefineButtons::checkButtonNotInUse(SDL_GamepadButton button) -> bool {
    return std::ranges::all_of(buttons_, [button](const auto& b) -> auto {
        return b.button != button;
    });
}

auto DefineButtons::checkTriggerNotInUse(int trigger_button) -> bool {
    return std::ranges::all_of(buttons_, [trigger_button](const auto& b) -> auto {
        return b.button != trigger_button;
    });
}

void DefineButtons::clearButtons() {
    buttons_.clear();
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_LEFT"), Input::Action::FIRE_LEFT, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_UP"), Input::Action::FIRE_CENTER, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] FIRE_RIGHT"), Input::Action::FIRE_RIGHT, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] START"), Input::Action::START, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
    buttons_.emplace_back(Lang::getText("[DEFINE_BUTTONS] SERVICE_MENU"), Input::Action::SERVICE, static_cast<int>(SDL_GAMEPAD_BUTTON_INVALID));
}

void DefineButtons::checkEnd() {
    if (finished_ && !message_shown_) {
        bindButtons(options_gamepad_);
        input_->saveGamepadConfigFromGamepad(options_gamepad_->instance);
        input_->resetInputStates();

        // Mostrar mensaje de finalización
        if (window_message_) {
            window_message_->clearTexts();
            window_message_->addText(Lang::getText("[DEFINE_BUTTONS] CONFIGURATION_COMPLETE"));
        }

        // Solo marcar que ya mostramos el mensaje
        message_shown_ = true;
        message_timer_ = 0.0F;
    }
}

auto DefineButtons::isReadyToClose() const -> bool {
    // Solo está listo para cerrar si:
    // 1. Terminó
    // 2. Ya mostró el mensaje
    // 3. Está cerrando
    // 4. La ventana ya no está visible (animación terminada)
    return finished_ && message_shown_ && closing_ &&
        (!window_message_ || !window_message_->isVisible());
}

void DefineButtons::updateWindowMessage() {
    if (!window_message_ || (options_gamepad_ == nullptr)) {
        return;
    }

    // Configurar título
    std::string title = Lang::getText("[DEFINE_BUTTONS] CONFIGURING") + ": " + options_gamepad_->name;
    window_message_->setTitle(title);

    // Limpiar textos anteriores
    window_message_->clearTexts();

    if (index_button_ < buttons_.size()) {
        // Instrucción actual
        std::string instruction = Lang::getText("[DEFINE_BUTTONS] PRESS_BUTTON_FOR") + ":";
        window_message_->addText(instruction);
        window_message_->addText(buttons_.at(index_button_).label);
    }
}