#include "app/TextureLoader.h"

#include <SDL3_image/SDL_image.h>

#include <algorithm>
#include <mutex>
#include <sstream>

#include <filesystem>

#include "utils/ImagePathResolver.h"

TextureLoader::TextureLoader(
    std::atomic<int>& loadedTasks,
    std::string& currentLoadingFile,
    std::mutex& currentLoadingMutex,
    std::vector<std::string>& assetLoadErrors,
    std::mutex& assetLoadErrorsMutex)
    : loadedTasks_(loadedTasks)
    , currentLoadingFile_(currentLoadingFile)
    , currentLoadingMutex_(currentLoadingMutex)
    , assetLoadErrors_(assetLoadErrors)
    , assetLoadErrorsMutex_(assetLoadErrorsMutex)
{
}

void TextureLoader::setCurrentLoadingFile(const std::string& filename) {
    std::lock_guard<std::mutex> lk(currentLoadingMutex_);
    currentLoadingFile_ = filename;
}

void TextureLoader::clearCurrentLoadingFile() {
    std::lock_guard<std::mutex> lk(currentLoadingMutex_);
    currentLoadingFile_.clear();
}

void TextureLoader::recordAssetLoadError(const std::string& message) {
    std::lock_guard<std::mutex> lk(assetLoadErrorsMutex_);
    assetLoadErrors_.emplace_back(message);
}

SDL_Texture* TextureLoader::loadFromImage(SDL_Renderer* renderer, const std::string& path, int* outW, int* outH) {
    if (!renderer) {
        return nullptr;
    }

    const std::string resolvedPath = AssetPath::resolveImagePath(path);
    setCurrentLoadingFile(resolvedPath.empty() ? path : resolvedPath);

    // Check filesystem existence for diagnostics (no console log)
    bool fileExists = false;
    try { if (!resolvedPath.empty()) fileExists = std::filesystem::exists(std::filesystem::u8path(resolvedPath)); } catch (...) { fileExists = false; }
    // If resource manager provided, check cache first using the original asset key (path)
    if (resourceManager_) {
        if (auto sp = resourceManager_->get<SDL_Texture>(path)) {
            clearCurrentLoadingFile();
            loadedTasks_.fetch_add(1);
            return sp.get();
        }
    }

    SDL_Surface* surface = IMG_Load(resolvedPath.c_str());
    if (!surface) {
        {
            std::ostringstream ss;
            ss << "Image load failed: " << path << " (" << resolvedPath << "): " << SDL_GetError();
            recordAssetLoadError(ss.str());
        }
        loadedTasks_.fetch_add(1);
        clearCurrentLoadingFile();
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to load image %s (resolved: %s) exists=%s: %s", path.c_str(), resolvedPath.c_str(), fileExists ? "yes" : "no", SDL_GetError());
        return nullptr;
    }

    if (outW) {
        *outW = surface->w;
    }
    if (outH) {
        *outH = surface->h;
    }

    SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
    // surface size preserved in outW/outH; no console log
    SDL_DestroySurface(surface);

    if (!texture) {
        {
            std::ostringstream ss;
            ss << "Texture create failed: " << resolvedPath << ": " << SDL_GetError();
            recordAssetLoadError(ss.str());
        }
        loadedTasks_.fetch_add(1);
        clearCurrentLoadingFile();
        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to create texture from %s: %s", resolvedPath.c_str(), SDL_GetError());
        return nullptr;
    }

    // No texture-size console diagnostics here

    // cache in resource manager if present
    if (resourceManager_) {
        std::shared_ptr<void> sp(texture, [](void* t){ SDL_DestroyTexture(static_cast<SDL_Texture*>(t)); });
        // store under original asset key (path) so callers using logical asset names find them
        resourceManager_->put(path, sp);
    }

    loadedTasks_.fetch_add(1);
    clearCurrentLoadingFile();

    if (resolvedPath != path) {
        SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "Loaded %s via %s", path.c_str(), resolvedPath.c_str());
    }

    return texture;
}
