// stage_loader.cpp - Implementació del carregador de configuració YAML
// © 2026 JailDesigner

#include "stage_loader.hpp"

#include <cstddef>
#include <cstdint>
#include <exception>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>

#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"
#include "game/entities/enemy.hpp"
#include "stage_config.hpp"

namespace StageSystem {

    auto StageLoader::load(const std::string& path) -> std::unique_ptr<StageSystemConfig> {
        try {
            // Normalize path: "data/stages/stages.yaml" → "stages/stages.yaml"
            std::string normalized = path;
            if (normalized.starts_with("data/")) {
                normalized = normalized.substr(5);
            }

            std::vector<uint8_t> data = Resource::Helper::loadFile(normalized);
            if (data.empty()) {
                std::cerr << "[StageLoader] Error: no es pot load " << normalized << '\n';
                return nullptr;
            }

            std::string yaml_content(data.begin(), data.end());
            std::stringstream stream(yaml_content);

            fkyaml::node yaml = fkyaml::node::deserialize(stream);
            auto config = std::make_unique<StageSystemConfig>();

            if (!yaml.contains("metadata")) {
                std::cerr << "[StageLoader] Error: falta camp 'metadata'" << '\n';
                return nullptr;
            }
            if (!parseMetadata(yaml["metadata"], config->metadata)) {
                return nullptr;
            }

            if (!yaml.contains("stages")) {
                std::cerr << "[StageLoader] Error: falta camp 'stages'" << '\n';
                return nullptr;
            }
            if (!yaml["stages"].is_sequence()) {
                std::cerr << "[StageLoader] Error: 'stages' ha de ser una list" << '\n';
                return nullptr;
            }

            for (const auto& stage_yaml : yaml["stages"]) {
                StageConfig stage;
                if (!parseStage(stage_yaml, stage)) {
                    return nullptr;
                }
                config->stages.push_back(std::move(stage));
            }

            if (!validateConfig(*config)) {
                return nullptr;
            }

            std::cout << "[StageLoader] Carregats " << config->stages.size()
                      << " stages correctament" << '\n';
            return config;

        } catch (const std::exception& e) {
            std::cerr << "[StageLoader] Excepció: " << e.what() << '\n';
            return nullptr;
        }
    }

    auto StageLoader::parseMetadata(const fkyaml::node& yaml, MetadataStages& meta) -> bool {
        try {
            if (!yaml.contains("version") || !yaml.contains("total_stages")) {
                std::cerr << "[StageLoader] Error: metadata incompleta" << '\n';
                return false;
            }

            meta.version = yaml["version"].get_value<std::string>();
            meta.total_stages = yaml["total_stages"].get_value<uint8_t>();
            meta.descripcio = yaml.contains("description")
                ? yaml["description"].get_value<std::string>()
                : "";

            return true;
        } catch (const std::exception& e) {
            std::cerr << "[StageLoader] Error parsing metadata: " << e.what() << '\n';
            return false;
        }
    }

    auto StageLoader::parseStage(const fkyaml::node& yaml, StageConfig& stage) -> bool {
        try {
            if (!yaml.contains("stage_id") || !yaml.contains("waves")) {
                std::cerr << "[StageLoader] Error: stage incompleta (cal stage_id i waves)" << '\n';
                return false;
            }

            stage.stage_id = yaml["stage_id"].get_value<uint8_t>();

            // multipliers és opcional: si falta, queda als defaults (1.0/1.0/0.0).
            if (yaml.contains("multipliers")) {
                if (!parseMultipliers(yaml["multipliers"], stage.multiplicadors)) {
                    return false;
                }
            }

            if (!yaml["waves"].is_sequence()) {
                std::cerr << "[StageLoader] Error: 'waves' ha de ser una list" << '\n';
                return false;
            }
            for (const auto& wave_yaml : yaml["waves"]) {
                WaveConfig wave;
                if (!parseWave(wave_yaml, wave)) {
                    return false;
                }
                stage.waves.push_back(std::move(wave));
            }

            if (!stage.isValid()) {
                std::cerr << "[StageLoader] Error: stage " << static_cast<int>(stage.stage_id)
                          << " no és vàlid" << '\n';
                return false;
            }

            return true;
        } catch (const std::exception& e) {
            std::cerr << "[StageLoader] Error parsing stage: " << e.what() << '\n';
            return false;
        }
    }

    auto StageLoader::parseMultipliers(const fkyaml::node& yaml, MultiplicadorsDificultat& mult) -> bool {
        try {
            if (yaml.contains("velocity")) {
                mult.velocity = yaml["velocity"].get_value<float>();
            }
            if (yaml.contains("rotation")) {
                mult.rotation = yaml["rotation"].get_value<float>();
            }
            if (yaml.contains("tracking")) {
                mult.tracking_strength = yaml["tracking"].get_value<float>();
            }

            if (mult.velocity < 0.1F || mult.velocity > 5.0F) {
                std::cerr << "[StageLoader] Warning: velocity fora de rang (0.1-5.0)" << '\n';
            }
            if (mult.rotation < 0.1F || mult.rotation > 5.0F) {
                std::cerr << "[StageLoader] Warning: rotation fora de rang (0.1-5.0)" << '\n';
            }
            if (mult.tracking_strength < 0.0F || mult.tracking_strength > 2.0F) {
                std::cerr << "[StageLoader] Warning: tracking fora de rang (0.0-2.0)" << '\n';
            }

            return true;
        } catch (const std::exception& e) {
            std::cerr << "[StageLoader] Error parsing multipliers: " << e.what() << '\n';
            return false;
        }
    }

    auto StageLoader::parseWave(const fkyaml::node& yaml, WaveConfig& wave) -> bool {
        try {
            if (!yaml.contains("spawn") || !yaml.contains("next")) {
                std::cerr << "[StageLoader] Error: wave sense 'spawn' o 'next'" << '\n';
                return false;
            }

            if (!yaml["spawn"].is_sequence()) {
                std::cerr << "[StageLoader] Error: 'spawn' ha de ser una list" << '\n';
                return false;
            }
            for (const auto& type_node : yaml["spawn"]) {
                auto type_str = type_node.get_value<std::string>();
                EnemyType type{};
                if (!parseEnemyType(type_str, type)) {
                    std::cerr << "[StageLoader] Error: tipus d'enemic desconegut '"
                              << type_str << "'" << '\n';
                    return false;
                }
                wave.spawn.push_back(type);
            }

            wave.spawn_interval = yaml.contains("spawn_interval")
                ? yaml["spawn_interval"].get_value<float>()
                : 0.0F;

            if (!parseNext(yaml["next"], wave.next)) {
                return false;
            }

            if (!wave.next.isValid()) {
                std::cerr << "[StageLoader] Error: wave 'next' sense condició (cal all_dead o timeout)" << '\n';
                return false;
            }

            return true;
        } catch (const std::exception& e) {
            std::cerr << "[StageLoader] Error parsing wave: " << e.what() << '\n';
            return false;
        }
    }

    auto StageLoader::parseEnemyType(const std::string& type_str, EnemyType& out) -> bool {
        if (type_str == "pentagon") {
            out = EnemyType::PENTAGON;
        } else if (type_str == "cuadrado" || type_str == "square") {
            out = EnemyType::SQUARE;
        } else if (type_str == "molinillo" || type_str == "pinwheel") {
            out = EnemyType::PINWHEEL;
        } else if (type_str == "star") {
            out = EnemyType::STAR;
        } else if (type_str == "orb") {
            out = EnemyType::ORB;
        } else {
            return false;
        }
        return true;
    }

    auto StageLoader::parseNext(const fkyaml::node& yaml, WaveNext& next) -> bool {
        try {
            // Forma curta: scalar string ("all_dead" o "end").
            if (yaml.is_string()) {
                const auto S = yaml.get_value<std::string>();
                if (S == "all_dead" || S == "end") {
                    next.on_all_dead = true;
                    return true;
                }
                std::cerr << "[StageLoader] Error: 'next' string desconegut '" << S << "'" << '\n';
                return false;
            }

            // Forma llarga: mapping amb claus opcionals.
            if (yaml.is_mapping()) {
                if (yaml.contains("all_dead")) {
                    next.on_all_dead = yaml["all_dead"].get_value<bool>();
                }
                if (yaml.contains("timeout")) {
                    next.has_timeout = true;
                    next.timeout = yaml["timeout"].get_value<float>();
                }
                return true;
            }

            std::cerr << "[StageLoader] Error: 'next' ha de ser string o mapping" << '\n';
            return false;
        } catch (const std::exception& e) {
            std::cerr << "[StageLoader] Error parsing next: " << e.what() << '\n';
            return false;
        }
    }

    auto StageLoader::validateConfig(const StageSystemConfig& config) -> bool {
        if (config.stages.empty()) {
            std::cerr << "[StageLoader] Error: cap stage carregat" << '\n';
            return false;
        }

        if (config.stages.size() != config.metadata.total_stages) {
            std::cerr << "[StageLoader] Warning: nombre de stages (" << config.stages.size()
                      << ") no coincideix amb metadata.total_stages ("
                      << static_cast<int>(config.metadata.total_stages) << ")" << '\n';
        }

        for (size_t i = 0; i < config.stages.size(); i++) {
            if (config.stages[i].stage_id != i + 1) {
                std::cerr << "[StageLoader] Error: stage_id no consecutius (esperat "
                          << i + 1 << ", trobat " << static_cast<int>(config.stages[i].stage_id)
                          << ")" << '\n';
                return false;
            }
        }

        return true;
    }

}  // namespace StageSystem
