#include "app/AssetLoader.h"
#include <SDL3_image/SDL_image.h>
#include <algorithm>
#include "app/TextureLoader.h"

#include "utils/ImagePathResolver.h"
#include <filesystem>

AssetLoader::AssetLoader() = default;

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

void AssetLoader::init(SDL_Renderer* renderer) {
    m_renderer = renderer;
}

void AssetLoader::shutdown() {
    // Destroy textures
    {
        std::lock_guard<std::mutex> lk(m_texturesMutex);
        for (auto &p : m_textures) {
            if (p.second) SDL_DestroyTexture(p.second);
        }
        m_textures.clear();
    }

    // Clear queue and errors
    {
        std::lock_guard<std::mutex> lk(m_queueMutex);
        m_queue.clear();
    }
    {
        std::lock_guard<std::mutex> lk(m_errorsMutex);
        m_errors.clear();
    }

    m_totalTasks = 0;
    m_loadedTasks = 0;
    m_renderer = nullptr;
}

void AssetLoader::setResourceManager(resources::ResourceManager* mgr) {
    m_resourceManager = mgr;
}

void AssetLoader::setBasePath(const std::string& basePath) {
    m_basePath = basePath;
}

void AssetLoader::queueTexture(const std::string& path) {
    {
        std::lock_guard<std::mutex> lk(m_queueMutex);
        m_queue.push_back(path);
    }
    m_totalTasks.fetch_add(1, std::memory_order_relaxed);
}

bool AssetLoader::performStep() {
    std::string path;
    {
        std::lock_guard<std::mutex> lk(m_queueMutex);
        if (m_queue.empty()) return true;
        path = m_queue.front();
        m_queue.erase(m_queue.begin());
    }

    {
        std::lock_guard<std::mutex> lk(m_currentLoadingMutex);
        m_currentLoading = path;
    }

    std::string fullPath = m_basePath.empty() ? path : (m_basePath + "/" + path);

    // Diagnostic: resolve path and check file existence
    const std::string resolved = AssetPath::resolveImagePath(path);
    bool exists = false;
    try { if (!resolved.empty()) exists = std::filesystem::exists(std::filesystem::u8path(resolved)); } catch (...) { exists = false; }

    // Use TextureLoader to centralize loading and ResourceManager caching
    TextureLoader loader(m_loadedTasks, m_currentLoading, m_currentLoadingMutex, m_errors, m_errorsMutex);
    loader.setResourceManager(m_resourceManager);
    // Pass the original queued path (not the full resolved path) so caching keys stay consistent
    SDL_Texture* tex = loader.loadFromImage(m_renderer, path);
    if (!tex) {
        // errors have been recorded by TextureLoader
    } else {
        std::lock_guard<std::mutex> lk(m_texturesMutex);
        auto& slot = m_textures[path];
        if (slot && slot != tex) {
            SDL_DestroyTexture(slot);
        }
        slot = tex;
    }

    m_loadedTasks.fetch_add(1, std::memory_order_relaxed);

    {
        std::lock_guard<std::mutex> lk(m_currentLoadingMutex);
        m_currentLoading.clear();
    }

    // Return true when no more queued tasks
    {
        std::lock_guard<std::mutex> lk(m_queueMutex);
        return m_queue.empty();
    }
}

void AssetLoader::adoptTexture(const std::string& path, SDL_Texture* texture) {
    if (!texture) {
        return;
    }

    // register in local map and resource manager
    std::lock_guard<std::mutex> lk(m_texturesMutex);
    auto& slot = m_textures[path];
    if (slot && slot != texture) {
        SDL_DestroyTexture(slot);
    }
    slot = texture;
    if (m_resourceManager) {
        std::shared_ptr<void> sp(texture, [](void* t){ SDL_DestroyTexture(static_cast<SDL_Texture*>(t)); });
        m_resourceManager->put(path, sp);
    }
}

float AssetLoader::getProgress() const {
    int total = m_totalTasks.load(std::memory_order_relaxed);
    if (total <= 0) return 1.0f;
    int loaded = m_loadedTasks.load(std::memory_order_relaxed);
    return static_cast<float>(loaded) / static_cast<float>(total);
}

std::vector<std::string> AssetLoader::getAndClearErrors() {
    std::lock_guard<std::mutex> lk(m_errorsMutex);
    std::vector<std::string> out = m_errors;
    m_errors.clear();
    return out;
}

SDL_Texture* AssetLoader::getTexture(const std::string& path) const {
    std::lock_guard<std::mutex> lk(m_texturesMutex);
    auto it = m_textures.find(path);
    if (it == m_textures.end()) return nullptr;
    return it->second;
}

std::string AssetLoader::getCurrentLoading() const {
    std::lock_guard<std::mutex> lk(m_currentLoadingMutex);
    return m_currentLoading;
}
