#include "core/resources/resource_loader.hpp"

#include <algorithm>   // Para replace
#include <filesystem>  // Para exists, path, recursive_directory_iterator, directory_entry, relative
#include <fstream>     // Para basic_ostream, basic_ifstream, operator<<, basic_ios, endl, ios, basic_istream, operator|, basic_istream::read, basic_istream::seekg, basic_istream::tellg, ifstream, streamsize
#include <iostream>    // Para cerr, cout

#include "core/resources/resource_pack.hpp"  // Para ResourcePack

std::unique_ptr<ResourceLoader> ResourceLoader::instance = nullptr;

ResourceLoader::ResourceLoader()
    : resource_pack_(nullptr),
      fallback_to_files_(true) {}

auto ResourceLoader::getInstance() -> ResourceLoader& {
    if (!instance) {
        instance = std::unique_ptr<ResourceLoader>(new ResourceLoader());
    }
    return *instance;
}

ResourceLoader::~ResourceLoader() {
    shutdown();
}

auto ResourceLoader::initialize(const std::string& pack_file, bool enable_fallback) -> bool {
    shutdown();

    fallback_to_files_ = enable_fallback;
    pack_path_ = pack_file;

    if (std::filesystem::exists(pack_file)) {
        resource_pack_ = new ResourcePack();
        if (resource_pack_->loadPack(pack_file)) {
            return true;
        }
        delete resource_pack_;
        resource_pack_ = nullptr;
        std::cerr << "Failed to load resource pack: " << pack_file << '\n';
    }

    if (fallback_to_files_) {
        return true;
    }

    std::cerr << "Resource pack not found and fallback disabled: " << pack_file << '\n';
    return false;
}

void ResourceLoader::shutdown() {
    if (resource_pack_ != nullptr) {
        delete resource_pack_;
        resource_pack_ = nullptr;
    }
}

auto ResourceLoader::loadResource(const std::string& filename) -> std::vector<uint8_t> {
    if ((resource_pack_ != nullptr) && resource_pack_->hasResource(filename)) {
        return resource_pack_->getResource(filename);
    }

    if (fallback_to_files_) {
        return loadFromFile(filename);
    }

    std::cerr << "Resource not found: " << filename << '\n';
    return {};
}

auto ResourceLoader::resourceExists(const std::string& filename) -> bool {
    if ((resource_pack_ != nullptr) && resource_pack_->hasResource(filename)) {
        return true;
    }

    if (fallback_to_files_) {
        std::string full_path = getDataPath(filename);
        return std::filesystem::exists(full_path);
    }

    return false;
}

auto ResourceLoader::loadFromFile(const std::string& filename) -> std::vector<uint8_t> {
    std::string full_path = getDataPath(filename);

    std::ifstream file(full_path, std::ios::binary | std::ios::ate);
    if (!file) {
        std::cerr << "Error: Could not open file: " << full_path << '\n';
        return {};
    }

    std::streamsize file_size = file.tellg();
    file.seekg(0, std::ios::beg);

    std::vector<uint8_t> data(file_size);
    if (!file.read(reinterpret_cast<char*>(data.data()), file_size)) {
        std::cerr << "Error: Could not read file: " << full_path << '\n';
        return {};
    }

    return data;
}

auto ResourceLoader::getDataPath(const std::string& filename) -> std::string {
    return "data/" + filename;
}

auto ResourceLoader::getLoadedResourceCount() const -> size_t {
    if (resource_pack_ != nullptr) {
        return resource_pack_->getResourceCount();
    }
    return 0;
}

auto ResourceLoader::getAvailableResources() const -> std::vector<std::string> {
    if (resource_pack_ != nullptr) {
        return resource_pack_->getResourceList();
    }

    std::vector<std::string> result;
    if (fallback_to_files_ && std::filesystem::exists("data")) {
        for (const auto& entry : std::filesystem::recursive_directory_iterator("data")) {
            if (entry.is_regular_file()) {
                std::string filename = std::filesystem::relative(entry.path(), "data").string();
                std::ranges::replace(filename, '\\', '/');
                result.push_back(filename);
            }
        }
    }

    return result;
}