#include "game/gameplay/room_format.hpp"

#include <algorithm>  // Para std::ranges::copy, transform
#include <exception>  // Para exception
#include <iostream>   // Para cout, cerr
#include <iterator>   // Para std::back_inserter

#include "core/resources/resource_helper.hpp"  // Para Resource::Helper
#include "external/fkyaml_node.hpp"            // Para fkyaml::node
#include "game/gameplay/zone.hpp"              // Para Zone::Data
#include "game/gameplay/zone_manager.hpp"      // Para ZoneManager
#include "utils/defines.hpp"                   // Para Tile::SIZE, Map::WIDTH/HEIGHT
#include "utils/utils.hpp"                     // Para safeStoi

#ifdef _DEBUG
#include <cmath>    // Para std::round
#include <fstream>  // Para ofstream
#include <sstream>  // Para ostringstream
#endif

// ============================================================================
// Helpers no-miembro (parser)
// ============================================================================

namespace {

    // Lee un nodo de color como Uint8 (acepta entero directo o string numérico)
    auto readColorNode(const fkyaml::node& node) -> Uint8 {
        if (node.is_integer()) { return static_cast<Uint8>(node.get_value<int>()); }
        if (node.is_string()) { return static_cast<Uint8>(safeStoi(node.get_value<std::string>(), 0)); }
        return 0;
    }

    // Lee un array 2D de enteros desde un nodo YAML
    auto readTilemap2D(const fkyaml::node& node) -> std::vector<std::vector<int>> {
        std::vector<std::vector<int>> tilemap_2d;
        tilemap_2d.reserve(Map::HEIGHT);

        for (const auto& row_node : node) {
            std::vector<int> row;
            row.reserve(Map::WIDTH);

            std::ranges::transform(row_node, std::back_inserter(row), [](const auto& tile_node) { return tile_node.template get_value<int>(); });

            tilemap_2d.push_back(row);
        }

        return tilemap_2d;
    }

}  // namespace

// ============================================================================
// Conversores básicos
// ============================================================================

auto RoomFormat::convertRoomConnection(const std::string& value) -> std::string {
    if (value == "null" || value.empty()) {
        return "0";
    }
    if (value.size() > 5 && value.substr(value.size() - 5) == ".yaml") {
        return value;
    }
    return value + ".yaml";
}

auto RoomFormat::convertAutoSurface(const fkyaml::node& node) -> int {
    if (node.is_integer()) {
        return node.get_value<int>();
    }
    if (node.is_string()) {
        const auto VALUE = node.get_value<std::string>();
        if (VALUE == "left") { return -1; }
        if (VALUE == "right") { return 1; }
    }
    return 0;
}

auto RoomFormat::flattenTilemap(const std::vector<std::vector<int>>& tilemap_2d) -> std::vector<int> {
    std::vector<int> tilemap_flat;
    tilemap_flat.reserve(static_cast<size_t>(Map::WIDTH) * static_cast<size_t>(Map::HEIGHT));

    for (const auto& row : tilemap_2d) {
        std::ranges::copy(row, std::back_inserter(tilemap_flat));
    }

    return tilemap_flat;
}

// ============================================================================
// Parseo de la sección room
// ============================================================================

void RoomFormat::parseRoomConfig(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name) {
    if (!yaml.contains("room")) {
        return;
    }

    const auto& room_node = yaml["room"];

    // Extract room number from filename (e.g., "001.yaml" → "001")
    room.number = file_name.substr(0, file_name.find_last_of('.'));

    // --- Resolución de zona + overrides (tileSetFile, music) ---
    std::string zone_name;
    if (room_node.contains("zone")) {
        zone_name = room_node["zone"].get_value<std::string>();
    } else {
        std::cerr << "Warning: room " << file_name << " has no 'zone' field, using default\n";
        const Zone::Data* default_zone = ZoneManager::get()->getDefaultZone();
        if (default_zone != nullptr) { zone_name = default_zone->name; }
    }
    room.zone = zone_name;

    const Zone::Data* zone = ZoneManager::get()->getZone(zone_name);
    if (zone == nullptr) {
        std::cerr << "Warning: unknown zone '" << zone_name << "' in " << file_name << ", using default\n";
        zone = ZoneManager::get()->getDefaultZone();
    }

    // tileSetFile: zona, override si está en el yaml
    if (room_node.contains("tileSetFile")) {
        room.tile_set_file = room_node["tileSetFile"].get_value<std::string>();
        room.tile_set_overridden = true;
    } else if (zone != nullptr) {
        room.tile_set_file = zone->tile_set_file;
    }

    // music: zona, override si está en el yaml
    if (room_node.contains("music")) {
        room.music = room_node["music"].get_value<std::string>();
        room.music_overridden = true;
    } else if (zone != nullptr) {
        room.music = zone->music;
    }

    // bgColor: zona, override si está en el yaml
    if (room_node.contains("bgColor")) {
        room.bg_color = readColorNode(room_node["bgColor"]);
        room.bg_color_overridden = true;
    } else if (zone != nullptr) {
        room.bg_color = zone->bg_color;
    }

    // Room connections
    if (room_node.contains("connections")) {
        parseRoomConnections(room_node["connections"], room);
    }
}

void RoomFormat::parseRoomConnections(const fkyaml::node& conn_node, Room::Data& room) {
    room.upper_room = conn_node.contains("up")
        ? convertRoomConnection(conn_node["up"].get_value_or<std::string>("null"))
        : "0";
    room.lower_room = conn_node.contains("down")
        ? convertRoomConnection(conn_node["down"].get_value_or<std::string>("null"))
        : "0";
    room.left_room = conn_node.contains("left")
        ? convertRoomConnection(conn_node["left"].get_value_or<std::string>("null"))
        : "0";
    room.right_room = conn_node.contains("right")
        ? convertRoomConnection(conn_node["right"].get_value_or<std::string>("null"))
        : "0";
}

// ============================================================================
// Parseo del tilemap
// ============================================================================

// Solo soporta el formato actual: tilemap.draw + tilemap.collision.
// La rama de retrocompatibilidad para tilemap directo se eliminó porque dejaba
// collision_tile_map vacío y causaba un crash diferido en CollisionMap.
void RoomFormat::parseTilemap(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name, bool verbose) {
    if (!yaml.contains("tilemap")) {
        std::cerr << "Error: No tilemap found in " << file_name << '\n';
        return;
    }

    const auto& tilemap_node = yaml["tilemap"];

    if (!tilemap_node.contains("draw") || !tilemap_node.contains("collision")) {
        std::cerr << "Error: " << file_name << " has malformed tilemap (missing draw or collision)\n";
        return;
    }

    room.tile_map = flattenTilemap(readTilemap2D(tilemap_node["draw"]));
    room.collision_tile_map = flattenTilemap(readTilemap2D(tilemap_node["collision"]));

    if (verbose) {
        std::cout << "Loaded tilemap: " << room.tile_map.size() << " tiles + collision: "
                  << room.collision_tile_map.size() << " tiles\n";
    }
}

// ============================================================================
// Parseo de enemigos
// ============================================================================

void RoomFormat::parseEnemyBoundaries(const fkyaml::node& bounds_node, Enemy::Data& enemy) {
    // Nuevo formato: position1 y position2
    if (bounds_node.contains("position1")) {
        const auto& pos1 = bounds_node["position1"];
        if (pos1.contains("x")) { enemy.x1 = pos1["x"].get_value<int>() * Tile::SIZE; }
        if (pos1.contains("y")) { enemy.y1 = pos1["y"].get_value<int>() * Tile::SIZE; }
    }
    if (bounds_node.contains("position2")) {
        const auto& pos2 = bounds_node["position2"];
        if (pos2.contains("x")) { enemy.x2 = pos2["x"].get_value<int>() * Tile::SIZE; }
        if (pos2.contains("y")) { enemy.y2 = pos2["y"].get_value<int>() * Tile::SIZE; }
    }

    // Formato antiguo: x1/y1/x2/y2 (compatibilidad)
    if (bounds_node.contains("x1")) { enemy.x1 = bounds_node["x1"].get_value<int>() * Tile::SIZE; }
    if (bounds_node.contains("y1")) { enemy.y1 = bounds_node["y1"].get_value<int>() * Tile::SIZE; }
    if (bounds_node.contains("x2")) { enemy.x2 = bounds_node["x2"].get_value<int>() * Tile::SIZE; }
    if (bounds_node.contains("y2")) { enemy.y2 = bounds_node["y2"].get_value<int>() * Tile::SIZE; }
}

auto RoomFormat::parseEnemyData(const fkyaml::node& enemy_node) -> Enemy::Data {
    Enemy::Data enemy;

    if (enemy_node.contains("type")) {
        enemy.type = enemy_node["type"].get_value<std::string>();
    }

    if (enemy_node.contains("animation")) {
        enemy.animation_path = enemy_node["animation"].get_value<std::string>();
    }

    if (enemy_node.contains("position")) {
        const auto& pos = enemy_node["position"];
        if (pos.contains("x")) { enemy.x = pos["x"].get_value<float>() * Tile::SIZE; }
        if (pos.contains("y")) { enemy.y = pos["y"].get_value<float>() * Tile::SIZE; }
    }

    if (enemy_node.contains("velocity")) {
        const auto& vel = enemy_node["velocity"];
        if (vel.contains("x")) { enemy.vx = vel["x"].get_value<float>(); }
        if (vel.contains("y")) { enemy.vy = vel["y"].get_value<float>(); }
    }

    if (enemy_node.contains("boundaries")) {
        parseEnemyBoundaries(enemy_node["boundaries"], enemy);
    }

    enemy.flip = enemy_node.contains("flip")
        ? enemy_node["flip"].get_value_or<bool>(false)
        : false;

    enemy.mirror = enemy_node.contains("mirror")
        ? enemy_node["mirror"].get_value_or<bool>(false)
        : false;

    enemy.frame = enemy_node.contains("frame")
        ? enemy_node["frame"].get_value_or<int>(-1)
        : -1;

    return enemy;
}

void RoomFormat::parseEnemies(const fkyaml::node& yaml, Room::Data& room, bool verbose) {
    if (!yaml.contains("enemies") || yaml["enemies"].is_null()) {
        return;
    }

    for (const auto& enemy_node : yaml["enemies"]) {
        room.enemies.push_back(parseEnemyData(enemy_node));
    }

    if (verbose) {
        std::cout << "Loaded " << room.enemies.size() << " enemies\n";
    }
}

// ============================================================================
// Parseo de items
// ============================================================================

auto RoomFormat::parseItemData(const fkyaml::node& item_node) -> Item::Data {
    Item::Data item;

    if (item_node.contains("tileSetFile")) {
        item.tile_set_file = item_node["tileSetFile"].get_value<std::string>();
    }

    if (item_node.contains("tile")) {
        item.tile = item_node["tile"].get_value<int>();
    }

    if (item_node.contains("position")) {
        const auto& pos = item_node["position"];
        if (pos.contains("x")) { item.x = pos["x"].get_value<float>() * Tile::SIZE; }
        if (pos.contains("y")) { item.y = pos["y"].get_value<float>() * Tile::SIZE; }
    }

    item.counter = item_node.contains("counter")
        ? item_node["counter"].get_value_or<int>(0)
        : 0;

    // Colores: defaults de Item::Data, override si están en el yaml
    if (item_node.contains("color1")) {
        item.color1 = readColorNode(item_node["color1"]);
        item.color1_overridden = true;
    }
    if (item_node.contains("color2")) {
        item.color2 = readColorNode(item_node["color2"]);
        item.color2_overridden = true;
    }

    return item;
}

void RoomFormat::parseItems(const fkyaml::node& yaml, Room::Data& room, bool verbose) {
    if (!yaml.contains("items") || yaml["items"].is_null()) {
        return;
    }

    for (const auto& item_node : yaml["items"]) {
        room.items.push_back(parseItemData(item_node));
    }

    if (verbose) {
        std::cout << "Loaded " << room.items.size() << " items\n";
    }
}

// ============================================================================
// Parseo de plataformas
// ============================================================================

auto RoomFormat::parsePlatformData(const fkyaml::node& platform_node) -> MovingPlatform::Data {
    MovingPlatform::Data platform;

    if (platform_node.contains("animation")) {
        platform.animation_path = platform_node["animation"].get_value<std::string>();
    }
    if (platform_node.contains("speed")) {
        platform.speed = platform_node["speed"].get_value<float>();
    }
    if (platform_node.contains("loop")) {
        auto loop_str = platform_node["loop"].get_value<std::string>();
        platform.loop = (loop_str == "circular") ? LoopMode::CIRCULAR : LoopMode::PINGPONG;
    }
    if (platform_node.contains("easing")) {
        platform.easing = platform_node["easing"].get_value<std::string>();
    }
    platform.frame = platform_node.contains("frame")
        ? platform_node["frame"].get_value_or<int>(-1)
        : -1;

    if (platform_node.contains("path")) {
        for (const auto& wp_node : platform_node["path"]) {
            Waypoint wp;
            wp.x = wp_node["x"].get_value<float>() * Tile::SIZE;
            wp.y = wp_node["y"].get_value<float>() * Tile::SIZE;
            if (wp_node.contains("wait")) {
                wp.wait = wp_node["wait"].get_value<float>();
            }
            platform.path.push_back(wp);
        }
    }

    return platform;
}

void RoomFormat::parsePlatforms(const fkyaml::node& yaml, Room::Data& room, bool verbose) {
    if (!yaml.contains("platforms") || yaml["platforms"].is_null()) {
        return;
    }

    for (const auto& platform_node : yaml["platforms"]) {
        room.platforms.push_back(parsePlatformData(platform_node));
    }

    if (verbose) {
        std::cout << "Loaded " << room.platforms.size() << " platforms\n";
    }
}

// ============================================================================
// Parseo de llaves
// ============================================================================

auto RoomFormat::parseKeyData(const fkyaml::node& key_node) -> Key::Data {
    Key::Data key;

    if (key_node.contains("animation")) {
        key.animation_path = key_node["animation"].get_value<std::string>();
    }
    if (key_node.contains("id")) {
        key.id = key_node["id"].get_value<std::string>();
    }
    if (key_node.contains("position")) {
        const auto& pos = key_node["position"];
        if (pos.contains("x")) { key.x = pos["x"].get_value<float>() * Tile::SIZE; }
        if (pos.contains("y")) { key.y = pos["y"].get_value<float>() * Tile::SIZE; }
    }

    return key;
}

void RoomFormat::parseKeys(const fkyaml::node& yaml, Room::Data& room, bool verbose) {
    if (!yaml.contains("keys") || yaml["keys"].is_null()) {
        return;
    }

    for (const auto& key_node : yaml["keys"]) {
        room.keys.push_back(parseKeyData(key_node));
    }

    if (verbose) {
        std::cout << "Loaded " << room.keys.size() << " keys\n";
    }
}

// ============================================================================
// Parseo de puertas
// ============================================================================

auto RoomFormat::parseDoorData(const fkyaml::node& door_node) -> Door::Data {
    Door::Data door;

    if (door_node.contains("animation")) {
        door.animation_path = door_node["animation"].get_value<std::string>();
    }
    if (door_node.contains("id")) {
        door.id = door_node["id"].get_value<std::string>();
    }
    if (door_node.contains("position")) {
        const auto& pos = door_node["position"];
        if (pos.contains("x")) { door.x = pos["x"].get_value<float>() * Tile::SIZE; }
        if (pos.contains("y")) { door.y = pos["y"].get_value<float>() * Tile::SIZE; }
    }

    return door;
}

void RoomFormat::parseDoors(const fkyaml::node& yaml, Room::Data& room, bool verbose) {
    if (!yaml.contains("doors") || yaml["doors"].is_null()) {
        return;
    }

    for (const auto& door_node : yaml["doors"]) {
        room.doors.push_back(parseDoorData(door_node));
    }

    if (verbose) {
        std::cout << "Loaded " << room.doors.size() << " doors\n";
    }
}

// ============================================================================
// Punto de entrada del parser
// ============================================================================

void RoomFormat::parseAll(const fkyaml::node& yaml, Room::Data& room, const std::string& file_name, bool verbose) {
    parseRoomConfig(yaml, room, file_name);
    parseTilemap(yaml, room, file_name, verbose);
    parseEnemies(yaml, room, verbose);
    parseItems(yaml, room, verbose);
    parsePlatforms(yaml, room, verbose);
    parseKeys(yaml, room, verbose);
    parseDoors(yaml, room, verbose);
}

auto RoomFormat::loadYAML(const std::string& file_path, bool verbose) -> Room::Data {
    Room::Data room;

    const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1);

    try {
        auto file_data = Resource::Helper::loadFile(file_path);
        if (file_data.empty()) {
            std::cerr << "Error: Unable to load file " << FILE_NAME << '\n';
            return room;
        }

        const std::string YAML_CONTENT(file_data.begin(), file_data.end());
        auto yaml = fkyaml::node::deserialize(YAML_CONTENT);

        parseAll(yaml, room, FILE_NAME, verbose);

        if (verbose) {
            std::cout << "Room loaded successfully: " << FILE_NAME << '\n';
        }
    } catch (const fkyaml::exception& e) {
        std::cerr << "YAML parsing error in " << FILE_NAME << ": " << e.what() << '\n';
    } catch (const std::exception& e) {
        std::cerr << "Error loading room " << FILE_NAME << ": " << e.what() << '\n';
    }

    return room;
}

#ifdef _DEBUG

auto RoomFormat::loadFromString(const std::string& yaml_content, const std::string& file_name) -> Room::Data {
    Room::Data room;
    try {
        auto yaml = fkyaml::node::deserialize(yaml_content);
        parseAll(yaml, room, file_name, false);
    } catch (const fkyaml::exception& e) {
        std::cerr << "YAML parsing error in " << file_name << ": " << e.what() << '\n';
    } catch (const std::exception& e) {
        std::cerr << "Error loading room " << file_name << ": " << e.what() << '\n';
    }
    return room;
}

// ============================================================================
// createDefault: construye un Room::Data válido para una room nueva
// ============================================================================

auto RoomFormat::createDefault() -> Room::Data {
    Room::Data data;

    // Zona por defecto (resuelve tile_set_file, music y bg_color desde ZoneManager)
    const Zone::Data* zone = ZoneManager::get()->getDefaultZone();
    if (zone != nullptr) {
        data.zone = zone->name;
        data.tile_set_file = zone->tile_set_file;
        data.music = zone->music;
        data.bg_color = zone->bg_color;
    }
    data.tile_set_overridden = false;
    data.music_overridden = false;
    data.bg_color_overridden = false;

    // Conexiones a cero
    data.upper_room = "0";
    data.lower_room = "0";
    data.left_room = "0";
    data.right_room = "0";

    // Tilemaps del tamaño correcto, vacíos
    data.tile_map.resize(static_cast<size_t>(Map::WIDTH) * static_cast<size_t>(Map::HEIGHT), -1);
    data.collision_tile_map.resize(static_cast<size_t>(Map::WIDTH) * static_cast<size_t>(Map::HEIGHT), 0);

    return data;
}

// ============================================================================
// Serialización
// ============================================================================

auto RoomFormat::roomConnectionToYAML(const std::string& connection) -> std::string {
    if (connection == "0" || connection.empty()) { return "null"; }
    return connection;
}

void RoomFormat::writeRoomSection(std::ostringstream& out, const Room::Data& room_data) {
    out << "room:\n";

    // zone es siempre obligatoria
    out << "  zone: " << room_data.zone << "\n";

    // tileSetFile/music/bgColor solo si son override explícito del valor heredado de la zona
    if (room_data.tile_set_overridden) { out << "  tileSetFile: " << room_data.tile_set_file << "\n"; }
    if (room_data.music_overridden) { out << "  music: " << room_data.music << "\n"; }
    if (room_data.bg_color_overridden) { out << "  bgColor: " << static_cast<int>(room_data.bg_color) << "\n"; }

    out << "\n";
    out << "  # Conexiones de la habitación (null = sin conexión)\n";
    out << "  connections:\n";
    out << "    up: " << roomConnectionToYAML(room_data.upper_room) << "\n";
    out << "    down: " << roomConnectionToYAML(room_data.lower_room) << "\n";
    out << "    left: " << roomConnectionToYAML(room_data.left_room) << "\n";
    out << "    right: " << roomConnectionToYAML(room_data.right_room) << "\n";
}

void RoomFormat::writeTilemapSection(std::ostringstream& out, const Room::Data& room_data) {
    out << "\n";
    out << "# Tilemap: " << Map::HEIGHT << " filas x " << Map::WIDTH << " columnas @ " << Tile::SIZE << "px/tile\n";
    out << "tilemap:\n";

    auto write_grid = [&out](const auto& tilemap, const char* header, int empty_value) {
        out << header;
        for (int row = 0; row < Map::HEIGHT; ++row) {
            out << "    - [";
            for (int col = 0; col < Map::WIDTH; ++col) {
                const int INDEX = (row * Map::WIDTH) + col;
                out << (INDEX < static_cast<int>(tilemap.size()) ? tilemap[INDEX] : empty_value);
                if (col < Map::WIDTH - 1) { out << ", "; }
            }
            out << "]\n";
        }
    };

    write_grid(room_data.tile_map, "  # Mapa de dibujo (indices de tiles, -1 = vacio)\n  draw:\n", -1);
    write_grid(room_data.collision_tile_map, "  # Mapa de colisiones (0 = vacio, 1 = solido)\n  collision:\n", 0);
}

void RoomFormat::writeEnemiesSection(std::ostringstream& out, const Room::Data& room_data) {
    if (room_data.enemies.empty()) { return; }
    out << "\n";
    out << "# Enemigos en esta habitación\n";
    out << "enemies:\n";
    for (const auto& enemy : room_data.enemies) {
        out << "  - animation: " << enemy.animation_path << "\n";
        if (enemy.type != "path") { out << "    type: " << enemy.type << "\n"; }

        const int POS_X = static_cast<int>(std::round(enemy.x / Tile::SIZE));
        const int POS_Y = static_cast<int>(std::round(enemy.y / Tile::SIZE));
        out << "    position: {x: " << POS_X << ", y: " << POS_Y << "}\n";
        out << "    velocity: {x: " << enemy.vx << ", y: " << enemy.vy << "}\n";

        const int B1_X = enemy.x1 / Tile::SIZE;
        const int B1_Y = enemy.y1 / Tile::SIZE;
        const int B2_X = enemy.x2 / Tile::SIZE;
        const int B2_Y = enemy.y2 / Tile::SIZE;
        out << "    boundaries:\n";
        out << "      position1: {x: " << B1_X << ", y: " << B1_Y << "}\n";
        out << "      position2: {x: " << B2_X << ", y: " << B2_Y << "}\n";

        if (enemy.flip) { out << "    flip: true\n"; }
        if (enemy.mirror) { out << "    mirror: true\n"; }
        if (enemy.frame != -1) { out << "    frame: " << enemy.frame << "\n"; }
        out << "\n";
    }
}

void RoomFormat::writeItemsSection(std::ostringstream& out, const Room::Data& room_data) {
    if (room_data.items.empty()) { return; }
    out << "# Objetos en esta habitación\n";
    out << "items:\n";
    for (const auto& item : room_data.items) {
        out << "  - tileSetFile: " << item.tile_set_file << "\n";
        out << "    tile: " << item.tile << "\n";

        const int ITEM_X = static_cast<int>(std::round(item.x / Tile::SIZE));
        const int ITEM_Y = static_cast<int>(std::round(item.y / Tile::SIZE));
        out << "    position: {x: " << ITEM_X << ", y: " << ITEM_Y << "}\n";

        if (item.counter != 0) { out << "    counter: " << item.counter << "\n"; }
        if (item.color1_overridden) { out << "    color1: " << static_cast<int>(item.color1) << "\n"; }
        if (item.color2_overridden) { out << "    color2: " << static_cast<int>(item.color2) << "\n"; }
        out << "\n";
    }
}

void RoomFormat::writePlatformsSection(std::ostringstream& out, const Room::Data& room_data) {
    if (room_data.platforms.empty()) { return; }
    out << "# Plataformas móviles en esta habitación\n";
    out << "platforms:\n";
    for (const auto& plat : room_data.platforms) {
        out << "  - animation: " << plat.animation_path << "\n";
        out << "    speed: " << plat.speed << "\n";
        out << "    loop: " << (plat.loop == LoopMode::CIRCULAR ? "circular" : "pingpong") << "\n";
        if (plat.easing != "linear") { out << "    easing: " << plat.easing << "\n"; }
        if (plat.frame != -1) { out << "    frame: " << plat.frame << "\n"; }
        out << "    path:\n";
        for (const auto& wp : plat.path) {
            const int WX = static_cast<int>(std::round(wp.x / Tile::SIZE));
            const int WY = static_cast<int>(std::round(wp.y / Tile::SIZE));
            out << "      - {x: " << WX << ", y: " << WY;
            if (wp.wait > 0.0F) { out << ", wait: " << wp.wait; }
            out << "}\n";
        }
        out << "\n";
    }
}

void RoomFormat::writeKeysSection(std::ostringstream& out, const Room::Data& room_data) {
    if (room_data.keys.empty()) { return; }
    out << "# Llaves en esta habitación\n";
    out << "keys:\n";
    for (const auto& key : room_data.keys) {
        out << "  - animation: " << key.animation_path << "\n";
        out << "    id: \"" << key.id << "\"\n";
        const int KX = static_cast<int>(std::round(key.x / Tile::SIZE));
        const int KY = static_cast<int>(std::round(key.y / Tile::SIZE));
        out << "    position: {x: " << KX << ", y: " << KY << "}\n";
        out << "\n";
    }
}

void RoomFormat::writeDoorsSection(std::ostringstream& out, const Room::Data& room_data) {
    if (room_data.doors.empty()) { return; }
    out << "# Puertas en esta habitación\n";
    out << "doors:\n";
    for (const auto& door : room_data.doors) {
        out << "  - animation: " << door.animation_path << "\n";
        out << "    id: \"" << door.id << "\"\n";
        const int DX = static_cast<int>(std::round(door.x / Tile::SIZE));
        const int DY = static_cast<int>(std::round(door.y / Tile::SIZE));
        out << "    position: {x: " << DX << ", y: " << DY << "}\n";
        out << "\n";
    }
}

auto RoomFormat::buildContent(const Room::Data& room_data) -> std::string {
    std::ostringstream out;
    writeRoomSection(out, room_data);
    writeTilemapSection(out, room_data);
    writeEnemiesSection(out, room_data);
    writeItemsSection(out, room_data);
    writePlatformsSection(out, room_data);
    writeKeysSection(out, room_data);
    writeDoorsSection(out, room_data);
    return out.str();
}

auto RoomFormat::saveYAML(const std::string& file_path, const Room::Data& data) -> std::string {
    const std::string CONTENT = buildContent(data);

    std::ofstream file(file_path);
    if (!file.is_open()) {
        std::cerr << "RoomFormat: Cannot write to " << file_path << "\n";
        return "Error: Cannot write to " + file_path;
    }

    file << CONTENT;
    file.close();

    const std::string FILE_NAME = file_path.substr(file_path.find_last_of("\\/") + 1);
    std::cout << "RoomFormat: Saved " << FILE_NAME << "\n";
    return "Saved " + FILE_NAME;
}

#endif  // _DEBUG
