#include "core/resources/resource_list.hpp"

#include <algorithm>
#include <iostream>
#include <stdexcept>

#include "core/resources/resource_helper.hpp"
#include "external/fkyaml_node.hpp"

namespace Resource {

    std::unique_ptr<List> List::instance;

    void List::init(const std::string& yaml_path) {
        instance = std::unique_ptr<List>(new List());
        instance->loadFromYaml(yaml_path);
    }

    void List::destroy() { instance.reset(); }

    auto List::get() -> List* { return instance.get(); }

    void List::loadFromYaml(const std::string& yaml_path) {
        auto bytes = ResourceHelper::loadFile(yaml_path);
        if (bytes.empty()) {
            std::cout << "Resource::List: cannot load manifest " << yaml_path << '\n';
            return;
        }
        std::string content(bytes.begin(), bytes.end());
        loadFromString(content);
    }

    void List::loadFromString(const std::string& yaml_content) {
        try {
            auto yaml = fkyaml::node::deserialize(yaml_content);
            if (!yaml.contains("assets")) {
                std::cout << "Resource::List: missing 'assets' root key\n";
                return;
            }
            const auto& assets = yaml["assets"];
            for (auto cat_it = assets.begin(); cat_it != assets.end(); ++cat_it) {
                const auto& category_node = cat_it.value();
                if (!category_node.is_mapping()) {
                    continue;
                }
                for (auto type_it = category_node.begin(); type_it != category_node.end(); ++type_it) {
                    auto type_str = type_it.key().get_value<std::string>();
                    Type type = parseAssetType(type_str);
                    const auto& items = type_it.value();
                    if (!items.is_sequence()) {
                        continue;
                    }
                    for (const auto& item : items) {
                        if (item.is_string()) {
                            addToMap(item.get_value<std::string>(), type);
                        }
                    }
                }
            }
            std::cout << "Resource::List: loaded " << file_list_.size() << " assets from manifest\n";
        } catch (const std::exception& e) {
            std::cout << "Resource::List: YAML parse error: " << e.what() << '\n';
        }
    }

    void List::addToMap(const std::string& path, Type type) {
        auto key = basename(path);
        if (file_list_.contains(key)) {
            std::cout << "Resource::List: duplicate asset key '" << key << "', overwriting\n";
        }
        file_list_.emplace(key, Item{path, type});
    }

    auto List::get(const std::string& filename) const -> std::string {
        auto it = file_list_.find(filename);
        if (it != file_list_.end()) {
            return it->second.path;
        }
        return "";
    }

    auto List::getListByType(Type type) const -> std::vector<std::string> {
        std::vector<std::string> list;
        for (const auto& [filename, item] : file_list_) {
            if (item.type == type) {
                list.push_back(item.path);
            }
        }
        std::ranges::sort(list);
        return list;
    }

    auto List::exists(const std::string& filename) const -> bool {
        return file_list_.contains(filename);
    }

    auto List::parseAssetType(const std::string& type_str) -> Type {
        if (type_str == "DATA") {
            return Type::DATA;
        }
        if (type_str == "BITMAP") {
            return Type::BITMAP;
        }
        if (type_str == "MUSIC") {
            return Type::MUSIC;
        }
        if (type_str == "SOUND") {
            return Type::SOUND;
        }
        if (type_str == "FONT") {
            return Type::FONT;
        }
        throw std::runtime_error("Unknown asset type: " + type_str);
    }

    auto List::basename(const std::string& path) -> std::string {
        auto pos = path.find_last_of("/\\");
        return pos == std::string::npos ? path : path.substr(pos + 1);
    }

}  // namespace Resource
