#include "game/scenes/game_over.hpp"

#include <SDL3/SDL.h>

#include <algorithm>  // Para min, max
#include <string>     // Para basic_string, operator+, to_string

#include "core/audio/audio.hpp"                       // Para Audio
#include "core/input/global_inputs.hpp"               // Para check
#include "core/input/input.hpp"                       // Para Input
#include "core/locale/locale.hpp"                     // Para Locale
#include "core/rendering/screen.hpp"                  // Para Screen
#include "core/rendering/sprite/animated_sprite.hpp"  // Para SAnimatedSprite
#include "core/rendering/text.hpp"                    // Para Text::CENTER_FLAG, Text::COLOR_FLAG, Text
#include "core/resources/resource_cache.hpp"          // Para Resource
#include "core/system/global_events.hpp"              // Para check
#include "game/options.hpp"                           // Para Options, options, OptionsStats, Secti...
#include "game/scene_manager.hpp"                     // Para SceneManager
#include "utils/defines.hpp"                          // Para GameCanvas::CENTER_X
#include "utils/delta_timer.hpp"                      // Para DeltaTimer
#include "utils/utils.hpp"                            // Para PaletteColor, stringToColor

// Constructor
GameOver::GameOver()
    : player_sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData("player_game_over.yaml"))),
      tv_sprite_(std::make_shared<AnimatedSprite>(Resource::Cache::get()->getAnimationData("tv.yaml"))),
      delta_timer_(std::make_shared<DeltaTimer>()) {
    SceneManager::current = SceneManager::Scene::GAME_OVER;
    SceneManager::options = SceneManager::Options::NONE;

    // Inicializa las posiciones de los sprites usando las constantes
    player_sprite_->setPosX(GameCanvas::CENTER_X + PLAYER_X_OFFSET);
    player_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET);
    tv_sprite_->setPosX(GameCanvas::CENTER_X - tv_sprite_->getWidth() - TV_X_OFFSET);
    tv_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET);

    Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));

    // Inicializa el vector de colores (de brillante a oscuro para fade)
    const std::vector<std::string> COLORS = {"white", "yellow", "cyan", "green", "magenta", "red", "blue", "black"};
    colors_.reserve(COLORS.size());
    std::ranges::transform(COLORS, std::back_inserter(colors_), [](const auto& color) { return stringToColor(color); });
    color_ = colors_.back();  // Empieza en black
}

// Actualiza el objeto
void GameOver::update() {
    const float DELTA_TIME = delta_timer_->tick();
    elapsed_time_ += DELTA_TIME;

    handleInput();  // Comprueba las entradas

    updateState();                       // Actualiza el estado de la escena
    updateColor();                       // Actualiza el color usado para renderizar los textos e imagenes
    player_sprite_->update(DELTA_TIME);  // Actualiza el sprite
    tv_sprite_->update(DELTA_TIME);      // Actualiza el sprite

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

// Dibuja el final en pantalla
void GameOver::render() {
    Screen::get()->start();
    Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));

    auto text = Resource::Cache::get()->getText("smb2");

    // Escribe el texto de GAME OVER
    const auto* loc = Locale::get();
    text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y, loc->get("game_over.title"), 1, color_);  // NOLINT(readability-static-accessed-through-instance)

    // Dibuja los sprites (ya posicionados en el constructor, solo ajustamos Y)
    player_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET);
    tv_sprite_->setPosY(TEXT_Y + SPRITE_Y_OFFSET);
    renderSprites();

    // Escribe el texto con las habitaciones y los items
    const std::string ITEMS_TEXT = std::to_string(Options::stats.items / 100) + std::to_string((Options::stats.items % 100) / 10) + std::to_string(Options::stats.items % 10);
    const std::string ROOMS_TEXT = std::to_string(Options::stats.rooms / 100) + std::to_string((Options::stats.rooms % 100) / 10) + std::to_string(Options::stats.rooms % 10);
    text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ITEMS_Y_OFFSET, loc->get("game_over.items") + ITEMS_TEXT, 1, color_);  // NOLINT(readability-static-accessed-through-instance)
    text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + ROOMS_Y_OFFSET, loc->get("game_over.rooms") + ROOMS_TEXT, 1, color_);  // NOLINT(readability-static-accessed-through-instance)

    // Escribe el texto con "Tu peor pesadilla"
    text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + NIGHTMARE_TITLE_Y_OFFSET, loc->get("game_over.worst_nightmare"), 1, color_);  // NOLINT(readability-static-accessed-through-instance)
    text->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, GameCanvas::CENTER_X, TEXT_Y + NIGHTMARE_TEXT_Y_OFFSET, Options::stats.worst_nightmare, 1, color_);

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

// Despatx d'un event (SDL3 Callback API)
void GameOver::handleEvent(const SDL_Event& event) {
    GlobalEvents::handle(event);
}

// Comprueba las entradas
void GameOver::handleInput() {
    Input::get()->update();
    GlobalInputs::handle();
}

// Un frame de l'escena (SDL3 Callback API)
void GameOver::iterate() {
    update();
    render();
}

// Actualiza el color usado para renderizar los textos e imagenes
void GameOver::updateColor() {
    // Calcula el color basado en el estado actual
    switch (state_) {
        case State::WAITING:
            // Durante la espera, mantener en black
            color_ = colors_.back();  // black
            break;

        case State::FADE_IN: {
            // Fade in: de black (último color) a white (primer color)
            // Progreso: 0.0 (black) -> 1.0 (white)
            const float PROGRESS = std::min(elapsed_time_ / FADE_IN_DURATION, 1.0F);
            const int INDEX = (colors_.size() - 1) - static_cast<int>((colors_.size() - 1) * PROGRESS);
            color_ = colors_[std::clamp(INDEX, 0, static_cast<int>(colors_.size() - 1))];
            break;
        }

        case State::DISPLAY:
            // Durante display, mantener el color más brillante
            color_ = colors_[0];  // white
            break;

        case State::FADE_OUT: {
            // Fade out: de white (primer color) a black (último color)
            // Progreso: 0.0 (white) -> 1.0 (black)
            const float PROGRESS = std::min(elapsed_time_ / FADE_OUT_DURATION, 1.0F);
            const int INDEX = static_cast<int>((colors_.size() - 1) * PROGRESS);
            color_ = colors_[std::clamp(INDEX, 0, static_cast<int>(colors_.size() - 1))];
            break;
        }

        case State::ENDING:
        case State::TRANSITION:
            // Al final, mantener en black
            color_ = colors_.back();  // black
            break;
    }
}

// Dibuja los sprites
void GameOver::renderSprites() {
    player_sprite_->render(1, color_);
    tv_sprite_->render(1, color_);
}

// Actualiza el estado de la escena y gestiona transiciones
void GameOver::updateState() {
    // Máquina de estados basada en tiempo transcurrido
    switch (state_) {
        case State::WAITING:
            // Espera inicial antes de empezar
            if (elapsed_time_ >= WAITING_DURATION) {
                state_ = State::FADE_IN;
                elapsed_time_ = 0.0F;
                // Hace sonar la música cuando termina la espera
                Audio::get()->playMusic("game_over.ogg", 0);
            }
            break;

        case State::FADE_IN:
            // Fade in de colores desde black
            if (elapsed_time_ >= FADE_IN_DURATION) {
                state_ = State::DISPLAY;
                elapsed_time_ = 0.0F;
            }
            break;

        case State::DISPLAY:
            // Mostrando contenido con color brillante
            if (elapsed_time_ >= DISPLAY_DURATION) {
                state_ = State::FADE_OUT;
                elapsed_time_ = 0.0F;
            }
            break;

        case State::FADE_OUT:
            // Fade out hacia black
            if (elapsed_time_ >= FADE_OUT_DURATION) {
                state_ = State::ENDING;
                elapsed_time_ = 0.0F;
            }
            break;

        case State::ENDING:
            // Pantalla en negro antes de salir
            if (elapsed_time_ >= ENDING_DURATION) {
                state_ = State::TRANSITION;
                elapsed_time_ = 0.0F;
            }
            break;

        case State::TRANSITION:
            // Transición a la escena de logo
            SceneManager::current = SceneManager::Scene::LOGO;
            SceneManager::options = SceneManager::Options::LOGO_TO_TITLE;
            break;
    }
}