#pragma once

#include <SDL3/SDL.h>

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

#include "game/defaults.hpp"                      // Para Defaults::Game::Room
#include "game/entities/door.hpp"                 // Para Door::Data
#include "game/entities/enemy.hpp"                // Para EnemyData
#include "game/entities/item.hpp"                 // Para ItemData
#include "game/entities/key.hpp"                  // Para Key::Data
#include "game/entities/moving_platform.hpp"      // Para MovingPlatform::Data
#include "game/gameplay/collision_map.hpp"        // Para CollisionMap::AdjacentData
#include "game/gameplay/scoreboard.hpp"           // Para Scoreboard::Data
#include "game/gameplay/solid_actor_manager.hpp"  // Para SolidActorManager::AdjacentActors
#include "utils/defines.hpp"                      // Para Tile::SIZE, Map::WIDTH, Map::HEIGHT
class Surface;
class EnemyManager;
class ItemManager;
class PlatformManager;
class KeyManager;
class DoorManager;
class TileCollider;
class TilemapRenderer;

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

    struct Data {
        std::string number;
        Uint8 bg_color{0};
        std::string upper_room;
        std::string lower_room;
        std::string left_room;
        std::string right_room;
        std::string zone;                 // Nombre de la zona a la que pertenece
        std::string tile_set_file;        // Resuelto: zona o override del yaml
        std::string music;                // Resuelto: zona o override del yaml
        bool tile_set_overridden{false};  // True si el yaml tenía tileSetFile explícito
        bool music_overridden{false};     // True si el yaml tenía music explícito
        bool bg_color_overridden{false};  // True si el yaml tenía bgColor explícito
        std::vector<int> tile_map;
        std::vector<int> collision_tile_map;
        std::vector<Enemy::Data> enemies;
        std::vector<Item::Data> items;
        std::vector<MovingPlatform::Data> platforms;
        std::vector<Key::Data> keys;
        std::vector<Door::Data> doors;
    };

    // Constructor y destructor
    Room(const std::string& room_path, std::shared_ptr<Scoreboard::Data> data);
    // NOLINTNEXTLINE(modernize-use-equals-default,performance-trivially-destructible) -- destructor definit al .cpp perquè la classe té unique_ptr a tipus forward-declared; no es pot fer = default ni eliminar a l'header sense incloure tots els headers transitivament.
    ~Room();

    // --- Funciones ---
    [[nodiscard]] auto getNumber() const -> const std::string& { return number_; }
    [[nodiscard]] auto getBGColor() const -> Uint8 { return bg_color_; }
    [[nodiscard]] auto getZone() const -> const std::string& { return zone_; }
    [[nodiscard]] auto getMusic() const -> const std::string& { return music_; }
    [[nodiscard]] auto getTileSetFile() const -> const std::string& { return tile_set_file_; }
    void renderMap();
    void renderEnemies();
    void renderPlatforms();
    void renderItems();
    void renderKeys();
    void renderDoors();
#ifdef _DEBUG
    void redrawMap();
    void updateEditorMode(float delta_time);
    void resetEnemyPositions(const std::vector<Enemy::Data>& enemy_data);
    auto getEnemyManager() -> EnemyManager* { return enemy_manager_.get(); }
    auto getItemManager() -> ItemManager* { return item_manager_.get(); }
    auto getPlatformManager() -> PlatformManager* { return platform_manager_.get(); }
    auto getKeyManager() -> KeyManager* { return key_manager_.get(); }
    auto getDoorManager() -> DoorManager* { return door_manager_.get(); }
    auto getSolidActorManager() -> SolidActorManager* { return solid_actor_manager_.get(); }
    void setTile(int index, int tile_value);
    void setCollisionTile(int index, int value);
    void setConnection(Border border, const std::string& room_name);
    void setTileSet(const std::string& tile_set_file);
    void setBgColor(Uint8 bg_color);
    [[nodiscard]] auto getTileSetWidth() const -> int { return tile_set_width_; }
#endif
    void update(float delta_time);
    auto getRoom(Border border) -> std::string;
    auto enemyCollision(SDL_FRect& rect) -> bool;
    auto itemCollision(SDL_FRect& rect) -> bool;
    auto keyCollision(SDL_FRect& rect) -> bool;
    void tryUnlockDoors(const SDL_FRect& player_rect);
    void setPaused(bool value);
    [[nodiscard]] auto getTileCollider() const -> const TileCollider&;
    [[nodiscard]] auto getSolidActors() const -> const SolidActorManager&;
    [[nodiscard]] auto getSolidActors() -> SolidActorManager&;
    [[nodiscard]] auto getCollisionTileMap() const -> const std::vector<int>&;
    void updateCollisionBorders(const CollisionMap::AdjacentData& adjacent);
    void updateSolidActorBorders(const SolidActorManager::AdjacentActors& adjacent);

    // Método de carga de archivos YAML (delegado a RoomFormat)
    static auto loadYAML(const std::string& file_path, bool verbose = false) -> Data;

   private:
    static constexpr int TILE_SIZE = ::Tile::SIZE;
    static constexpr int MAP_WIDTH = ::Map::WIDTH;
    static constexpr int MAP_HEIGHT = ::Map::HEIGHT;

    std::unique_ptr<EnemyManager> enemy_manager_;
    std::unique_ptr<ItemManager> item_manager_;
    std::unique_ptr<PlatformManager> platform_manager_;
    std::unique_ptr<KeyManager> key_manager_;
    std::unique_ptr<DoorManager> door_manager_;
    std::unique_ptr<SolidActorManager> solid_actor_manager_;
    std::unique_ptr<CollisionMap> collision_map_;
    std::unique_ptr<TilemapRenderer> tilemap_renderer_;
    std::shared_ptr<Surface> surface_;
    std::shared_ptr<Scoreboard::Data> data_;

    std::string number_;
    Uint8 bg_color_{0};
    std::string upper_room_;
    std::string lower_room_;
    std::string left_room_;
    std::string right_room_;
    std::string zone_;
    std::string tile_set_file_;
    std::string music_;
    std::vector<int> tile_map_;
    bool is_paused_{false};
    int tile_set_width_{0};

    void initializeRoom(const Data& room);
};
