#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/input/global_inputs.hpp"                // Para check
#include "core/rendering/screen.hpp"                   // Para Screen
#include "core/rendering/surface_animated_sprite.hpp"  // Para SAnimatedSprite
#include "core/rendering/text.hpp"                     // Para TEXT_CENTER, TEXT_COLOR, Text
#include "core/resources/resource.hpp"                 // Para Resource
#include "core/system/global_events.hpp"               // Para check
#include "external/jail_audio.h"                       // Para JA_PlayMusic
#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<SurfaceAnimatedSprite>(Resource::get()->getSurface("player_game_over.gif"), Resource::get()->getAnimations("player_game_over.ani"))),
      tv_sprite_(std::make_shared<SurfaceAnimatedSprite>(Resource::get()->getSurface("tv.gif"), Resource::get()->getAnimations("tv.ani"))),
      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"};
    for (const auto& color : COLORS) {
        colors_.push_back(stringToColor(color));
    }
    color_ = colors_.back();  // Empieza en black
}

// Actualiza el objeto
void GameOver::update() {
    // Obtiene el delta time desde el último frame
    const float delta = delta_timer_->tick();
    elapsed_time_ += delta;

    // Comprueba las entradas
    checkInput();

    // Actualiza el estado de la escena
    updateState();

    // Actualiza el color usado para renderizar los textos e imagenes
    updateColor();

    // Actualiza los dos sprites con delta time
    player_sprite_->update(delta);
    tv_sprite_->update(delta);

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

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

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

    // Escribe el texto de GAME OVER
    text->writeDX(TEXT_CENTER | TEXT_COLOR, GAMECANVAS_CENTER_X, TEXT_Y, "G A M E   O V E R", 1, color_);

    // 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 | TEXT_COLOR, GAMECANVAS_CENTER_X, TEXT_Y + ITEMS_Y_OFFSET, "ITEMS: " + ITEMS_TEXT, 1, color_);
    text->writeDX(TEXT_CENTER | TEXT_COLOR, GAMECANVAS_CENTER_X, TEXT_Y + ROOMS_Y_OFFSET, "ROOMS: " + ROOMS_TEXT, 1, color_);

    // Escribe el texto con "Tu peor pesadilla"
    text->writeDX(TEXT_CENTER | TEXT_COLOR, GAMECANVAS_CENTER_X, TEXT_Y + NIGHTMARE_TITLE_Y_OFFSET, "YOUR WORST NIGHTMARE IS", 1, color_);
    text->writeDX(TEXT_CENTER | TEXT_COLOR, 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();
}

// Comprueba el manejador de eventos
void GameOver::checkEvents() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        GlobalEvents::check(event);
    }
}

// Comprueba las entradas
void GameOver::checkInput() {
    GlobalInputs::check();
}

// Bucle principal
void GameOver::run() {
    while (SceneManager::current == SceneManager::Scene::GAME_OVER) {
        update();
        checkEvents();
        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
                JA_PlayMusic(Resource::get()->getMusic("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;
    }
}