#pragma once

#include <SDL3/SDL.h>

#include <cstdint>           // Para uint8_t
#include <initializer_list>  // Para initializer_list
#include <memory>            // Para shared_ptr
#include <string>            // Para string
#include <vector>            // Para vector

#include "game/entities/player.hpp"  // Para PlayerSpawn
#include "game/scenes/scene.hpp"     // Para Scene
#include "utils/delta_timer.hpp"     // Para DeltaTimer
class Room;                          // lines 12-12
class RoomTracker;                   // lines 13-13
class Scoreboard;                    // lines 14-14
class Stats;                         // lines 15-15
class Surface;

class Game : public Scene {
   public:
    // --- Estructuras ---
    enum class Mode : std::uint8_t {
        DEMO,
        GAME
    };

    enum class State : std::uint8_t {
        PLAYING,           // Normal gameplay
        BLACK_SCREEN,      // Black screen after death (0.30s)
        GAME_OVER,         // Intermediate state before changing scene
        FADE_TO_ENDING,    // Fade out transition
        POST_FADE_ENDING,  // Black screen delay before ending
    };

    // --- Constructor y Destructor ---
    explicit Game(Mode mode);
    ~Game() override;

    // --- Bucle para el juego (SDL3 Callback API) ---
    void iterate() override;
    void handleEvent(const SDL_Event& event) override;

   private:
    // --- Constantes de tiempo ---
    static constexpr float BLACK_SCREEN_DURATION = 0.30F;  // Duración de la pantalla negra en segundos (20 frames a 66.67fps)
    static constexpr float GAME_OVER_THRESHOLD = 0.255F;   // Tiempo antes del game over en segundos (17 frames a 66.67fps)
    static constexpr float DEMO_ROOM_DURATION = 6.0F;      // Duración de cada habitación en modo demo en segundos (400 frames)
    static constexpr float JAIL_RESTORE_INTERVAL = 1.5F;   // Intervalo de restauración de vidas en la Jail en segundos (100 frames)
    static constexpr float FADE_STEP_INTERVAL = 0.05F;     // Intervalo entre pasos de fade en segundos
    static constexpr float POST_FADE_DELAY = 2.0F;         // Duración de la pantalla negra después del fade

    // --- Estructuras ---
    struct DemoData {
        float time_accumulator{0.0F};    // Acumulador de tiempo para el modo demo
        int room_index{0};               // Índice para el vector de habitaciones
        std::vector<std::string> rooms;  // Listado con los mapas de la demo
    };

    // --- Métodos ---
    void update();                                                                      // Actualiza el juego, las variables, comprueba la entrada, etc.
    void render();                                                                      // Pinta los objetos en pantalla
    void renderRoomName();                                                              // Escribe el nombre de la pantalla
    void transitionToState(State new_state);                                            // Cambia al estado especificado y resetea los timers
    void updatePlaying(float delta_time);                                               // Actualiza el juego en estado PLAYING
    void updateBlackScreen(float delta_time);                                           // Actualiza el juego en estado BLACK_SCREEN
    void updateGameOver(float delta_time);                                              // Actualiza el juego en estado GAME_OVER
    void updateFadeToEnding(float delta_time);                                          // Actualiza el juego en estado FADE_TO_ENDING
    void updatePostFadeEnding(float delta_time);                                        // Actualiza el juego en estado POST_FADE_ENDING
    void renderPlaying();                                                               // Renderiza el juego en estado PLAYING (directo a pantalla)
    static void renderBlackScreen();                                                    // Renderiza el juego en estado BLACK_SCREEN (pantalla negra)
    static void renderGameOver();                                                       // Renderiza el juego en estado GAME_OVER (pantalla negra)
    void renderFadeToEnding();                                                          // Renderiza el juego en estado FADE_TO_ENDING (via backbuffer)
    static void renderPostFadeEnding();                                                 // Renderiza el juego en estado POST_FADE_ENDING (pantalla negra)
    auto changeRoom(const std::string& room_path) -> bool;                              // Cambia de habitación
    void handleInput();                                                                 // Comprueba el teclado
    void checkPlayerIsOnBorder();                                                       // Comprueba si el jugador esta en el borde de la pantalla y actua
    auto checkPlayerAndEnemies() -> bool;                                               // Comprueba las colisiones del jugador con los enemigos
    void checkPlayerAndItems();                                                         // Comprueba las colisiones del jugador con los objetos
    void checkIfPlayerIsAlive();                                                        // Comprueba si el jugador esta vivo
    void killPlayer();                                                                  // Mata al jugador
    void setScoreBoardColor();                                                          // Pone el color del marcador en función del color del borde de la habitación
    auto checkEndGame() -> bool;                                                        // Comprueba si ha finalizado el juego
    static auto getTotalItems() -> int;                                                 // Obtiene la cantidad total de items que hay en el mapeado del juego
    void togglePause();                                                                 // Pone el juego en pausa
    void checkRestoringJail(float delta_time);                                          // Da vidas al jugador cuando está en la Jail
    void initStats();                                                                   // Inicializa el diccionario de las estadísticas
    void fillRoomNameTexture();                                                         // Pone el nombre de la habitación en la textura
    void checkSomeCheevos();                                                            // Comprueba algunos logros
    void checkEndGameCheevos();                                                         // Comprueba los logros de completar el juego
    void initPlayer(const Player::SpawnData& spawn_point, std::shared_ptr<Room> room);  // Inicializa al jugador
    void createRoomNameTexture();                                                       // Crea la textura para poner el nombre de la habitación
    void keepMusicPlaying();                                                            // Hace sonar la música
    void demoInit();                                                                    // DEMO MODE: Inicializa las variables para el modo demo
    void demoCheckRoomChange(float delta_time);                                         // DEMO MODE: Comprueba si se ha de cambiar de habitación
#ifdef _DEBUG
    static void renderDebugInfo();                   // Pone la información de debug en pantalla
    void handleDebugEvents(const SDL_Event& event);  // Comprueba los eventos
    void handleDebugMouseDrag(float delta_time);     // Maneja el arrastre del jugador con el ratón (debug)
#endif

    // --- Variables miembro ---
    // Objetos y punteros a recursos
    std::shared_ptr<Scoreboard::Data> scoreboard_data_;  // Estructura con los datos del marcador
    std::shared_ptr<Scoreboard> scoreboard_;             // Objeto encargado de gestionar el marcador
    std::shared_ptr<RoomTracker> room_tracker_;          // Lleva el control de las habitaciones visitadas
    std::shared_ptr<Room> room_;                         // Objeto encargado de gestionar cada habitación del juego
    std::shared_ptr<Player> player_;                     // Objeto con el jugador
    std::shared_ptr<Stats> stats_;                       // Objeto encargado de gestionar las estadísticas
    std::shared_ptr<Surface> room_name_surface_;         // Textura para escribir el nombre de la habitación
    std::shared_ptr<Surface> game_backbuffer_surface_;   // Backbuffer para efectos de fade

    // Variables de estado del juego
    Mode mode_;                     // Modo del juego
    State state_{State::PLAYING};   // Estado actual de la escena
    DeltaTimer delta_timer_;        // Timer para calcular delta time
    std::string current_room_;      // Fichero de la habitación actual
    Player::SpawnData spawn_data_;  // Lugar de la habitación donde aparece el jugador
    int total_items_;               // Cantidad total de items que hay en el mapeado del juego
    bool paused_{false};            // Indica si el juego se encuentra en pausa
    float state_time_{0.0F};        // Tiempo acumulado en el estado actual
    float fade_accumulator_{0.0F};  // Acumulador de tiempo para el fade

    // Variables de demo mode
    DemoData demo_;  // Variables para el modo demo

    // Variables de efectos visuales
    SDL_FRect room_name_rect_;       // Rectangulo donde pintar la textura con el nombre de la habitación
    float jail_restore_time_{0.0F};  // Tiempo acumulado para restauración de vidas en la Jail

#ifdef _DEBUG
    // Variables de debug para arrastre con ratón
    bool debug_dragging_player_{false};  // Indica si estamos arrastrando al jugador con el ratón
    float debug_drag_speed_{0.0F};       // Velocidad actual del arrastre (ease-in)
    // Estado previo de invencibilidad antes de entrar en modo debug
    bool invincible_before_debug_{false};
#endif
};