#include "room_loader.hpp"

#include <exception>  // Para exception
#include <iostream>   // Para cout, cerr
#include "external/fkyaml_node.hpp"  // Para fkyaml::node

#include "core/resources/resource_helper.hpp"  // Para Resource::Helper
#include "utils/defines.hpp"                   // Para TILE_SIZE
#include "utils/utils.hpp"                     // Para stringToColor

// Convierte room connection de YAML a formato con extensión
auto RoomLoader::convertRoomConnection(const std::string& value) -> std::string {
    if (value == "null" || value.empty()) {
        return "0";
    }
    // "02" → "02.yaml"
    return value + ".yaml";
}

// Convierte un tilemap 2D a vector 1D flat
auto RoomLoader::flattenTilemap(const std::vector<std::vector<int>>& tilemap_2d) -> std::vector<int> {
    std::vector<int> tilemap_flat;
    tilemap_flat.reserve(512);  // 16 rows × 32 cols

    for (const auto& row : tilemap_2d) {
        for (int tile : row) {
            tilemap_flat.push_back(tile);
        }
    }

    return tilemap_flat;
}

// Carga un archivo de room en formato YAML
auto RoomLoader::loadYAML(const std::string& file_path, bool verbose) -> Room::Data {
    Room::Data room;

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

    try {
        // Load YAML file using ResourceHelper (supports both filesystem and pack)
        auto file_data = Resource::Helper::loadFile(file_path);

        if (file_data.empty()) {
            std::cerr << "Error: Unable to load file " << FILE_NAME << '\n';
            return room;
        }

        // Parse YAML from string
        std::string yaml_content(file_data.begin(), file_data.end());
        auto yaml = fkyaml::node::deserialize(yaml_content);

        // --- Parse room configuration ---
        if (yaml.contains("room")) {
            const auto& room_node = yaml["room"];

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

            // Basic properties
            if (room_node.contains("name")) {
                room.name = room_node["name"].get_value<std::string>();
            }
            if (room_node.contains("bgColor")) {
                room.bg_color = room_node["bgColor"].get_value<std::string>();
            }
            if (room_node.contains("border")) {
                room.border_color = room_node["border"].get_value<std::string>();
            }
            if (room_node.contains("tileSetFile")) {
                room.tile_set_file = room_node["tileSetFile"].get_value<std::string>();
            }

            // Room connections
            if (room_node.contains("connections")) {
                const auto& conn = room_node["connections"];

                if (conn.contains("up")) {
                    room.upper_room = convertRoomConnection(conn["up"].get_value_or<std::string>("null"));
                } else {
                    room.upper_room = "0";
                }

                if (conn.contains("down")) {
                    room.lower_room = convertRoomConnection(conn["down"].get_value_or<std::string>("null"));
                } else {
                    room.lower_room = "0";
                }

                if (conn.contains("left")) {
                    room.left_room = convertRoomConnection(conn["left"].get_value_or<std::string>("null"));
                } else {
                    room.left_room = "0";
                }

                if (conn.contains("right")) {
                    room.right_room = convertRoomConnection(conn["right"].get_value_or<std::string>("null"));
                } else {
                    room.right_room = "0";
                }
            }

            // Item colors
            if (room_node.contains("itemColor1")) {
                room.item_color1 = room_node["itemColor1"].get_value_or<std::string>("yellow");
            } else {
                room.item_color1 = "yellow";
            }

            if (room_node.contains("itemColor2")) {
                room.item_color2 = room_node["itemColor2"].get_value_or<std::string>("magenta");
            } else {
                room.item_color2 = "magenta";
            }

            // Conveyor belt direction
            if (room_node.contains("autoSurface")) {
                room.conveyor_belt_direction = room_node["autoSurface"].get_value_or<int>(0);
            } else {
                room.conveyor_belt_direction = 0;
            }
        }

        // --- Parse tilemap ---
        if (yaml.contains("tilemap")) {
            const auto& tilemap_node = yaml["tilemap"];

            // Read 2D array
            std::vector<std::vector<int>> tilemap_2d;
            tilemap_2d.reserve(16);

            for (const auto& row_node : tilemap_node) {
                std::vector<int> row;
                row.reserve(32);

                for (const auto& tile_node : row_node) {
                    row.push_back(tile_node.get_value<int>());
                }

                tilemap_2d.push_back(row);
            }

            // Convert to 1D flat array
            room.tile_map = flattenTilemap(tilemap_2d);

            if (verbose) {
                std::cout << "Loaded tilemap: " << room.tile_map.size() << " tiles\n";
            }
        } else {
            std::cerr << "Warning: No tilemap found in " << FILE_NAME << '\n';
        }

        // --- Parse enemies ---
        if (yaml.contains("enemies") && !yaml["enemies"].is_null()) {
            const auto& enemies_node = yaml["enemies"];

            for (const auto& enemy_node : enemies_node) {
                Enemy::Data enemy;

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

                // Position (in tiles, convert to pixels)
                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;
                    }
                }

                // Velocity (already in pixels/second)
                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>();
                    }
                }

                // Boundaries (in tiles, convert to pixels)
                if (enemy_node.contains("boundaries")) {
                    const auto& bounds = enemy_node["boundaries"];
                    if (bounds.contains("x1")) {
                        enemy.x1 = bounds["x1"].get_value<int>() * TILE_SIZE;
                    }
                    if (bounds.contains("y1")) {
                        enemy.y1 = bounds["y1"].get_value<int>() * TILE_SIZE;
                    }
                    if (bounds.contains("x2")) {
                        enemy.x2 = bounds["x2"].get_value<int>() * TILE_SIZE;
                    }
                    if (bounds.contains("y2")) {
                        enemy.y2 = bounds["y2"].get_value<int>() * TILE_SIZE;
                    }
                }

                // Color
                if (enemy_node.contains("color")) {
                    enemy.color = enemy_node["color"].get_value_or<std::string>("white");
                } else {
                    enemy.color = "white";
                }

                // Optional fields
                if (enemy_node.contains("flip")) {
                    enemy.flip = enemy_node["flip"].get_value_or<bool>(false);
                } else {
                    enemy.flip = false;
                }

                if (enemy_node.contains("mirror")) {
                    enemy.mirror = enemy_node["mirror"].get_value_or<bool>(false);
                } else {
                    enemy.mirror = false;
                }

                if (enemy_node.contains("frame")) {
                    enemy.frame = enemy_node["frame"].get_value_or<int>(-1);
                } else {
                    enemy.frame = -1;
                }

                room.enemies.push_back(enemy);
            }

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

        // --- Parse items ---
        if (yaml.contains("items") && !yaml["items"].is_null()) {
            const auto& items_node = yaml["items"];

            for (const auto& item_node : items_node) {
                Item::Data item;

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

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

                // Position (in tiles, convert to pixels)
                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;
                    }
                }

                // Counter
                if (item_node.contains("counter")) {
                    item.counter = item_node["counter"].get_value_or<int>(0);
                } else {
                    item.counter = 0;
                }

                // Colors (assigned from room defaults)
                item.color1 = stringToColor(room.item_color1);
                item.color2 = stringToColor(room.item_color2);

                room.items.push_back(item);
            }

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

        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;
}
