#pragma once

#ifdef _DEBUG

#include <SDL3/SDL.h>

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

#include "external/fkyaml_node.hpp"      // Para fkyaml::node
#include "game/editor/mini_map.hpp"      // Para MiniMap
#include "game/editor/tile_picker.hpp"   // Para TilePicker
#include "game/entities/enemy.hpp"       // Para Enemy::Data
#include "game/entities/item.hpp"        // Para Item::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;

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;
    [[nodiscard]] auto getSetCompletions() 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)
    auto showInfo(bool show) -> std::string;
    auto showGrid(bool show) -> 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;
    void openTilePicker(const std::string& tileset_name, int current_tile);

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

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

    // Opciones persistentes del editor
    struct Settings {
        bool grid{false};
        bool show_render_info{false};
        std::string minimap_bg{"blue"};
        std::string minimap_conn{"white"};
    };
    Settings settings_;
    void loadSettings();
    void saveSettings() const;

    // Tipos para drag & drop y selección
    enum class DragTarget { NONE,
        PLAYER,
        ENEMY_INITIAL,
        ENEMY_BOUND1,
        ENEMY_BOUND2,
        ITEM };

    struct DragState {
        DragTarget target{DragTarget::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 renderEnemyBoundaries();
    static void renderBoundaryMarker(float x, float y, Uint8 color);
    void renderSelectionHighlight();
    void renderGrid() const;
    void handleMouseDown(float game_x, float game_y);
    void handleMouseUp();
    void updateDrag();
    void autosave();
    void updateStatusBarInfo();
    static auto snapToGrid(float value) -> float;
    static auto pointInRect(float px, float py, const SDL_FRect& rect) -> bool;

    // Estado del editor
    bool active_{false};
    DragState drag_;
    int selected_enemy_{-1};                 // Índice del enemigo seleccionado (-1 = ninguno)
    int selected_item_{-1};                  // Índice del item seleccionado (-1 = ninguno)
    static constexpr int NO_BRUSH = -2;      // Sin brush activo
    static constexpr int ERASER_BRUSH = -1;  // Brush borrador (pinta tile vacío = -1)
    int brush_tile_{NO_BRUSH};               // Tile activo para pintar
    bool painting_{false};                   // true mientras se está pintando con click izquierdo mantenido

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

    // YAML: nodo original (para campos que no se editan: name_ca, etc.)
    fkyaml::node yaml_;
    fkyaml::node yaml_backup_;

    // 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 render_info_before_editor_{false};
    bool reenter_{false};  // true cuando es un re-enter tras cambio de room (no tocar render_info)
};

#endif  // _DEBUG
