#pragma once

#include <SDL3/SDL.h>

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

#include "game/entities/enemy.hpp"       // Para EnemyData
#include "game/entities/item.hpp"        // Para ItemData
#include "game/gameplay/scoreboard.hpp"  // Para Scoreboard::Data
#include "utils/utils.hpp"               // Para LineHorizontal, LineDiagonal, LineVertical
class Sprite;                            // lines 12-12
class Surface;                           // lines 13-13
class EnemyManager;
class ItemManager;
class CollisionMap;
class TilemapRenderer;

class Room {
   public:
    // -- Enumeraciones y estructuras ---
    enum class Border : std::uint8_t {
        TOP = 0,
        RIGHT = 1,
        BOTTOM = 2,
        LEFT = 3,
        NONE = 4
    };

    enum class Tile : std::uint8_t {
        EMPTY,
        WALL,
        PASSABLE,
        SLOPE_L,
        SLOPE_R,
        KILL,
        ANIMATED
    };

    struct Data {
        std::string number;                // Numero de la habitación
        std::string name;                  // Nombre de la habitación
        std::string bg_color;              // Color de fondo de la habitación
        std::string border_color;          // Color del borde de la pantalla
        std::string item_color1;           // Color 1 para los items de la habitación
        std::string item_color2;           // Color 2 para los items de la habitación
        std::string upper_room;            // Identificador de la habitación que se encuentra arriba
        std::string lower_room;            // Identificador de la habitación que se encuentra abajo
        std::string left_room;             // Identificador de la habitación que se encuentra a la izquierda
        std::string right_room;            // Identificador de la habitación que se encuentra a la derecha
        std::string tile_set_file;         // Imagen con los gráficos para la habitación
        int conveyor_belt_direction{0};    // Sentido en el que arrastran las superficies automáticas de la habitación
        std::vector<int> tile_map;         // Índice de los tiles a dibujar en la habitación (embebido desde YAML)
        std::vector<Enemy::Data> enemies;  // Listado con los enemigos de la habitación
        std::vector<Item::Data> items;     // Listado con los items que hay en la habitación
    };

    // Constructor y destructor
    Room(const std::string& room_path, std::shared_ptr<Scoreboard::Data> data);
    ~Room();  // NOLINT(modernize-use-equals-default, performance-trivially-destructible) -- defined in .cpp for unique_ptr with forward declarations

    // --- Funciones ---
    [[nodiscard]] auto getNumber() const -> const std::string& { return number_; }               // Devuelve el numero de la habitación
    [[nodiscard]] auto getName() const -> const std::string& { return name_; }                   // Devuelve el nombre de la habitación
    [[nodiscard]] auto getBGColor() const -> Uint8 { return stringToColor(bg_color_); }          // Devuelve el color de la habitación
    [[nodiscard]] auto getBorderColor() const -> Uint8 { return stringToColor(border_color_); }  // Devuelve el color del borde
    void renderMap();                                                                            // Dibuja el mapa en pantalla
    void renderEnemies();                                                                        // Dibuja los enemigos en pantalla
    void renderItems();                                                                          // Dibuja los objetos en pantalla
#ifdef _DEBUG
    void redrawMap();                                                          // Redibuja el mapa (para actualizar modo debug)
    void updateEditorMode(float delta_time);                                   // Actualiza animaciones sin mover enemigos (para editor)
    void resetEnemyPositions(const std::vector<Enemy::Data>& enemy_data);      // Resetea enemigos a posiciones iniciales
    auto getEnemyManager() -> EnemyManager* { return enemy_manager_.get(); }   // Acceso al gestor de enemigos (para editor)
    auto getItemManager() -> ItemManager* { return item_manager_.get(); }      // Acceso al gestor de items (para editor)
    void setBgColor(const std::string& color);                                 // Cambia color de fondo y redibuja (para editor)
    void setItemColors(const std::string& color1, const std::string& color2);  // Cambia colores de items (para editor)
    void setTile(int index, int tile_value);                                   // Cambia un tile y redibuja (para editor)
    [[nodiscard]] auto getTileSetFile() const -> const std::string& { return tile_set_file_; }
    [[nodiscard]] auto getTileSetWidth() const -> int { return tile_set_width_; }
#endif
    void update(float delta_time);                                                                   // Actualiza las variables y objetos de la habitación
    auto getRoom(Border border) -> std::string;                                                      // Devuelve la cadena del fichero de la habitación contigua segun el borde
    auto getTile(SDL_FPoint point) -> Tile;                                                          // Devuelve el tipo de tile que hay en ese pixel
    auto getTile(int index) -> Tile;                                                                 // Devuelve el tipo de tile en un índice del tilemap
    auto enemyCollision(SDL_FRect& rect) -> bool;                                                    // Indica si hay colision con un enemigo a partir de un rectangulo
    auto itemCollision(SDL_FRect& rect) -> bool;                                                     // Indica si hay colision con un objeto a partir de un rectangulo
    static auto getTileSize() -> int { return TILE_SIZE; }                                           // Obten el tamaño del tile
    static auto getSlopeHeight(SDL_FPoint p, Tile slope) -> int;                                     // Obten la coordenada de la cuesta a partir de un punto perteneciente a ese tile
    auto checkRightSurfaces(const SDL_FRect& rect) -> int;                                           // Comprueba las colisiones
    auto checkLeftSurfaces(const SDL_FRect& rect) -> int;                                            // Comprueba las colisiones
    auto checkTopSurfaces(const SDL_FRect& rect) -> int;                                             // Comprueba las colisiones
    auto checkBottomSurfaces(const SDL_FRect& rect) -> int;                                          // Comprueba las colisiones
    auto checkAutoSurfaces(const SDL_FRect& rect) -> int;                                            // Comprueba las colisiones
    auto checkTopSurfaces(const SDL_FPoint& p) -> bool;                                              // Comprueba las colisiones
    auto checkConveyorBelts(const SDL_FPoint& p) -> bool;                                            // Comprueba las colisiones
    auto checkLeftSlopes(const LineVertical& line) -> int;                                           // Comprueba las colisiones
    auto checkLeftSlopes(const SDL_FPoint& p) -> bool;                                               // Comprueba las colisiones
    auto checkRightSlopes(const LineVertical& line) -> int;                                          // Comprueba las colisiones
    auto checkRightSlopes(const SDL_FPoint& p) -> bool;                                              // Comprueba las colisiones
    [[nodiscard]] auto getSlopeAtPoint(const SDL_FPoint& p) const -> const LineDiagonal*;            // Obtiene puntero a slope en un punto
    void setPaused(bool value);                                                                      // Pone el mapa en modo pausa
    [[nodiscard]] auto getConveyorBeltDirection() const -> int { return conveyor_belt_direction_; }  // Obten la direccion de las superficies automaticas

    // Método de carga de archivos YAML (delegado a RoomLoader)
    static auto loadYAML(const std::string& file_path, bool verbose = false) -> Data;  // Carga habitación desde archivo YAML unificado

   private:
    // Constantes
    static constexpr int TILE_SIZE = 8;    // Ancho del tile en pixels
    static constexpr int MAP_WIDTH = 32;   // Ancho del mapa en tiles
    static constexpr int MAP_HEIGHT = 16;  // Alto del mapa en tiles

    // Objetos y punteros
    std::unique_ptr<EnemyManager> enemy_manager_;        // Gestor de enemigos de la habitación
    std::unique_ptr<ItemManager> item_manager_;          // Gestor de items de la habitación
    std::unique_ptr<CollisionMap> collision_map_;        // Mapa de colisiones de la habitación
    std::unique_ptr<TilemapRenderer> tilemap_renderer_;  // Renderizador del mapa de tiles
    std::shared_ptr<Surface> surface_;                   // Textura con los graficos de la habitación
    std::shared_ptr<Scoreboard::Data> data_;             // Puntero a los datos del marcador

    // --- Variables ---
    std::string number_;              // Numero de la habitación
    std::string name_;                // Nombre de la habitación
    std::string bg_color_;            // Color de fondo de la habitación
    std::string border_color_;        // Color del borde de la pantalla
    std::string item_color1_;         // Color 1 para los items de la habitación
    std::string item_color2_;         // Color 2 para los items de la habitación
    std::string upper_room_;          // Identificador de la habitación que se encuentra arriba
    std::string lower_room_;          // Identificador de la habitación que se encuentra abajp
    std::string left_room_;           // Identificador de la habitación que se encuentra a la izquierda
    std::string right_room_;          // Identificador de la habitación que se encuentra a la derecha
    std::string tile_set_file_;       // Imagen con los graficos para la habitación
    std::vector<int> tile_map_;       // Indice de los tiles a dibujar en la habitación (embebido desde YAML)
    int conveyor_belt_direction_{0};  // Sentido en el que arrastran las superficies automáticas de la habitación
    bool is_paused_{false};           // Indica si el mapa esta en modo pausa
    int tile_set_width_{0};           // Ancho del tileset en tiles

    // --- Funciones ---
    void initializeRoom(const Data& room);  // Inicializa los valores
    void openTheJail();                     // Abre la jail para poder entrar
};