#pragma once

#ifdef _DEBUG

#include <SDL3/SDL.h>

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

#include "game/editor/mini_map.hpp"           // Para MiniMap
#include "game/editor/tile_picker.hpp"        // Para TilePicker
#include "game/entities/door.hpp"             // Para Door::Data
#include "game/entities/enemy.hpp"            // Para Enemy::Data
#include "game/entities/item.hpp"             // Para Item::Data
#include "game/entities/key.hpp"              // Para Key::Data
#include "game/entities/moving_platform.hpp"  // Para MovingPlatform::Data
#include "game/entities/player.hpp"           // Para Player::SpawnData
#include "game/gameplay/room.hpp"             // Para Room::Data
#include "game/gameplay/scoreboard.hpp"       // Para Scoreboard::Data
#include "game/options.hpp"                   // Para Options::Cheat

class EditorStatusBar;

// Tipo de entidad editable en el editor
enum class EntityType : std::uint8_t { NONE,
    ENEMY,
    ITEM,
    PLATFORM,
    KEY,
    DOOR };

// Seleccion unificada: una sola entidad seleccionada a la vez
struct Selection {
    EntityType type{EntityType::NONE};
    int index{-1};
    void clear() {
        type = EntityType::NONE;
        index = -1;
    }
    [[nodiscard]] auto isNone() const -> bool { return type == EntityType::NONE; }
    [[nodiscard]] auto is(EntityType t) const -> bool { return type == t && index >= 0; }
};

// Brush rectangular para pintar tiles. Una sola estructura cubre:
// - brush vacío (width=0, height=0)
// - single tile (width=1, height=1, tiles={N})
// - borrador explícito (width=1, height=1, tiles={ERASE})
// - patrón rectangular sampleado del nivel o del tileset
struct BrushPattern {
    static constexpr int TRANSPARENT = -2;  // celda no se escribe al estampar
    static constexpr int ERASE = -1;        // celda escribe -1 (vacía el destino)

    int width{0};
    int height{0};
    std::vector<int> tiles;  // size = width*height, row-major

    [[nodiscard]] auto isEmpty() const -> bool { return width <= 0 || height <= 0; }
    [[nodiscard]] auto at(int dx, int dy) const -> int { return tiles[(dy * width) + dx]; }
    void clear() {
        width = 0;
        height = 0;
        tiles.clear();
    }
    void setSingle(int tile) {
        width = 1;
        height = 1;
        tiles = {tile};
    }
};

// Estado del eyedropper (clic derecho con drag para samplear el nivel)
struct EyedropperState {
    bool active{false};
    int start_tile_x{0};
    int start_tile_y{0};
};

class MapEditor {
   public:
    static void init();               // [SINGLETON] Crea el objeto
    static void destroy();            // [SINGLETON] Destruye el objeto
    static auto get() -> MapEditor*;  // [SINGLETON] Obtiene el objeto

    void enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player, const std::string& room_path, std::shared_ptr<Scoreboard::Data> scoreboard_data);
    void exit();
    [[nodiscard]] auto isActive() const -> bool { return active_; }

    void update(float delta_time);
    void render();
    void handleEvent(const SDL_Event& event);
    auto revert() -> std::string;

    // Comandos para enemigos (llamados desde console_commands)
    auto setEnemyProperty(const std::string& property, const std::string& value) -> std::string;
    auto addEnemy() -> std::string;
    auto deleteEnemy() -> std::string;
    auto duplicateEnemy() -> std::string;
    [[nodiscard]] auto hasSelectedEnemy() const -> bool { return selection_.is(EntityType::ENEMY); }
    [[nodiscard]] auto getSetCompletions() const -> std::vector<std::string>;
    [[nodiscard]] auto getAnimationCompletions() const -> std::vector<std::string>;

    // Comandos para propiedades de la habitación
    auto setRoomProperty(const std::string& property, const std::string& value) -> std::string;
    auto createNewRoom(const std::string& direction = "") -> std::string;
    auto deleteRoom() -> std::string;

    // Opciones del editor (llamados desde console_commands / teclas)
    static auto showInfo(bool show) -> std::string;
    auto showGrid(bool show) -> std::string;
    auto setEditingCollision(bool collision) -> std::string;
    [[nodiscard]] auto isGridEnabled() const -> bool { return settings_.grid; }
    void toggleMiniMap();
    void setReenter(bool value) { reenter_ = value; }
    auto setMiniMapBg(const std::string& color) -> std::string;
    auto setMiniMapConn(const std::string& color) -> std::string;

    // Comandos para items
    auto setItemProperty(const std::string& property, const std::string& value) -> std::string;
    auto addItem() -> std::string;
    auto deleteItem() -> std::string;
    auto duplicateItem() -> std::string;
    [[nodiscard]] auto hasSelectedItem() const -> bool { return selection_.is(EntityType::ITEM); }
    void openTilePicker(const std::string& tileset_name, int current_tile);

    // Comandos para plataformas
    auto setPlatformProperty(const std::string& property, const std::string& value) -> std::string;
    auto addPlatform() -> std::string;
    auto deletePlatform() -> std::string;
    auto duplicatePlatform() -> std::string;
    [[nodiscard]] auto hasSelectedPlatform() const -> bool { return selection_.is(EntityType::PLATFORM); }

    // Comandos para llaves
    auto setKeyProperty(const std::string& property, const std::string& value) -> std::string;
    auto addKey() -> std::string;
    auto deleteKey() -> std::string;
    auto duplicateKey() -> std::string;
    [[nodiscard]] auto hasSelectedKey() const -> bool { return selection_.is(EntityType::KEY); }

    // Comandos para puertas
    auto setDoorProperty(const std::string& property, const std::string& value) -> std::string;
    auto addDoor() -> std::string;
    auto deleteDoor() -> std::string;
    auto duplicateDoor() -> std::string;
    [[nodiscard]] auto hasSelectedDoor() const -> bool { return selection_.is(EntityType::DOOR); }

    // Seleccion unificada
    [[nodiscard]] auto getSelectionType() const -> EntityType { return selection_.type; }

   private:
    static MapEditor* instance_;  // NOLINT(readability-identifier-naming) [SINGLETON] Objeto privado

    MapEditor();   // Constructor
    ~MapEditor();  // Destructor

    // Opciones persistentes del editor
    struct Settings {
        bool grid{false};
        Uint8 minimap_bg{2};
        Uint8 minimap_conn{14};
    };
    Settings settings_;
    void loadSettings();
    void saveSettings() const;

    // Tipos para drag & drop
    enum class DragTarget : std::uint8_t { NONE,
        PLAYER,
        ENTITY_INITIAL,
        ENTITY_BOUND1,
        ENTITY_BOUND2 };

    struct DragState {
        DragTarget target{DragTarget::NONE};
        EntityType entity_type{EntityType::NONE};
        int index{-1};
        float offset_x{0.0F};
        float offset_y{0.0F};
        float snap_x{0.0F};
        float snap_y{0.0F};
        bool moved{false};  // true si el ratón se movió durante el drag
    };

    // Métodos internos
    void updateMousePosition();
    void renderCollisionOverlay() const;
    void renderEntityBoundaries();
    static void renderBoundaryMarker(float x, float y, Uint8 color);
    void renderSelectionHighlight();
    void renderBrushPreview();
    void renderEyedropperRect() const;
    static void renderGrid();
    void handleMouseDown(float game_x, float game_y);
    void handleMouseUp();
    void stampBrushAt(int tile_x, int tile_y);
    void commitEyedropper();
    [[nodiscard]] auto sampleBrush(int x1, int y1, int x2, int y2) const -> BrushPattern;
    [[nodiscard]] static auto buildPatternFromTileset(const std::string& tileset_name, int col, int row, int width, int height) -> BrushPattern;

    // Reconstruye todas las puertas vivas desde room_data_, limpiando primero
    // los WALLs antiguos del CollisionMap. Lo usa setDoorProperty cuando un
    // cambio (id, animation) requiere recrear el Door y mantener los tiles
    // sincronizados.
    void rebuildDoors();
    void updateDrag();
    auto commitEntityDrag() -> bool;
    void moveEntityVisual();
    void autosave();
    void updateStatusBarInfo();
    static auto snapToGrid(float value) -> float;
    static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool;

    // Entity helpers: acceso abstracto a datos de entidad por tipo
    struct BoundaryData {
        int x1, y1, x2, y2;
    };
    [[nodiscard]] auto entityCount(EntityType type) const -> int;
    auto entityRect(EntityType type, int index) -> SDL_FRect;
    static auto entityHasBoundaries(EntityType type) -> bool;
    [[nodiscard]] auto entityBoundaries(EntityType type, int index) const -> BoundaryData;
    [[nodiscard]] auto entityPosition(EntityType type, int index) const -> std::pair<float, float>;
    [[nodiscard]] auto entityDataCount(EntityType type) const -> int;
    static auto entityLabel(EntityType type) -> const char*;

    // Estado del editor
    bool active_{false};
    DragState drag_;
    Selection selection_;            // Entidad seleccionada (unificada: enemy, item o platform)
    BrushPattern brush_;             // Brush activo (vacío = sin brush)
    EyedropperState eyedropper_;     // Estado del eyedropper (clic derecho)
    bool painting_{false};           // true mientras se está pintando con click izquierdo mantenido
    bool editing_collision_{false};  // true = editando collision tilemap, false = editando draw tilemap
    int auto_collision_{-1};         // -1 = desactivado, 1 = WALL, 2 = PASSABLE (se aplica al pintar draw tiles)

    // Datos de la habitación
    Room::Data room_data_;
    std::string room_path_;
    std::string file_path_;

    // Referencias a objetos vivos
    std::shared_ptr<Room> room_;
    std::shared_ptr<Player> player_;
    std::shared_ptr<Scoreboard::Data> scoreboard_data_;

    // Barra de estado del editor
    std::unique_ptr<EditorStatusBar> statusbar_;

    // Tile picker y mini mapa
    TilePicker tile_picker_;
    std::unique_ptr<MiniMap> mini_map_;
    bool mini_map_visible_{false};

    // Estado del ratón
    float mouse_game_x_{0.0F};
    float mouse_game_y_{0.0F};
    int mouse_tile_x_{0};
    int mouse_tile_y_{0};

    // Estado previo (para restaurar al salir)
    Options::Cheat::State invincible_before_editor_{Options::Cheat::State::DISABLED};
    bool reenter_{false};  // true cuando es un re-enter tras cambio de room
};

#endif  // _DEBUG
