#ifdef _DEBUG

#include "game/editor/map_editor.hpp"

#include <SDL3/SDL.h>

#include <algorithm>
#include <cmath>     // Para std::round
#include <cstdio>    // Para std::remove (borrar fichero)
#include <fstream>   // Para ifstream, ofstream
#include <iostream>  // Para cout
#include <set>       // Para set

#include "core/input/mouse.hpp"               // Para Mouse
#include "core/rendering/render_info.hpp"     // Para RenderInfo
#include "core/rendering/screen.hpp"          // Para Screen
#include "core/rendering/surface.hpp"         // Para Surface
#include "core/resources/resource_cache.hpp"  // Para Resource::Cache
#include "core/resources/resource_list.hpp"   // Para Resource::List
#include "core/resources/resource_types.hpp"  // Para RoomResource
#include "game/editor/editor_statusbar.hpp"   // Para EditorStatusBar
#include "game/editor/room_saver.hpp"         // Para RoomSaver
#include "game/entities/player.hpp"           // Para Player
#include "game/game_control.hpp"              // Para GameControl
#include "game/gameplay/enemy_manager.hpp"    // Para EnemyManager
#include "game/gameplay/item_manager.hpp"     // Para ItemManager
#include "game/gameplay/room.hpp"             // Para Room
#include "game/options.hpp"                   // Para Options
#include "game/ui/console.hpp"                // Para Console
#include "utils/defines.hpp"                  // Para Tile::SIZE, PlayArea
#include "utils/utils.hpp"                    // Para stringToColor

// Singleton
MapEditor* MapEditor::instance_ = nullptr;

void MapEditor::init() {
    instance_ = new MapEditor();
}

void MapEditor::destroy() {
    delete instance_;
    instance_ = nullptr;
}

auto MapEditor::get() -> MapEditor* {
    return instance_;
}

// Constructor
MapEditor::MapEditor() {
    loadSettings();
}

// Destructor
MapEditor::~MapEditor() = default;

// Carga las opciones del editor desde editor.yaml
void MapEditor::loadSettings() {
    std::string path = Resource::List::get()->get("editor.yaml");
    if (path.empty()) { return; }

    std::ifstream file(path);
    if (!file.is_open()) { return; }

    std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
    file.close();

    try {
        auto yaml = fkyaml::node::deserialize(content);
        if (yaml.contains("grid")) { settings_.grid = yaml["grid"].get_value<bool>(); }
        if (yaml.contains("show_render_info")) { settings_.show_render_info = yaml["show_render_info"].get_value<bool>(); }
        if (yaml.contains("minimap_bg")) {
            if (yaml["minimap_bg"].is_string()) {
                settings_.minimap_bg = yaml["minimap_bg"].get_value<std::string>();
            } else if (yaml["minimap_bg"].is_integer()) {
                settings_.minimap_bg = std::to_string(yaml["minimap_bg"].get_value<int>());
            }
        }
        if (yaml.contains("minimap_conn")) {
            if (yaml["minimap_conn"].is_string()) {
                settings_.minimap_conn = yaml["minimap_conn"].get_value<std::string>();
            } else if (yaml["minimap_conn"].is_integer()) {
                settings_.minimap_conn = std::to_string(yaml["minimap_conn"].get_value<int>());
            }
        }
    } catch (...) {
        // Fichero corrupto o vacío, usar defaults
    }
}

// Guarda las opciones del editor a editor.yaml
void MapEditor::saveSettings() const {
    std::string path = Resource::List::get()->get("editor.yaml");
    if (path.empty()) { return; }

    std::ofstream file(path);
    if (!file.is_open()) { return; }

    file << "# Map Editor Settings\n";
    file << "grid: " << (settings_.grid ? "true" : "false") << "\n";
    file << "show_render_info: " << (settings_.show_render_info ? "true" : "false") << "\n";
    file << "minimap_bg: " << settings_.minimap_bg << "\n";
    file << "minimap_conn: " << settings_.minimap_conn << "\n";
    file.close();
}

// Muestra/oculta render info (persistente)
auto MapEditor::showInfo(bool show) -> std::string {
    settings_.show_render_info = show;
    if (RenderInfo::get()->isActive() != show) {
        RenderInfo::get()->toggle();
    }
    saveSettings();
    return show ? "Info ON" : "Info OFF";
}

// Muestra/oculta grid (persistente)
auto MapEditor::showGrid(bool show) -> std::string {
    settings_.grid = show;
    saveSettings();
    return show ? "Grid ON" : "Grid OFF";
}

// Parsea un color por nombre o índice numérico
static auto parseColor(const std::string& value) -> Uint8 {
    try {
        return static_cast<Uint8>(std::stoi(value));
    } catch (...) { return stringToColor(value); }
}

void MapEditor::toggleMiniMap() {
    if (!mini_map_) {
        mini_map_ = std::make_unique<MiniMap>(parseColor(settings_.minimap_bg), parseColor(settings_.minimap_conn));
        mini_map_->on_navigate = [this](const std::string& room_name) {
            mini_map_visible_ = false;
            reenter_ = true;
            if (GameControl::exit_editor) { GameControl::exit_editor(); }
            if (GameControl::change_room && GameControl::change_room(room_name)) {
                if (GameControl::enter_editor) { GameControl::enter_editor(); }
            }
        };
    }
    mini_map_visible_ = !mini_map_visible_;
    if (mini_map_visible_) {
        // Reconstruir el minimapa (pueden haber cambiado rooms, conexiones, tiles)
        mini_map_ = std::make_unique<MiniMap>(parseColor(settings_.minimap_bg), parseColor(settings_.minimap_conn));
        mini_map_->on_navigate = [this](const std::string& room_name) {
            mini_map_visible_ = false;
            reenter_ = true;
            if (GameControl::exit_editor) { GameControl::exit_editor(); }
            if (GameControl::change_room && GameControl::change_room(room_name)) {
                if (GameControl::enter_editor) { GameControl::enter_editor(); }
            }
        };
        mini_map_->centerOnRoom(room_path_);
    }
}

auto MapEditor::setMiniMapBg(const std::string& color) -> std::string {
    settings_.minimap_bg = toLower(color);
    saveSettings();
    if (mini_map_) {
        mini_map_->rebuild(parseColor(settings_.minimap_bg), parseColor(settings_.minimap_conn));
    }
    return "minimap bg: " + settings_.minimap_bg;
}

auto MapEditor::setMiniMapConn(const std::string& color) -> std::string {
    settings_.minimap_conn = toLower(color);
    saveSettings();
    if (mini_map_) {
        mini_map_->rebuild(parseColor(settings_.minimap_bg), parseColor(settings_.minimap_conn));
    }
    return "minimap conn: " + settings_.minimap_conn;
}

// Entra en modo editor
void MapEditor::enter(std::shared_ptr<Room> room, std::shared_ptr<Player> player, const std::string& room_path, std::shared_ptr<Scoreboard::Data> scoreboard_data) {
    if (active_) { return; }

    room_ = std::move(room);
    player_ = std::move(player);
    room_path_ = room_path;
    scoreboard_data_ = std::move(scoreboard_data);

    // Cargar una copia de los datos de la habitación (para boundaries y edición)
    auto room_data_ptr = Resource::Cache::get()->getRoom(room_path);
    if (room_data_ptr) {
        room_data_ = *room_data_ptr;
    }

    // Obtener la ruta completa y cargar el YAML original (para edición parcial y backup)
    file_path_ = Resource::List::get()->get(room_path_);
    if (!file_path_.empty()) {
        yaml_ = RoomSaver::loadYAML(file_path_);
        yaml_backup_ = yaml_;  // Copia profunda para revert
    }

    if (!reenter_) {
        // Solo guardar estado previo en el primer enter (no en re-enter tras cambio de room)
        invincible_before_editor_ = Options::cheats.invincible;
        render_info_before_editor_ = RenderInfo::get()->isActive();
    }
    reenter_ = false;

    // Forzar invencibilidad
    Options::cheats.invincible = Options::Cheat::State::ENABLED;
    player_->setColor();

    // Aplicar el setting de render_info del editor
    if (settings_.show_render_info != RenderInfo::get()->isActive()) {
        RenderInfo::get()->toggle();
    }

    // Activar scope de la consola para el editor
    Console::get()->setScope("editor");

    // Resetear enemigos a su posición inicial (pueden haberse movido durante el gameplay)
    room_->resetEnemyPositions(room_data_.enemies);

    // Crear la barra de estado
    statusbar_ = std::make_unique<EditorStatusBar>(room_->getNumber(), room_->getName());

    // Resetear estado
    drag_ = {};
    selected_enemy_ = -1;
    selected_item_ = -1;
    brush_tile_ = NO_BRUSH;
    painting_ = false;

    active_ = true;
    std::cout << "MapEditor: ON (room " << room_path_ << ")\n";
}

// Sale del modo editor
void MapEditor::exit() {
    if (!active_) { return; }

    active_ = false;

    if (!reenter_) {
        // Solo restaurar en el exit final (no en cambio de room)
        Options::cheats.invincible = invincible_before_editor_;
        player_->setColor();

        if (RenderInfo::get()->isActive() != render_info_before_editor_) {
            RenderInfo::get()->toggle();
        }
    }

    // Restaurar prompt y scope de la consola
    selected_enemy_ = -1;
    Console::get()->setPrompt("> ");
    Console::get()->setScope("debug");
    drag_ = {};
    statusbar_.reset();
    room_.reset();
    player_.reset();
    scoreboard_data_.reset();

    std::cout << "MapEditor: OFF\n";
}

// Revierte todos los cambios al estado original
auto MapEditor::revert() -> std::string {
    if (!active_) { return "Editor not active"; }
    if (file_path_.empty()) { return "Error: No file path"; }

    // Restaurar el YAML original y reescribir el fichero
    yaml_ = yaml_backup_;
    auto room_data_ptr = Resource::Cache::get()->getRoom(room_path_);
    if (room_data_ptr) {
        room_data_ = *room_data_ptr;
    }
    RoomSaver::saveYAML(file_path_, yaml_, room_data_);

    // Resetear enemigos a posiciones originales
    room_->resetEnemyPositions(room_data_.enemies);

    // Resetear items (posiciones y colores)
    auto* item_mgr = room_->getItemManager();
    for (int i = 0; i < item_mgr->getCount() && i < static_cast<int>(room_data_.items.size()); ++i) {
        item_mgr->getItem(i)->setPosition(room_data_.items[i].x, room_data_.items[i].y);
    }
    room_->setItemColors(room_data_.item_color1, room_data_.item_color2);

    // Refrescar visuales de la habitación
    room_->setBgColor(room_data_.bg_color);
    Screen::get()->setBorderColor(stringToColor(room_data_.border_color));

    // Restaurar el tilemap completo
    for (int i = 0; i < static_cast<int>(room_data_.tile_map.size()); ++i) {
        room_->setTile(i, room_data_.tile_map[i]);
    }

    selected_enemy_ = -1;
    selected_item_ = -1;
    brush_tile_ = NO_BRUSH;
    return "Reverted to original";
}

// Auto-guarda al YAML tras soltar una entidad
void MapEditor::autosave() {
    if (file_path_.empty()) { return; }

    // Sincronizar posiciones de items desde los sprites vivos a room_data_
    auto* item_mgr = room_->getItemManager();
    for (int i = 0; i < item_mgr->getCount() && i < static_cast<int>(room_data_.items.size()); ++i) {
        SDL_FPoint pos = item_mgr->getItem(i)->getPos();
        room_data_.items[i].x = pos.x;
        room_data_.items[i].y = pos.y;
    }

    RoomSaver::saveYAML(file_path_, yaml_, room_data_);
}

// Actualiza el editor
void MapEditor::update(float delta_time) {
    // Mantener el ratón siempre visible
    SDL_ShowCursor();
    Mouse::last_mouse_move_time = SDL_GetTicks();

    // Actualizar animaciones de enemigos e items (sin mover enemigos)
    room_->updateEditorMode(delta_time);

    // Actualizar posición del ratón
    updateMousePosition();

    // Si estamos arrastrando, actualizar la posición snapped
    if (drag_.target != DragTarget::NONE) {
        updateDrag();
    }

    // Si estamos pintando tiles, pintar en la posición actual del ratón
    if (painting_ && brush_tile_ != NO_BRUSH) {
        int tile_index = (mouse_tile_y_ * 32) + mouse_tile_x_;
        if (tile_index >= 0 && tile_index < static_cast<int>(room_data_.tile_map.size())) {
            if (room_data_.tile_map[tile_index] != brush_tile_) {
                room_data_.tile_map[tile_index] = brush_tile_;
                room_->setTile(tile_index, brush_tile_);
            }
        }
    }

    // Actualizar la barra de estado
    updateStatusBarInfo();
    if (statusbar_) {
        statusbar_->update(delta_time);
    }
}

// Renderiza el editor
void MapEditor::render() {
    // El tilemap ya ha sido renderizado por Game::renderPlaying() antes de llamar aquí

    // Grid (debajo de todo)
    if (settings_.grid) {
        renderGrid();
    }

    // Renderizar los marcadores de boundaries y líneas de ruta (debajo de los sprites)
    renderEnemyBoundaries();

    // Renderizar entidades normales: enemigos (animados en posición inicial), items, jugador
    room_->renderEnemies();
    room_->renderItems();
    player_->render();

    // Renderizar highlight de selección (encima de los sprites)
    renderSelectionHighlight();

    // Tile picker o mini mapa (encima de todo en el play area)
    if (tile_picker_.isOpen()) {
        tile_picker_.render();
    } else if (mini_map_visible_ && mini_map_) {
        mini_map_->render(room_path_);
    }

    // Renderizar barra de estado del editor (reemplaza al scoreboard)
    if (statusbar_) {
        statusbar_->render();
    }
}

// Maneja eventos del editor
void MapEditor::handleEvent(const SDL_Event& event) {  // NOLINT(readability-function-cognitive-complexity)
    // Si el tile picker está abierto, los eventos van a él
    if (tile_picker_.isOpen()) {
        tile_picker_.handleEvent(event);
        return;
    }

    // Si el mini mapa está visible, delegar eventos (ESC o M para cerrar)
    if (mini_map_visible_ && mini_map_) {
        if (event.type == SDL_EVENT_KEY_DOWN &&
            (event.key.key == SDLK_ESCAPE || event.key.key == SDLK_M) &&
            static_cast<int>(event.key.repeat) == 0) {
            mini_map_visible_ = false;
            return;
        }
        mini_map_->handleEvent(event, room_path_);
        return;
    }

    // ESC: desactivar brush
    if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_ESCAPE && brush_tile_ != NO_BRUSH) {
        brush_tile_ = NO_BRUSH;
        return;
    }

    // E: toggle borrador
    if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_E && static_cast<int>(event.key.repeat) == 0) {
        brush_tile_ = (brush_tile_ == ERASER_BRUSH) ? NO_BRUSH : ERASER_BRUSH;
        return;
    }

    // M: toggle mini mapa
    if (event.type == SDL_EVENT_KEY_DOWN && event.key.key == SDLK_M && static_cast<int>(event.key.repeat) == 0) {
        toggleMiniMap();
        return;
    }

    // Click derecho: abrir TilePicker del mapa
    if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_RIGHT) {
        // Deseleccionar entidades
        selected_enemy_ = -1;
        selected_item_ = -1;

        // Tile bajo el cursor como tile actual del picker
        int tile_index = (mouse_tile_y_ * 32) + mouse_tile_x_;
        int current = (tile_index >= 0 && tile_index < static_cast<int>(room_data_.tile_map.size()))
            ? room_data_.tile_map[tile_index]
            : -1;

        tile_picker_.on_select = [this](int tile) {
            brush_tile_ = tile;
        };
        tile_picker_.open(room_->getTileSetFile(), current, stringToColor(room_data_.bg_color));
        return;
    }

    // Click izquierdo
    if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN && event.button.button == SDL_BUTTON_LEFT) {
        // Si hay brush activo y no hacemos hit en ninguna entidad → pintar
        if (brush_tile_ != NO_BRUSH) {
            // Comprobar si hay hit en alguna entidad primero
            bool hit_entity = false;
            SDL_FRect player_rect = player_->getRect();
            if (pointInRect(mouse_game_x_, mouse_game_y_, player_rect)) { hit_entity = true; }

            if (!hit_entity) {
                auto* enemy_mgr = room_->getEnemyManager();
                for (int i = 0; i < enemy_mgr->getCount() && !hit_entity; ++i) {
                    if (pointInRect(mouse_game_x_, mouse_game_y_, enemy_mgr->getEnemy(i)->getRect())) { hit_entity = true; }
                }
            }
            if (!hit_entity) {
                auto* item_mgr = room_->getItemManager();
                for (int i = 0; i < item_mgr->getCount() && !hit_entity; ++i) {
                    if (pointInRect(mouse_game_x_, mouse_game_y_, item_mgr->getItem(i)->getCollider())) { hit_entity = true; }
                }
            }

            if (!hit_entity) {
                // Pintar tile y entrar en modo painting
                painting_ = true;
                int tile_index = (mouse_tile_y_ * 32) + mouse_tile_x_;
                if (tile_index >= 0 && tile_index < static_cast<int>(room_data_.tile_map.size())) {
                    room_data_.tile_map[tile_index] = brush_tile_;
                    room_->setTile(tile_index, brush_tile_);
                }
                return;
            }
        }
        handleMouseDown(mouse_game_x_, mouse_game_y_);
    } else if (event.type == SDL_EVENT_MOUSE_BUTTON_UP && event.button.button == SDL_BUTTON_LEFT) {
        if (painting_) {
            painting_ = false;
            autosave();
        } else {
            handleMouseUp();
        }
    }
}

// Procesa click del ratón: hit test + inicio de drag
void MapEditor::handleMouseDown(float game_x, float game_y) {
    // Prioridad de hit test: jugador → enemigos (initial) → enemigos (boundaries) → items

    // Helper para iniciar drag
    auto start_drag = [&](DragTarget target, int index, float entity_x, float entity_y) {
        drag_.target = target;
        drag_.index = index;
        drag_.offset_x = game_x - entity_x;
        drag_.offset_y = game_y - entity_y;
        drag_.snap_x = entity_x;
        drag_.snap_y = entity_y;
        drag_.moved = false;
    };

    // 1. Hit test sobre el jugador (8x16)
    SDL_FRect player_rect = player_->getRect();
    if (pointInRect(game_x, game_y, player_rect)) {
        start_drag(DragTarget::PLAYER, -1, player_rect.x, player_rect.y);
        return;
    }

    // 2. Hit test sobre enemigos: posición inicial (usan el rect del sprite vivo)
    auto* enemy_mgr = room_->getEnemyManager();
    for (int i = 0; i < enemy_mgr->getCount(); ++i) {
        SDL_FRect enemy_rect = enemy_mgr->getEnemy(i)->getRect();
        if (pointInRect(game_x, game_y, enemy_rect)) {
            start_drag(DragTarget::ENEMY_INITIAL, i, enemy_rect.x, enemy_rect.y);
            return;
        }
    }

    // 3. Hit test sobre boundaries de enemigos (rectángulos 8x8 en las posiciones de room_data_)
    for (int i = 0; i < static_cast<int>(room_data_.enemies.size()); ++i) {
        const auto& ed = room_data_.enemies[i];
        constexpr auto SZ = static_cast<float>(Tile::SIZE);

        SDL_FRect b1_rect = {.x = static_cast<float>(ed.x1), .y = static_cast<float>(ed.y1), .w = SZ, .h = SZ};
        if (pointInRect(game_x, game_y, b1_rect)) {
            start_drag(DragTarget::ENEMY_BOUND1, i, b1_rect.x, b1_rect.y);
            return;
        }

        SDL_FRect b2_rect = {.x = static_cast<float>(ed.x2), .y = static_cast<float>(ed.y2), .w = SZ, .h = SZ};
        if (pointInRect(game_x, game_y, b2_rect)) {
            start_drag(DragTarget::ENEMY_BOUND2, i, b2_rect.x, b2_rect.y);
            return;
        }
    }

    // 4. Hit test sobre items (8x8)
    auto* item_mgr = room_->getItemManager();
    for (int i = 0; i < item_mgr->getCount(); ++i) {
        SDL_FRect item_rect = item_mgr->getItem(i)->getCollider();
        if (pointInRect(game_x, game_y, item_rect)) {
            start_drag(DragTarget::ITEM, i, item_rect.x, item_rect.y);
            return;
        }
    }

    // Click en el fondo: deseleccionar todo
    selected_enemy_ = -1;
    selected_item_ = -1;
}

// Procesa soltar el ratón: commit del drag
void MapEditor::handleMouseUp() {  // NOLINT(readability-function-cognitive-complexity)
    if (drag_.target == DragTarget::NONE) { return; }

    const int IDX = drag_.index;

    // Si no se movió: fue un click → seleccionar/deseleccionar
    if (!drag_.moved) {
        if (drag_.target == DragTarget::ENEMY_INITIAL) {
            selected_enemy_ = (selected_enemy_ == IDX) ? -1 : IDX;
            selected_item_ = -1;
        } else if (drag_.target == DragTarget::ITEM) {
            selected_item_ = (selected_item_ == IDX) ? -1 : IDX;
            selected_enemy_ = -1;
        } else {
            selected_enemy_ = -1;
            selected_item_ = -1;
        }
        drag_ = {};
        return;
    }

    // Hubo movimiento: commit del drag
    const int SNAP_X = static_cast<int>(drag_.snap_x);
    const int SNAP_Y = static_cast<int>(drag_.snap_y);
    bool changed = false;

    switch (drag_.target) {
        case DragTarget::PLAYER:
            player_->setDebugPosition(drag_.snap_x, drag_.snap_y);
            player_->finalizeDebugTeleport();
            break;

        case DragTarget::ENEMY_INITIAL:
            if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
                room_data_.enemies[IDX].x = drag_.snap_x;
                room_data_.enemies[IDX].y = drag_.snap_y;
                room_->getEnemyManager()->getEnemy(IDX)->resetToInitialPosition(room_data_.enemies[IDX]);
                selected_enemy_ = IDX;  // Seleccionar el enemigo arrastrado
                changed = true;
            }
            break;

        case DragTarget::ENEMY_BOUND1:
            if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
                room_data_.enemies[IDX].x1 = SNAP_X;
                room_data_.enemies[IDX].y1 = SNAP_Y;
                selected_enemy_ = IDX;
                changed = true;
            }
            break;

        case DragTarget::ENEMY_BOUND2:
            if (IDX >= 0 && IDX < static_cast<int>(room_data_.enemies.size())) {
                room_data_.enemies[IDX].x2 = SNAP_X;
                room_data_.enemies[IDX].y2 = SNAP_Y;
                selected_enemy_ = IDX;
                changed = true;
            }
            break;

        case DragTarget::ITEM:
            if (IDX >= 0 && IDX < room_->getItemManager()->getCount()) {
                room_->getItemManager()->getItem(IDX)->setPosition(drag_.snap_x, drag_.snap_y);
                selected_item_ = IDX;
                selected_enemy_ = -1;
                changed = true;
            }
            break;

        case DragTarget::NONE:
            break;
    }

    if (changed) { autosave(); }
    drag_ = {};
}

// Actualiza la posición snapped durante el drag
void MapEditor::updateDrag() {
    float raw_x = mouse_game_x_ - drag_.offset_x;
    float raw_y = mouse_game_y_ - drag_.offset_y;

    float new_snap_x = snapToGrid(raw_x);
    float new_snap_y = snapToGrid(raw_y);

    // Detectar si hubo movimiento real (el snap cambió respecto al inicio)
    if (new_snap_x != drag_.snap_x || new_snap_y != drag_.snap_y) {
        drag_.moved = true;
    }

    drag_.snap_x = new_snap_x;
    drag_.snap_y = new_snap_y;

    // Mientras arrastramos, mover la entidad visualmente a la posición snapped
    switch (drag_.target) {
        case DragTarget::PLAYER:
            player_->setDebugPosition(drag_.snap_x, drag_.snap_y);
            break;

        case DragTarget::ENEMY_INITIAL:
            if (drag_.index >= 0 && drag_.index < room_->getEnemyManager()->getCount()) {
                // Mover el sprite vivo del enemigo durante el arrastre
                auto& enemy = room_->getEnemyManager()->getEnemy(drag_.index);
                Enemy::Data temp_data = room_data_.enemies[drag_.index];
                temp_data.x = drag_.snap_x;
                temp_data.y = drag_.snap_y;
                enemy->resetToInitialPosition(temp_data);
            }
            break;

        case DragTarget::ENEMY_BOUND1:
            // Los boundaries se actualizan visualmente en renderEnemyBoundaries() via drag_.snap
            break;

        case DragTarget::ENEMY_BOUND2:
            break;

        case DragTarget::ITEM:
            if (drag_.index >= 0 && drag_.index < room_->getItemManager()->getCount()) {
                room_->getItemManager()->getItem(drag_.index)->setPosition(drag_.snap_x, drag_.snap_y);
            }
            break;

        case DragTarget::NONE:
            break;
    }
}

// Dibuja highlight del elemento seleccionado/arrastrado
void MapEditor::renderSelectionHighlight() {
    auto game_surface = Screen::get()->getRendererSurface();
    if (!game_surface) { return; }

    constexpr auto SZ = static_cast<float>(Tile::SIZE);

    // Highlight del enemigo seleccionado (persistente, color bright_green)
    if (selected_enemy_ >= 0 && selected_enemy_ < room_->getEnemyManager()->getCount()) {
        SDL_FRect enemy_rect = room_->getEnemyManager()->getEnemy(selected_enemy_)->getRect();
        SDL_FRect border = {
            .x = enemy_rect.x - 1,
            .y = enemy_rect.y - 1,
            .w = enemy_rect.w + 2,
            .h = enemy_rect.h + 2};
        game_surface->drawRectBorder(&border, stringToColor("bright_green"));
    }

    // Highlight del item seleccionado (persistente, color bright_green)
    if (selected_item_ >= 0 && selected_item_ < room_->getItemManager()->getCount()) {
        SDL_FRect item_rect = room_->getItemManager()->getItem(selected_item_)->getCollider();
        SDL_FRect border = {
            .x = item_rect.x - 1,
            .y = item_rect.y - 1,
            .w = item_rect.w + 2,
            .h = item_rect.h + 2};
        game_surface->drawRectBorder(&border, stringToColor("bright_green"));
    }

    // Highlight del drag activo (temporal, color bright_white)
    if (drag_.target == DragTarget::NONE || !drag_.moved) { return; }

    const Uint8 DRAG_COLOR = stringToColor("bright_white");
    SDL_FRect highlight_rect{};

    switch (drag_.target) {
        case DragTarget::PLAYER:
            highlight_rect = player_->getRect();
            break;
        case DragTarget::ENEMY_INITIAL:
            if (drag_.index >= 0 && drag_.index < room_->getEnemyManager()->getCount()) {
                highlight_rect = room_->getEnemyManager()->getEnemy(drag_.index)->getRect();
            }
            break;
        case DragTarget::ENEMY_BOUND1:
        case DragTarget::ENEMY_BOUND2:
            highlight_rect = {.x = drag_.snap_x, .y = drag_.snap_y, .w = SZ, .h = SZ};
            break;
        case DragTarget::ITEM:
            if (drag_.index >= 0 && drag_.index < room_->getItemManager()->getCount()) {
                highlight_rect = room_->getItemManager()->getItem(drag_.index)->getCollider();
            }
            break;
        case DragTarget::NONE:
            return;
    }

    SDL_FRect border = {
        .x = highlight_rect.x - 1,
        .y = highlight_rect.y - 1,
        .w = highlight_rect.w + 2,
        .h = highlight_rect.h + 2};
    game_surface->drawRectBorder(&border, DRAG_COLOR);
}

// Alinea un valor a la cuadrícula de 8x8
auto MapEditor::snapToGrid(float value) -> float {
    return std::round(value / static_cast<float>(Tile::SIZE)) * static_cast<float>(Tile::SIZE);
}

// Hit test: punto dentro de rectángulo
auto MapEditor::pointInRect(float px, float py, const SDL_FRect& rect) -> bool {
    return px >= rect.x && px < rect.x + rect.w && py >= rect.y && py < rect.y + rect.h;
}

// Dibuja marcadores de boundaries y líneas de ruta para los enemigos
void MapEditor::renderEnemyBoundaries() {
    auto game_surface = Screen::get()->getRendererSurface();
    if (!game_surface) { return; }

    const Uint8 COLOR_BOUND1 = stringToColor("bright_cyan");
    const Uint8 COLOR_BOUND2 = stringToColor("bright_yellow");
    const Uint8 COLOR_ROUTE = stringToColor("bright_white");

    for (int i = 0; i < static_cast<int>(room_data_.enemies.size()); ++i) {
        const auto& enemy = room_data_.enemies[i];
        constexpr float HALF = Tile::SIZE / 2.0F;

        // Posiciones base (pueden estar siendo arrastradas)
        float init_x = enemy.x;
        float init_y = enemy.y;
        auto b1_x = static_cast<float>(enemy.x1);
        auto b1_y = static_cast<float>(enemy.y1);
        auto b2_x = static_cast<float>(enemy.x2);
        auto b2_y = static_cast<float>(enemy.y2);

        // Si estamos arrastrando una boundary de este enemigo, usar la posición snapped
        if (drag_.index == i) {
            if (drag_.target == DragTarget::ENEMY_BOUND1) {
                b1_x = drag_.snap_x;
                b1_y = drag_.snap_y;
            } else if (drag_.target == DragTarget::ENEMY_BOUND2) {
                b2_x = drag_.snap_x;
                b2_y = drag_.snap_y;
            } else if (drag_.target == DragTarget::ENEMY_INITIAL) {
                init_x = drag_.snap_x;
                init_y = drag_.snap_y;
            }
        }

        // Dibujar líneas de ruta
        game_surface->drawLine(b1_x + HALF, b1_y + HALF, init_x + HALF, init_y + HALF, COLOR_ROUTE);
        game_surface->drawLine(init_x + HALF, init_y + HALF, b2_x + HALF, b2_y + HALF, COLOR_ROUTE);

        // Marcadores en las boundaries
        renderBoundaryMarker(b1_x, b1_y, COLOR_BOUND1);
        renderBoundaryMarker(b2_x, b2_y, COLOR_BOUND2);
    }
}

// Dibuja un marcador de boundary (rectángulo pequeño) en una posición
void MapEditor::renderBoundaryMarker(float x, float y, Uint8 color) {
    auto game_surface = Screen::get()->getRendererSurface();
    if (!game_surface) { return; }

    SDL_FRect marker = {.x = x, .y = y, .w = static_cast<float>(Tile::SIZE), .h = static_cast<float>(Tile::SIZE)};
    game_surface->drawRectBorder(&marker, color);
}

// Convierte coordenadas de ventana a coordenadas de juego y tile
void MapEditor::updateMousePosition() {
    float mouse_x = 0.0F;
    float mouse_y = 0.0F;
    SDL_GetMouseState(&mouse_x, &mouse_y);

    float render_x = 0.0F;
    float render_y = 0.0F;
    SDL_RenderCoordinatesFromWindow(Screen::get()->getRenderer(), mouse_x, mouse_y, &render_x, &render_y);

    SDL_FRect dst_rect = Screen::get()->getGameSurfaceDstRect();
    mouse_game_x_ = render_x - dst_rect.x;
    mouse_game_y_ = render_y - dst_rect.y;

    // Convertir a coordenadas de tile (clampeadas al área de juego)
    mouse_tile_x_ = static_cast<int>(mouse_game_x_) / Tile::SIZE;
    mouse_tile_y_ = static_cast<int>(mouse_game_y_) / Tile::SIZE;

    // Clampear a los límites del mapa
    mouse_tile_x_ = std::max(mouse_tile_x_, 0);
    if (mouse_tile_x_ >= PlayArea::WIDTH / Tile::SIZE) { mouse_tile_x_ = PlayArea::WIDTH / Tile::SIZE - 1; }
    mouse_tile_y_ = std::max(mouse_tile_y_, 0);
    if (mouse_tile_y_ >= PlayArea::HEIGHT / Tile::SIZE) { mouse_tile_y_ = PlayArea::HEIGHT / Tile::SIZE - 1; }
}

// Actualiza la información de la barra de estado
void MapEditor::updateStatusBarInfo() {  // NOLINT(readability-function-cognitive-complexity)
    if (!statusbar_) { return; }

    statusbar_->setMouseTile(mouse_tile_x_, mouse_tile_y_);

    std::string line2;
    std::string line3;
    std::string line5;

    // Helper para conexiones compactas
    auto conn = [](const std::string& r) -> std::string {
        if (r == "0" || r.empty()) { return "-"; }
        return r.substr(0, r.find('.'));
    };

    // Info de drag activo (línea 5, junto a tile coords)
    if (drag_.target != DragTarget::NONE && drag_.moved) {
        switch (drag_.target) {
            case DragTarget::PLAYER:
                line5 = "dragging: player";
                break;
            case DragTarget::ENEMY_INITIAL:
                line5 = "dragging: enemy " + std::to_string(drag_.index);
                break;
            case DragTarget::ENEMY_BOUND1:
                line5 = "dragging: e" + std::to_string(drag_.index) + " bound1";
                break;
            case DragTarget::ENEMY_BOUND2:
                line5 = "dragging: e" + std::to_string(drag_.index) + " bound2";
                break;
            case DragTarget::ITEM:
                line5 = "dragging: item " + std::to_string(drag_.index);
                break;
            case DragTarget::NONE:
                break;
        }
    }

    // Líneas 2-3 según selección
    if (selected_enemy_ >= 0 && selected_enemy_ < static_cast<int>(room_data_.enemies.size())) {
        // Enemigo seleccionado
        const auto& e = room_data_.enemies[selected_enemy_];
        std::string anim = e.animation_path;
        auto dot = anim.rfind('.');
        if (dot != std::string::npos) { anim.resize(dot); }

        line2 = "enemy " + std::to_string(selected_enemy_) + ": " + anim + "  " + e.color;
        line3 = "vx:" + std::to_string(static_cast<int>(e.vx)) +
            " vy:" + std::to_string(static_cast<int>(e.vy));
        if (e.flip) { line3 += " flip"; }
        if (e.mirror) { line3 += " mirror"; }
    } else if (selected_item_ >= 0 && selected_item_ < static_cast<int>(room_data_.items.size())) {
        // Item seleccionado
        const auto& it = room_data_.items[selected_item_];
        line2 = "item " + std::to_string(selected_item_) + ": tile=" + std::to_string(it.tile) +
            "  counter=" + std::to_string(it.counter);
        line3 = "tileset: " + it.tile_set_file;
    } else {
        // Propiedades de la habitación
        std::string conv = "none";
        if (room_data_.conveyor_belt_direction < 0) {
            conv = "left";
        } else if (room_data_.conveyor_belt_direction > 0) {
            conv = "right";
        }

        line2 = "bg:" + room_data_.bg_color + "  brd:" + room_data_.border_color + "  conv:" + conv;
        line3 = "u:" + conn(room_data_.upper_room) + " d:" + conn(room_data_.lower_room) +
            " l:" + conn(room_data_.left_room) + " r:" + conn(room_data_.right_room) +
            "  itm:" + room_data_.item_color1 + "/" + room_data_.item_color2;
    }

    // Línea 4: brush activo
    std::string line4;
    if (brush_tile_ == ERASER_BRUSH) {
        line4 = "brush: eraser (e)";
    } else if (brush_tile_ != NO_BRUSH) {
        line4 = "brush: tile " + std::to_string(brush_tile_);
    }

    statusbar_->setLine2(line2);
    statusbar_->setLine3(line3);
    statusbar_->setLine4(line4);
    statusbar_->setLine5(line5);

    // Actualizar el prompt de la consola según la selección
    if (selected_enemy_ >= 0) {
        Console::get()->setPrompt("enemy " + std::to_string(selected_enemy_) + "> ");
    } else if (selected_item_ >= 0) {
        Console::get()->setPrompt("item " + std::to_string(selected_item_) + "> ");
    } else {
        Console::get()->setPrompt("room> ");
    }
}

// ¿Hay un enemigo seleccionado?
auto MapEditor::hasSelectedEnemy() const -> bool {
    return selected_enemy_ >= 0 && selected_enemy_ < static_cast<int>(room_data_.enemies.size());
}

// Devuelve las propiedades válidas de SET según la selección actual
auto MapEditor::getSetCompletions() const -> std::vector<std::string> {
    if (hasSelectedEnemy()) {
        return {"ANIMATION", "COLOR", "VX", "VY", "FLIP", "MIRROR"};
    }
    if (hasSelectedItem()) {
        return {"TILE", "COUNTER"};
    }
    // Room
    return {"BGCOLOR", "BORDER", "ITEMCOLOR1", "ITEMCOLOR2", "CONVEYOR", "TILESET", "UP", "DOWN", "LEFT", "RIGHT"};
}

// Modifica una propiedad del enemigo seleccionado
auto MapEditor::setEnemyProperty(const std::string& property, const std::string& value) -> std::string {  // NOLINT(readability-function-cognitive-complexity)
    if (!active_) { return "Editor not active"; }
    if (!hasSelectedEnemy()) { return "No enemy selected"; }

    auto& enemy = room_data_.enemies[selected_enemy_];

    if (property == "ANIMATION") {
        std::string anim = toLower(value);
        if (anim.find('.') == std::string::npos) { anim += ".yaml"; }

        // Intentar recrear el enemigo con la nueva animación
        std::string old_anim = enemy.animation_path;
        enemy.animation_path = anim;
        try {
            auto* enemy_mgr = room_->getEnemyManager();
            enemy_mgr->getEnemy(selected_enemy_) = std::make_shared<Enemy>(enemy);
        } catch (const std::exception& e) {
            enemy.animation_path = old_anim;  // Restaurar si falla
            return std::string("Error: ") + e.what();
        }

        autosave();
        return "animation: " + anim;
    }

    if (property == "COLOR") {
        std::string color = toLower(value);

        // Intentar recrear el enemigo con el nuevo color
        std::string old_color = enemy.color;
        enemy.color = color;
        try {
            auto* enemy_mgr = room_->getEnemyManager();
            enemy_mgr->getEnemy(selected_enemy_) = std::make_shared<Enemy>(enemy);
        } catch (const std::exception& e) {
            enemy.color = old_color;
            return std::string("Error: ") + e.what();
        }

        autosave();
        return "color: " + color;
    }

    if (property == "VX") {
        try {
            enemy.vx = std::stof(value);
        } catch (...) { return "Invalid value: " + value; }
        enemy.vy = 0.0F;  // No se permiten velocidades en los dos ejes

        room_->getEnemyManager()->getEnemy(selected_enemy_)->resetToInitialPosition(enemy);
        autosave();
        return "vx: " + std::to_string(static_cast<int>(enemy.vx)) + " vy: 0";
    }

    if (property == "VY") {
        try {
            enemy.vy = std::stof(value);
        } catch (...) { return "Invalid value: " + value; }
        enemy.vx = 0.0F;  // No se permiten velocidades en los dos ejes

        room_->getEnemyManager()->getEnemy(selected_enemy_)->resetToInitialPosition(enemy);
        autosave();
        return "vy: " + std::to_string(static_cast<int>(enemy.vy)) + " vx: 0";
    }

    if (property == "FLIP") {
        enemy.flip = (value == "ON" || value == "TRUE" || value == "1");
        if (enemy.flip) { enemy.mirror = false; }  // Mutuamente excluyentes

        // Recrear el enemigo (flip/mirror se aplican en el constructor)
        try {
            auto* enemy_mgr = room_->getEnemyManager();
            enemy_mgr->getEnemy(selected_enemy_) = std::make_shared<Enemy>(enemy);
        } catch (const std::exception& e) {
            return std::string("Error: ") + e.what();
        }

        autosave();
        return std::string("flip: ") + (enemy.flip ? "on" : "off");
    }

    if (property == "MIRROR") {
        enemy.mirror = (value == "ON" || value == "TRUE" || value == "1");
        if (enemy.mirror) { enemy.flip = false; }  // Mutuamente excluyentes

        // Recrear el enemigo (flip/mirror se aplican en el constructor)
        try {
            auto* enemy_mgr = room_->getEnemyManager();
            enemy_mgr->getEnemy(selected_enemy_) = std::make_shared<Enemy>(enemy);
        } catch (const std::exception& e) {
            return std::string("Error: ") + e.what();
        }

        autosave();
        return std::string("mirror: ") + (enemy.mirror ? "on" : "off");
    }

    return "Unknown property: " + property + " (use: animation, color, vx, vy, flip, mirror)";
}

// Crea un nuevo enemigo con valores por defecto, centrado en la habitación
auto MapEditor::addEnemy() -> std::string {
    if (!active_) { return "Editor not active"; }

    constexpr float CENTER_X = PlayArea::CENTER_X;
    constexpr float CENTER_Y = PlayArea::CENTER_Y;
    constexpr float ROUTE_HALF = 2.0F * Tile::SIZE;  // 2 tiles a cada lado (5 tiles total con el centro)

    Enemy::Data new_enemy;
    new_enemy.animation_path = "spider.yaml";
    new_enemy.x = CENTER_X;
    new_enemy.y = CENTER_Y;
    new_enemy.vx = 24.0F;
    new_enemy.vy = 0.0F;
    new_enemy.x1 = static_cast<int>(CENTER_X - ROUTE_HALF);
    new_enemy.y1 = static_cast<int>(CENTER_Y);
    new_enemy.x2 = static_cast<int>(CENTER_X + ROUTE_HALF);
    new_enemy.y2 = static_cast<int>(CENTER_Y);
    new_enemy.color = "white";
    new_enemy.flip = true;
    new_enemy.frame = -1;

    // Añadir a los datos y crear el sprite vivo
    room_data_.enemies.push_back(new_enemy);
    room_->getEnemyManager()->addEnemy(std::make_shared<Enemy>(new_enemy));

    // Seleccionar el nuevo enemigo
    selected_enemy_ = static_cast<int>(room_data_.enemies.size()) - 1;

    autosave();
    return "Added enemy " + std::to_string(selected_enemy_);
}

// Elimina el enemigo seleccionado
auto MapEditor::deleteEnemy() -> std::string {
    if (!active_) { return "Editor not active"; }
    if (!hasSelectedEnemy()) { return "No enemy selected"; }

    const int IDX = selected_enemy_;

    // Eliminar de los datos
    room_data_.enemies.erase(room_data_.enemies.begin() + IDX);

    // Recrear todos los enemigos vivos (los índices cambian al borrar)
    auto* enemy_mgr = room_->getEnemyManager();
    enemy_mgr->clear();
    for (const auto& enemy_data : room_data_.enemies) {
        enemy_mgr->addEnemy(std::make_shared<Enemy>(enemy_data));
    }

    selected_enemy_ = -1;
    autosave();
    return "Deleted enemy " + std::to_string(IDX);
}

// Duplica el enemigo seleccionado (lo pone un tile a la derecha)
auto MapEditor::duplicateEnemy() -> std::string {
    if (!active_) { return "Editor not active"; }
    if (!hasSelectedEnemy()) { return "No enemy selected"; }

    // Copiar datos del enemigo seleccionado
    Enemy::Data copy = room_data_.enemies[selected_enemy_];

    // Desplazar un tile a la derecha
    copy.x += Tile::SIZE;
    copy.x1 += Tile::SIZE;
    copy.x2 += Tile::SIZE;

    // Añadir y crear sprite vivo
    room_data_.enemies.push_back(copy);
    room_->getEnemyManager()->addEnemy(std::make_shared<Enemy>(copy));

    // Seleccionar el nuevo enemigo
    selected_enemy_ = static_cast<int>(room_data_.enemies.size()) - 1;

    autosave();
    return "Duplicated as enemy " + std::to_string(selected_enemy_);
}

// Modifica una propiedad de la habitación
auto MapEditor::setRoomProperty(const std::string& property, const std::string& value) -> std::string {  // NOLINT(readability-function-cognitive-complexity)
    if (!active_) { return "Editor not active"; }

    std::string val = toLower(value);

    if (property == "BGCOLOR") {
        room_data_.bg_color = val;
        room_->setBgColor(val);
        autosave();
        return "bgcolor: " + val;
    }

    if (property == "BORDER") {
        room_data_.border_color = val;
        Screen::get()->setBorderColor(stringToColor(val));
        autosave();
        return "border: " + val;
    }

    if (property == "ITEMCOLOR1") {
        room_data_.item_color1 = val;
        room_->setItemColors(room_data_.item_color1, room_data_.item_color2);
        autosave();
        return "itemcolor1: " + val;
    }

    if (property == "ITEMCOLOR2") {
        room_data_.item_color2 = val;
        room_->setItemColors(room_data_.item_color1, room_data_.item_color2);
        autosave();
        return "itemcolor2: " + val;
    }

    if (property == "CONVEYOR") {
        if (val == "left") {
            room_data_.conveyor_belt_direction = -1;
        } else if (val == "right") {
            room_data_.conveyor_belt_direction = 1;
        } else {
            room_data_.conveyor_belt_direction = 0;
            val = "none";
        }
        autosave();
        return "conveyor: " + val;
    }

    if (property == "TILESET") {
        std::string tileset = val;
        if (tileset.find('.') == std::string::npos) { tileset += ".gif"; }
        room_data_.tile_set_file = tileset;
        autosave();
        return "tileset: " + tileset;
    }

    // Conexiones: UP, DOWN, LEFT, RIGHT
    if (property == "UP" || property == "DOWN" || property == "LEFT" || property == "RIGHT") {
        std::string connection = "0";
        if (val != "0" && val != "null" && val != "none") {
            try {
                int num = std::stoi(val);
                char buf[16];
                std::snprintf(buf, sizeof(buf), "%02d.yaml", num);
                connection = buf;
            } catch (...) {
                connection = val;
                if (connection.find('.') == std::string::npos) { connection += ".yaml"; }
            }
        }

        // Dirección opuesta para la conexión recíproca
        std::string opposite;
        std::string* our_field = nullptr;
        if (property == "UP") {
            opposite = "lower_room";
            our_field = &room_data_.upper_room;
        }
        if (property == "DOWN") {
            opposite = "upper_room";
            our_field = &room_data_.lower_room;
        }
        if (property == "LEFT") {
            opposite = "right_room";
            our_field = &room_data_.left_room;
        }
        if (property == "RIGHT") {
            opposite = "left_room";
            our_field = &room_data_.right_room;
        }

        // Si había una conexión anterior, romper la recíproca de la otra room
        if (our_field != nullptr && *our_field != "0" && !our_field->empty()) {
            auto old_other = Resource::Cache::get()->getRoom(*our_field);
            if (old_other) {
                if (opposite == "upper_room") {
                    old_other->upper_room = "0";
                } else if (opposite == "lower_room") {
                    old_other->lower_room = "0";
                } else if (opposite == "left_room") {
                    old_other->left_room = "0";
                } else if (opposite == "right_room") {
                    old_other->right_room = "0";
                }
                // Guardar la otra room
                std::string other_path = Resource::List::get()->get(*our_field);
                if (!other_path.empty()) {
                    auto other_yaml = RoomSaver::loadYAML(other_path);
                    RoomSaver::saveYAML(other_path, other_yaml, *old_other);
                }
            }
        }

        // Aplicar la nueva conexión
        if (our_field != nullptr) { *our_field = connection; }

        // Crear la conexión recíproca en la otra room
        if (connection != "0") {
            auto other = Resource::Cache::get()->getRoom(connection);
            if (other) {
                if (opposite == "upper_room") {
                    other->upper_room = room_path_;
                } else if (opposite == "lower_room") {
                    other->lower_room = room_path_;
                } else if (opposite == "left_room") {
                    other->left_room = room_path_;
                } else if (opposite == "right_room") {
                    other->right_room = room_path_;
                }
                std::string other_path = Resource::List::get()->get(connection);
                if (!other_path.empty()) {
                    auto other_yaml = RoomSaver::loadYAML(other_path);
                    RoomSaver::saveYAML(other_path, other_yaml, *other);
                }
            }
        }

        autosave();
        return toLower(property) + ": " + connection;
    }

    return "Unknown property: " + property + " (use: bgcolor, border, itemcolor1, itemcolor2, conveyor, tileset, up, down, left, right)";
}

// Crea una nueva habitación
auto MapEditor::createNewRoom(const std::string& direction) -> std::string {  // NOLINT(readability-function-cognitive-complexity)
    if (!active_) { return "Editor not active"; }

    // Validar dirección si se proporcionó
    if (!direction.empty() && direction != "LEFT" && direction != "RIGHT" && direction != "UP" && direction != "DOWN") {
        return "usage: room new [left|right|up|down]";
    }

    // Comprobar que no hay ya una room en esa dirección
    if (!direction.empty()) {
        const std::string* existing = nullptr;
        if (direction == "UP") {
            existing = &room_data_.upper_room;
        } else if (direction == "DOWN") {
            existing = &room_data_.lower_room;
        } else if (direction == "LEFT") {
            existing = &room_data_.left_room;
        } else if (direction == "RIGHT") {
            existing = &room_data_.right_room;
        }
        if (existing != nullptr && *existing != "0" && !existing->empty()) {
            return "Already connected " + toLower(direction) + ": " + *existing;
        }
    }

    // Encontrar el primer número libre (reutiliza huecos)
    const auto& rooms = Resource::Cache::get()->getRooms();
    std::set<int> used;
    for (const auto& r : rooms) {
        try {
            used.insert(std::stoi(r.name.substr(0, r.name.find('.'))));
        } catch (...) {}
    }
    int new_num = 1;
    while (used.contains(new_num)) { ++new_num; }
    char name_buf[16];
    std::snprintf(name_buf, sizeof(name_buf), "%02d.yaml", new_num);
    std::string new_name = name_buf;

    // Derivar la ruta de la carpeta de rooms
    std::string ref_path = Resource::List::get()->get(room_path_);
    if (ref_path.empty()) { return "Error: cannot resolve room path"; }
    std::string room_dir = ref_path.substr(0, ref_path.find_last_of("\\/") + 1);
    std::string new_path = room_dir + new_name;

    // Crear Room::Data por defecto con conexión recíproca
    Room::Data new_room;
    new_room.number = std::string(name_buf).substr(0, std::string(name_buf).find('.'));
    new_room.name = "NO_NAME";
    new_room.bg_color = "black";
    new_room.border_color = "magenta";
    new_room.tile_set_file = "standard.gif";
    new_room.item_color1 = "bright_cyan";
    new_room.item_color2 = "yellow";
    new_room.upper_room = "0";
    new_room.lower_room = "0";
    new_room.left_room = "0";
    new_room.right_room = "0";
    new_room.conveyor_belt_direction = 0;
    new_room.tile_map.resize(32 * 16, -1);

    // Conexión recíproca: la nueva room conecta de vuelta a la actual
    if (direction == "UP") {
        new_room.lower_room = room_path_;
    } else if (direction == "DOWN") {
        new_room.upper_room = room_path_;
    } else if (direction == "LEFT") {
        new_room.right_room = room_path_;
    } else if (direction == "RIGHT") {
        new_room.left_room = room_path_;
    }

    // Conexiones del YAML
    auto conn_str = [](const std::string& c) -> std::string { return (c == "0") ? "null" : c; };

    // Crear el YAML
    std::ofstream file(new_path);
    if (!file.is_open()) { return "Error: cannot create " + new_path; }

    file << "# NO_NAME\n";
    file << "room:\n";
    file << "  name_en: \"NO_NAME\"\n";
    file << "  name_ca: \"NO_NAME\"\n";
    file << "  bgColor: black\n";
    file << "  border: magenta\n";
    file << "  tileSetFile: standard.gif\n";
    file << "\n";
    file << "  connections:\n";
    file << "    up: " << conn_str(new_room.upper_room) << "\n";
    file << "    down: " << conn_str(new_room.lower_room) << "\n";
    file << "    left: " << conn_str(new_room.left_room) << "\n";
    file << "    right: " << conn_str(new_room.right_room) << "\n";
    file << "\n";
    file << "  itemColor1: bright_cyan\n";
    file << "  itemColor2: yellow\n";
    file << "\n";
    file << "  conveyorBelt: none\n";
    file << "\n";
    file << "tilemap:\n";
    for (int row = 0; row < 16; ++row) {
        file << "  - [";
        for (int col = 0; col < 32; ++col) {
            file << "-1";
            if (col < 31) { file << ", "; }
        }
        file << "]\n";
    }
    file.close();

    // Registrar en Resource::List (mapa + assets.yaml) y cache
    Resource::List::get()->addAsset(new_path, Resource::List::Type::ROOM);
    Resource::Cache::get()->getRooms().emplace_back(
        RoomResource{.name = new_name, .room = std::make_shared<Room::Data>(new_room)});

    // Conectar la room actual con la nueva (recíproco: ya hecho arriba para la nueva)
    if (direction == "UP") {
        room_data_.upper_room = new_name;
    } else if (direction == "DOWN") {
        room_data_.lower_room = new_name;
    } else if (direction == "LEFT") {
        room_data_.left_room = new_name;
    } else if (direction == "RIGHT") {
        room_data_.right_room = new_name;
    }

    if (!direction.empty()) { autosave(); }

    // Navegar a la nueva room
    reenter_ = true;
    if (GameControl::exit_editor) { GameControl::exit_editor(); }
    if (GameControl::change_room && GameControl::change_room(new_name)) {
        if (GameControl::enter_editor) { GameControl::enter_editor(); }
    }

    return "Created room " + new_name + (direction.empty() ? "" : " (" + toLower(direction) + ")");
}

// Elimina la habitación actual
auto MapEditor::deleteRoom() -> std::string {  // NOLINT(readability-function-cognitive-complexity)
    if (!active_) { return "Editor not active"; }

    std::string deleted_name = room_path_;

    // Buscar una room vecina a la que navegar después de borrar
    std::string target = "0";
    if (room_data_.upper_room != "0" && !room_data_.upper_room.empty()) {
        target = room_data_.upper_room;
    } else if (room_data_.lower_room != "0" && !room_data_.lower_room.empty()) {
        target = room_data_.lower_room;
    } else if (room_data_.left_room != "0" && !room_data_.left_room.empty()) {
        target = room_data_.left_room;
    } else if (room_data_.right_room != "0" && !room_data_.right_room.empty()) {
        target = room_data_.right_room;
    }

    if (target == "0") {
        // Buscar la primera room que no sea esta
        const auto& rooms = Resource::Cache::get()->getRooms();
        auto it = std::ranges::find_if(rooms,
            [&deleted_name](const auto& r) { return r.name != deleted_name; });
        if (it != rooms.end()) {
            target = it->name;
        }
    }
    if (target == "0") { return "Cannot delete: no other room to navigate to"; }

    // Desenlazar todas las conexiones recíprocas
    auto unlink_reciprocal = [&](const std::string& neighbor, const std::string& field_name) {
        if (neighbor == "0" || neighbor.empty()) { return; }
        auto other = Resource::Cache::get()->getRoom(neighbor);
        if (!other) { return; }
        if (field_name == "upper_room") {
            other->upper_room = "0";
        } else if (field_name == "lower_room") {
            other->lower_room = "0";
        } else if (field_name == "left_room") {
            other->left_room = "0";
        } else if (field_name == "right_room") {
            other->right_room = "0";
        }
        // Guardar la otra room
        std::string other_path = Resource::List::get()->get(neighbor);
        if (!other_path.empty()) {
            auto other_yaml = RoomSaver::loadYAML(other_path);
            RoomSaver::saveYAML(other_path, other_yaml, *other);
        }
    };

    unlink_reciprocal(room_data_.upper_room, "lower_room");  // Si nosotros somos su lower
    unlink_reciprocal(room_data_.lower_room, "upper_room");  // Si nosotros somos su upper
    unlink_reciprocal(room_data_.left_room, "right_room");   // Si nosotros somos su right
    unlink_reciprocal(room_data_.right_room, "left_room");   // Si nosotros somos su left

    // Navegar a la room destino antes de borrar
    reenter_ = true;
    if (GameControl::exit_editor) { GameControl::exit_editor(); }
    if (GameControl::change_room) { GameControl::change_room(target); }

    // Borrar el YAML del disco
    std::string yaml_path = Resource::List::get()->get(deleted_name);
    if (!yaml_path.empty()) {
        std::remove(yaml_path.c_str());
    }

    // Quitar del cache
    auto& cache_rooms = Resource::Cache::get()->getRooms();
    std::erase_if(cache_rooms, [&](const RoomResource& r) { return r.name == deleted_name; });

    // Quitar de Resource::List (mapa + assets.yaml)
    Resource::List::get()->removeAsset(deleted_name);

    // Re-entrar al editor en la room destino
    if (GameControl::enter_editor) { GameControl::enter_editor(); }

    return "Deleted room " + deleted_name + ", moved to " + target;
}

// ¿Hay un item seleccionado?
auto MapEditor::hasSelectedItem() const -> bool {
    return selected_item_ >= 0 && selected_item_ < static_cast<int>(room_data_.items.size());
}

// Modifica una propiedad del item seleccionado
auto MapEditor::setItemProperty(const std::string& property, const std::string& value) -> std::string {
    if (!active_) { return "Editor not active"; }
    if (!hasSelectedItem()) { return "No item selected"; }

    auto& item = room_data_.items[selected_item_];

    if (property == "TILE") {
        // Abrir el tile picker visual
        openTilePicker(item.tile_set_file, item.tile);
        return "Select tile...";
    }

    if (property == "COUNTER") {
        try {
            item.counter = std::stoi(value);
        } catch (...) { return "Invalid value: " + value; }
        autosave();
        return "counter: " + std::to_string(item.counter);
    }

    return "Unknown property: " + property + " (use: tile, counter)";
}

// Abre el tile picker para seleccionar un tile
void MapEditor::openTilePicker(const std::string& tileset_name, int current_tile) {
    // Cerrar la consola si está abierta (para que el primer click vaya al picker)
    if (Console::get()->isActive()) {
        Console::get()->toggle();
    }

    tile_picker_.on_select = [this](int tile) {
        if (!hasSelectedItem()) { return; }
        room_data_.items[selected_item_].tile = tile;
        room_->getItemManager()->getItem(selected_item_)->setTile(tile);
        autosave();
    };
    // Pasar color de fondo de la habitación + color de sustitución del item
    int bg = stringToColor(room_data_.bg_color);
    int item_color = stringToColor(room_data_.item_color1);
    tile_picker_.open(tileset_name, current_tile, bg, 1, item_color);
}

// Crea un nuevo item con valores por defecto, centrado en la habitación
auto MapEditor::addItem() -> std::string {
    if (!active_) { return "Editor not active"; }

    Item::Data new_item;
    new_item.tile_set_file = "items.gif";
    new_item.tile = 42;  // Tile por defecto
    new_item.x = PlayArea::CENTER_X;
    new_item.y = PlayArea::CENTER_Y;
    new_item.counter = 0;
    new_item.color1 = stringToColor(room_data_.item_color1);
    new_item.color2 = stringToColor(room_data_.item_color2);

    room_data_.items.push_back(new_item);
    room_->getItemManager()->addItem(std::make_shared<Item>(new_item));

    selected_item_ = static_cast<int>(room_data_.items.size()) - 1;
    selected_enemy_ = -1;

    autosave();
    return "Added item " + std::to_string(selected_item_);
}

// Elimina el item seleccionado
auto MapEditor::deleteItem() -> std::string {
    if (!active_) { return "Editor not active"; }
    if (!hasSelectedItem()) { return "No item selected"; }

    const int IDX = selected_item_;
    room_data_.items.erase(room_data_.items.begin() + IDX);

    // Recrear todos los items (los índices cambian al borrar)
    auto* item_mgr = room_->getItemManager();
    item_mgr->clear();
    for (const auto& item_data : room_data_.items) {
        item_mgr->addItem(std::make_shared<Item>(item_data));
    }

    selected_item_ = -1;
    autosave();
    return "Deleted item " + std::to_string(IDX);
}

// Duplica el item seleccionado (lo pone un tile a la derecha)
auto MapEditor::duplicateItem() -> std::string {
    if (!active_) { return "Editor not active"; }
    if (!hasSelectedItem()) { return "No item selected"; }

    Item::Data copy = room_data_.items[selected_item_];
    copy.x += Tile::SIZE;

    room_data_.items.push_back(copy);
    room_->getItemManager()->addItem(std::make_shared<Item>(copy));

    selected_item_ = static_cast<int>(room_data_.items.size()) - 1;
    selected_enemy_ = -1;

    autosave();
    return "Duplicated as item " + std::to_string(selected_item_);
}

// Elige un color de grid que contraste con el fondo
// Empieza con bright_black (1), si coincide con el bg en la paleta activa, sube índices
static auto pickGridColor(Uint8 bg, const std::shared_ptr<Surface>& surface) -> Uint8 {
    auto grid = static_cast<Uint8>(PaletteColor::BRIGHT_BLACK);
    Uint32 bg_argb = surface->getPaletteColor(bg);

    // Si bright_black es igual al bg, buscar el siguiente color distinto
    while (grid < 15 && surface->getPaletteColor(grid) == bg_argb) {
        grid += 2;  // Saltar de 2 en 2 para mantenerse en tonos discretos
    }

    return grid;
}

// Dibuja la cuadrícula de tiles (líneas de puntos, 1 pixel sí / 1 no)
void MapEditor::renderGrid() const {
    auto game_surface = Screen::get()->getRendererSurface();
    if (!game_surface) { return; }

    const Uint8 COLOR = pickGridColor(stringToColor(room_data_.bg_color), game_surface);

    // Líneas verticales (cada 8 pixels)
    for (int x = Tile::SIZE; x < PlayArea::WIDTH; x += Tile::SIZE) {
        for (int y = 0; y < PlayArea::HEIGHT; y += 2) {
            game_surface->putPixel(x, y, COLOR);
        }
    }

    // Líneas horizontales (cada 8 pixels)
    for (int y = Tile::SIZE; y < PlayArea::HEIGHT; y += Tile::SIZE) {
        for (int x = 0; x < PlayArea::WIDTH; x += 2) {
            game_surface->putPixel(x, y, COLOR);
        }
    }
}

#endif  // _DEBUG
