#pragma once

#include <SDL3/SDL.h>  // Para SDL_Event, SDL_Renderer, SDL_Texture, Uint64

#include <list>    // Para list
#include <memory>  // Para shared_ptr, unique_ptr
#include <string>  // Para string
#include <vector>  // Para vector

#include "core/system/demo.hpp"                    // for Demo
#include "game/entities/bullet.hpp"                // for Bullet
#include "game/entities/item.hpp"                  // for Item (ptr only), ItemType
#include "game/entities/player.hpp"                // for Player
#include "game/gameplay/manage_hiscore_table.hpp"  // for HiScoreEntry
#include "game/options.hpp"                        // for Settings, settings

class Background;
class Balloon;
class BalloonManager;
class BulletManager;
class Fade;
class Input;
class PathSprite;
class PauseManager;
class Scoreboard;
class Screen;
class SmartSprite;
class StageManager;
class Tabe;
class Texture;
struct Path;

namespace Difficulty {
    enum class Code;
}  // namespace Difficulty

// --- Clase Game: núcleo principal del gameplay ---
//
// Esta clase gestiona toda la lógica del juego durante las partidas activas,
// incluyendo mecánicas de juego, estados, objetos y sistemas de puntuación.
//
// Funcionalidades principales:
// • Gestión de jugadores: soporte para 1 o 2 jugadores simultáneos
// • Sistema de estados: fade-in, entrada, jugando, completado, game-over
// • Mecánicas de juego: globos, balas, ítems, power-ups y efectos especiales
// • Sistema de puntuación: scoreboard y tabla de récords
// • Efectos temporales: tiempo detenido, ayudas automáticas
// • Modo demo: reproducción automática para attract mode
// • Gestión de fases: progresión entre niveles y dificultad
//
// Utiliza un sistema de tiempo basado en milisegundos para garantizar
// comportamiento consistente independientemente del framerate.
class Game {
   public:
    // --- Constantes ---
    static constexpr bool DEMO_OFF = false;  // Modo demo desactivado
    static constexpr bool DEMO_ON = true;    // Modo demo activado

    // --- Constructor y destructor ---
    Game(Player::Id player_id, int current_stage, bool demo_enabled);  // Constructor principal
    ~Game();                                                           // Destructor

    // --- Callbacks para el bucle SDL_MAIN_USE_CALLBACKS ---
    void iterate();                            // Ejecuta un frame
    void handleEvent(const SDL_Event& event);  // Procesa un evento

    // --- Bucle principal legacy (fallback) ---
    void run();  // Ejecuta el bucle principal del juego

   private:
    using Players = std::vector<std::shared_ptr<Player>>;

    // --- Enums ---
    enum class State {
        FADE_IN,                    // Transición de entrada
        ENTERING_PLAYER,            // Jugador entrando
        SHOWING_GET_READY_MESSAGE,  // Mostrando mensaje de preparado
        PLAYING,                    // Jugando
        COMPLETED,                  // Juego completado
        GAME_OVER,                  // Fin del juego
    };

    // --- Constantes de tiempo (en segundos) ---
    static constexpr float HELP_COUNTER_S = 16.667F;              // Contador de ayuda (1000 frames a 60fps → segundos)
    static constexpr float GAME_COMPLETED_START_FADE_S = 8.333F;  // Inicio del fade al completar (500 frames → segundos)
    static constexpr float GAME_COMPLETED_END_S = 11.667F;        // Fin del juego completado (700 frames → segundos)
    static constexpr float GAME_OVER_DURATION_S = 8.5F;
    static constexpr float TIME_STOPPED_DURATION_S = 6.0F;
    static constexpr float DEMO_FADE_PRE_DURATION_S = 0.5F;
    static constexpr int ITEM_POINTS_1_DISK_ODDS = 10;
    static constexpr int ITEM_POINTS_2_GAVINA_ODDS = 6;
    static constexpr int ITEM_POINTS_3_PACMAR_ODDS = 3;
    static constexpr int ITEM_CLOCK_ODDS = 5;
    static constexpr int ITEM_COFFEE_ODDS = 5;
    static constexpr int ITEM_POWER_BALL_ODDS = 0;
    static constexpr int ITEM_COFFEE_MACHINE_ODDS = 4;

    // --- Estructuras ---
    struct Helper {
        bool need_coffee{false};                                 // Indica si se necesitan cafes
        bool need_coffee_machine{false};                         // Indica si se necesita PowerUp
        bool need_power_ball{false};                             // Indica si se necesita una PowerBall
        float counter{HELP_COUNTER_S * 1000};                    // Contador para no dar ayudas consecutivas
        int item_disk_odds{ITEM_POINTS_1_DISK_ODDS};             // Probabilidad de aparición del objeto
        int item_gavina_odds{ITEM_POINTS_2_GAVINA_ODDS};         // Probabilidad de aparición del objeto
        int item_pacmar_odds{ITEM_POINTS_3_PACMAR_ODDS};         // Probabilidad de aparición del objeto
        int item_clock_odds{ITEM_CLOCK_ODDS};                    // Probabilidad de aparición del objeto
        int item_coffee_odds{ITEM_COFFEE_ODDS};                  // Probabilidad de aparición del objeto
        int item_coffee_machine_odds{ITEM_COFFEE_MACHINE_ODDS};  // Probabilidad de aparición del objeto
    };

    // --- Objetos y punteros ---
    SDL_Renderer* renderer_;  // El renderizador de la ventana
    Screen* screen_;          // Objeto encargado de dibujar en pantalla
    Input* input_;            // Manejador de entrada
    Scoreboard* scoreboard_;  // Objeto para dibujar el marcador

    SDL_Texture* canvas_;  // Textura para dibujar la zona de juego

    Players players_;                                        // Vector con los jugadores
    Players players_draw_list_;                              // Vector con los jugadores ordenados para ser renderizados
    std::list<std::unique_ptr<Item>> items_;                 // Vector con los items
    std::list<std::unique_ptr<SmartSprite>> smart_sprites_;  // Vector con los smartsprites
    std::list<std::unique_ptr<PathSprite>> path_sprites_;    // Vector con los pathsprites

    std::vector<std::shared_ptr<Texture>> item_textures_;                 // Vector con las texturas de los items
    std::vector<std::vector<std::shared_ptr<Texture>>> player_textures_;  // Vector con todas las texturas de los jugadores

    std::vector<std::shared_ptr<Texture>> game_text_textures_;  // Vector con las texturas para los sprites con textos

    std::vector<std::vector<std::string>> item_animations_;     // Vector con las animaciones de los items
    std::vector<std::vector<std::string>> player1_animations_;  // Vector con las animaciones del jugador 1
    std::vector<std::vector<std::string>> player2_animations_;  // Vector con las animaciones del jugador 2

    std::unique_ptr<PauseManager> pause_manager_;      // Objeto para gestionar la pausa
    std::unique_ptr<StageManager> stage_manager_;      // Objeto para gestionar las fases
    std::unique_ptr<BalloonManager> balloon_manager_;  // Objeto para gestionar los globos
    std::unique_ptr<BulletManager> bullet_manager_;    // Objeto para gestionar las balas
    std::unique_ptr<Background> background_;           // Objeto para dibujar el fondo del juego
    std::unique_ptr<Fade> fade_in_;                    // Objeto para renderizar fades
    std::unique_ptr<Fade> fade_out_;                   // Objeto para renderizar fades
    std::unique_ptr<Tabe> tabe_;                       // Objeto para gestionar el Tabe Volaor
    std::vector<Path> paths_;                          // Vector con los recorridos precalculados almacenados

    // --- Variables de estado ---
    HiScoreEntry hi_score_ = HiScoreEntry(
        Options::settings.hi_score_table[0].name,
        Options::settings.hi_score_table[0].score);  // Máxima puntuación y nombre de quien la ostenta

    Demo demo_;                                                   // Variable con todas las variables relacionadas con el modo demo
    Difficulty::Code difficulty_ = Options::settings.difficulty;  // Dificultad del juego
    Helper helper_;                                               // Variable para gestionar las ayudas
    Uint64 last_time_ = 0;                                        // Último tiempo registrado para deltaTime
    bool coffee_machine_enabled_ = false;                         // Indica si hay una máquina de café en el terreno de juego
    bool hi_score_achieved_ = false;                              // Indica si se ha superado la puntuación máxima
    float difficulty_score_multiplier_ = 1.0F;                    // Multiplicador de puntos en función de la dificultad
    float counter_ = 0.0F;                                        // Contador para el juego
    float game_completed_timer_ = 0.0F;                           // Acumulador de tiempo para el tramo final (milisegundos)
    float game_over_timer_ = 0.0F;                                // Timer para el estado de fin de partida (milisegundos)
    float time_stopped_timer_ = 0.0F;                             // Temporizador para llevar la cuenta del tiempo detenido
    float time_stopped_sound_timer_ = 0.0F;                       // Temporizador para controlar el sonido del tiempo detenido
    int menace_ = 0;                                              // Nivel de amenaza actual
    int menace_threshold_ = 0;                                    // Umbral del nivel de amenaza. Si el nivel de amenaza cae por debajo del umbral, se generan más globos. Si el umbral aumenta, aumenta el número de globos
    State state_ = State::FADE_IN;                                // Estado

    // Estructuras para gestionar flags de eventos basados en tiempo
    struct GameOverFlags {
        bool music_fade_triggered = false;
        bool message_triggered = false;
        bool fade_out_triggered = false;

        void reset() {
            music_fade_triggered = false;
            message_triggered = false;
            fade_out_triggered = false;
        }
    } game_over_flags_;

    struct GameCompletedFlags {
        bool start_celebrations_triggered = false;
        bool end_celebrations_triggered = false;

        void reset() {
            start_celebrations_triggered = false;
            end_celebrations_triggered = false;
        }
    } game_completed_flags_;

    struct TimeStoppedFlags {
        bool color_flash_sound_played = false;
        bool warning_phase_started = false;

        void reset() {
            color_flash_sound_played = false;
            warning_phase_started = false;
        }
    } time_stopped_flags_;

#ifdef _DEBUG
    bool auto_pop_balloons_ = false;  // Si es true, incrementa automaticamente los globos explotados
    bool autoplay_ = false;           // Si es true, los jugadores se mueven solos con datos de demo
    float autoplay_elapsed_s_ = 0.0F;
    int autoplay_index_ = 0;
    std::vector<DemoData> autoplay_data_;
#endif

    // --- Ciclo principal del juego ---
    void update(float delta_time);       // Actualiza la lógica principal del juego
    auto calculateDeltaTime() -> float;  // Calcula el deltatime
    void render();                       // Renderiza todos los elementos del juego
    void handleEvents();                 // Procesa los eventos del sistema en cola
    void checkState();                   // Verifica y actualiza el estado actual del juego
    void setState(State state);          // Cambia el estado del juego
    void cleanLists();                   // Limpia vectores de elementos deshabilitados

    // --- Gestión de estados del juego ---
    void updateGameStates(float delta_time);                       // Actualiza todos los estados del juego
    void updateGameStateFadeIn(float delta_time);                  // Gestiona el estado de transición de entrada (time-based)
    void updateGameStateEnteringPlayer(float delta_time);          // Gestiona el estado de entrada de jugador
    void updateGameStateShowingGetReadyMessage(float delta_time);  // Gestiona el estado de mensaje "preparado"
    void updateGameStatePlaying(float delta_time);                 // Gestiona el estado de juego activo
    void updateGameStateCompleted(float delta_time);               // Gestiona el estado de juego completado
    void updateGameStateGameOver(float delta_time);                // Gestiona las actualizaciones continuas del estado de fin de partida

    // --- Gestión de jugadores ---
    void initPlayers(Player::Id player_id);                    // Inicializa los datos de los jugadores
    void updatePlayers(float delta_time);                      // Actualiza las variables y estados de los jugadores
    void renderPlayers();                                      // Renderiza todos los jugadores en pantalla
    auto getPlayer(Player::Id id) -> std::shared_ptr<Player>;  // Obtiene un jugador por su identificador
    static auto getController(Player::Id player_id) -> int;    // Obtiene el controlador asignado a un jugador

    // --- Estado de jugadores ---
    void checkAndUpdatePlayerStatus(int active_player_index, int inactive_player_index);  // Actualiza estado entre jugadores
    void checkPlayersStatusPlaying();                                                     // Verifica el estado de juego de todos los jugadores
    auto allPlayersAreWaitingOrGameOver() -> bool;                                        // Verifica si todos esperan o han perdido
    auto allPlayersAreGameOver() -> bool;                                                 // Verifica si todos los jugadores han perdido
    auto allPlayersAreNotPlaying() -> bool;                                               // Verifica si ningún jugador está activo

    // --- Colisiones de jugadores ---
    void handlePlayerCollision(std::shared_ptr<Player>& player, std::shared_ptr<Balloon>& balloon);  // Procesa colisión de jugador con globo
    auto checkPlayerBalloonCollision(std::shared_ptr<Player>& player) -> std::shared_ptr<Balloon>;   // Detecta colisión jugador-globo
    void checkPlayerItemCollision(std::shared_ptr<Player>& player);                                  // Detecta colisión jugador-ítem

    // --- Sistema de entrada (input) ---
    void checkInput();       // Gestiona toda la entrada durante el juego
    void checkPauseInput();  // Verifica solicitudes de pausa de controladores

    // --- Entrada de jugadores normales ---
    void handlePlayersInput();                                                       // Gestiona entrada de todos los jugadores
    void handleNormalPlayerInput(const std::shared_ptr<Player>& player);             // Procesa entrada de un jugador específico
    void handleFireInput(const std::shared_ptr<Player>& player, Bullet::Type type);  // Gestiona disparo de jugador
    void handleFireInputs(const std::shared_ptr<Player>& player, bool autofire);     // Procesa disparos automáticos
    void handlePlayerContinueInput(const std::shared_ptr<Player>& player);           // Permite continuar al jugador
    void handlePlayerWaitingInput(const std::shared_ptr<Player>& player);            // Permite (re)entrar al jugador
    void handleNameInput(const std::shared_ptr<Player>& player);                     // Gestiona entrada de nombre del jugador

    // --- Entrada en modo demo ---
    void demoHandleInput();                                                        // Gestiona entrada durante el modo demostración
    void demoHandlePassInput();                                                    // Permite saltar la demostración
    void demoHandlePlayerInput(const std::shared_ptr<Player>& player, int index);  // Procesa entrada de jugador en demo

    // --- Colisiones específicas de balas ---
    auto checkBulletTabeCollision(const std::shared_ptr<Bullet>& bullet) -> bool;                            // Detecta colisión bala-Tabe
    auto checkBulletBalloonCollision(const std::shared_ptr<Bullet>& bullet) -> bool;                         // Detecta colisión bala-globo
    void processBalloonHit(const std::shared_ptr<Bullet>& bullet, const std::shared_ptr<Balloon>& balloon);  // Procesa impacto en globo

    // --- Sistema de ítems y power-ups ---
    void updateItems(float delta_time);                // Actualiza posición y estado de todos los ítems
    void renderItems();                                // Renderiza todos los ítems activos
    auto dropItem() -> ItemType;                       // Determina aleatoriamente qué ítem soltar
    void createItem(ItemType type, float x, float y);  // Crea un nuevo ítem en posición específica
    void freeItems();                                  // Libera memoria del vector de ítems
    void destroyAllItems();                            // Elimina todos los ítems activos de la pantalla

    // --- ítems especiales ---
    void enableTimeStopItem();                 // Activa el efecto de detener el tiempo
    void disableTimeStopItem();                // Desactiva el efecto de detener el tiempo
    void updateTimeStopped(float delta_time);  // Actualiza el estado del tiempo detenido
    void handleGameCompletedEvents();          // Maneja eventos del juego completado
    void handleGameOverEvents();               // Maneja eventos discretos basados en tiempo durante game over
    void throwCoffee(int x, int y);            // Crea efecto de café arrojado al ser golpeado

    // --- Gestión de caída de ítems ---
    void handleItemDrop(const std::shared_ptr<Balloon>& balloon, const std::shared_ptr<Player>& player);  // Gestiona caída de ítem desde globo

    // --- Sprites inteligentes (smartsprites) ---
    void updateSmartSprites(float delta_time);  // Actualiza todos los sprites con lógica propia (time-based)
    void renderSmartSprites();                  // Renderiza todos los sprites inteligentes
    void freeSmartSprites();                    // Libera memoria de sprites inteligentes

    // --- Sprites por ruta (pathsprites) ---
    void updatePathSprites(float delta_time);  // Actualiza sprites que siguen rutas predefinidas
    void renderPathSprites();                  // Renderiza sprites animados por ruta
    void freePathSprites();                    // Libera memoria de sprites por ruta
    void initPaths();                          // Inicializa rutas predefinidas para animaciones

    // --- Creación de sprites especiales ---
    void createItemText(int x, const std::shared_ptr<Texture>& texture);                          // Crea texto animado para ítems
    void createMessage(const std::vector<Path>& paths, const std::shared_ptr<Texture>& texture);  // Crea mensaje con animación por ruta

    // --- Sistema de globos y enemigos ---
    void handleBalloonDestruction(const std::shared_ptr<Balloon>& balloon, const std::shared_ptr<Player>& player);  // Procesa destrucción de globo
    void handleTabeHitEffects();                                                                                    // Gestiona efectos al golpear a Tabe
    void checkAndUpdateBalloonSpeed();                                                                              // Ajusta velocidad de globos según progreso

    // --- Gestión de fases y progresión ---
    void updateStage();         // Verifica y actualiza cambio de fase
    void initDifficultyVars();  // Inicializa variables de dificultad

    // --- Sistema de amenaza ---
    void updateMenace();  // Gestiona el nivel de amenaza del juego
    void setMenace();     // Calcula y establece amenaza según globos activos

    // --- Puntuación y marcador ---
    void updateHiScore();                     // Actualiza el récord máximo si es necesario
    void updateScoreboard(float delta_time);  // Actualiza la visualización del marcador
    void updateHiScoreName();                 // Pone en el marcador el nombre del primer jugador de la tabla
    void initScoreboard();                    // Inicializa el sistema de puntuación

    // --- Modo demostración ---
    void initDemo(Player::Id player_id);  // Inicializa variables para el modo demostración
    void updateDemo(float delta_time);    // Actualiza lógica específica del modo demo

    // --- Recursos y renderizado ---
    void setResources();                      // Asigna texturas y animaciones a los objetos
    void updateBackground(float delta_time);  // Actualiza elementos del fondo (time-based)
    void fillCanvas();                        // Renderiza elementos del área de juego en su textura
    void updateHelper();                      // Actualiza variables auxiliares de renderizado

    // --- Sistema de audio ---
    static void playMusic(const std::string& music_file, int loop = -1);  // Reproduce la música de fondo
    void stopMusic() const;                                               // Detiene la reproducción de música
    static void pauseMusic();                                             // Pausa la música
    static void resumeMusic();                                            // Retoma la música que eestaba pausada
    void playSound(const std::string& name) const;                        // Reproduce un efecto de sonido específico

    // --- Gestion y dibujado de jugadores en z-order ---
    static void buildPlayerDrawList(const Players& elements, Players& draw_list);   // Construye el draw_list a partir del vector principal
    static void updatePlayerDrawList(const Players& elements, Players& draw_list);  // Actualiza draw_list tras cambios en los z_order
    static void renderPlayerDrawList(const Players& draw_list);                     // Dibuja en el orden definido
    static auto findPlayerIndex(const Players& elems, const std::shared_ptr<Player>& who) -> size_t;
    static void sendPlayerToBack(Players& elements, const std::shared_ptr<Player>& who, Players& draw_list);    // Envia al jugador al fondo de la pantalla
    static void bringPlayerToFront(Players& elements, const std::shared_ptr<Player>& who, Players& draw_list);  // Envia al jugador al frente de la pantalla

    // --- Varios ---
    void onPauseStateChanged(bool is_paused);

// SISTEMA DE GRABACIÓN (CONDICIONAL)
#ifdef RECORDING
    void updateRecording(float deltaTime);  // Actualiza variables durante modo de grabación
#endif

// --- Depuración (solo en modo DEBUG) ---
#ifdef _DEBUG
    void handleDebugEvents(const SDL_Event& event);  // Comprueba los eventos en el modo DEBUG
    void autoplayHandleInput();                      // Alimenta input de demo a los jugadores en autoplay
#endif
};