#include "AssetManager.h"
#include "../../graphics/ui/Font.h"
#include "../../audio/Audio.h"
#include "../../audio/AudioManager.h"
#include "../../audio/SoundEffect.h"
#include <SDL3/SDL.h>
#include <SDL3_image/SDL_image.h>
#include <SDL3_ttf/SDL_ttf.h>
#include <filesystem>
#include "../../utils/ImagePathResolver.h"
#include "../../core/Config.h"
#include "../../resources/AssetPaths.h"

AssetManager::AssetManager() 
    : m_renderer(nullptr)
    , m_audioSystem(nullptr)
    , m_soundSystem(nullptr)
    , m_totalLoadingTasks(0)
    , m_completedLoadingTasks(0)
    , m_loadingComplete(false)
    , m_defaultTexturePath("assets/images/")
    , m_defaultFontPath("assets/fonts/")
    , m_initialized(false) {
}

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

bool AssetManager::initialize(SDL_Renderer* renderer) {
    if (m_initialized) {
        logError("AssetManager already initialized");
        return false;
    }

    if (!renderer) {
        setError("Invalid renderer provided to AssetManager");
        return false;
    }

    m_renderer = renderer;
    
    // Get references to singleton systems
    m_audioSystem = AudioManager::get();
    m_soundSystem = &SoundEffectManager::instance();

    m_initialized = true;
    logInfo("AssetManager initialized successfully");
    return true;
}

void AssetManager::shutdown() {
    if (!m_initialized) {
        return;
    }

    logInfo("Shutting down AssetManager...");

    // Clear loading tasks
    clearLoadingTasks();

    // Cleanup textures
    for (auto& [id, texture] : m_textures) {
        if (texture) {
            SDL_DestroyTexture(texture);
            logInfo("Destroyed texture: " + id);
        }
    }
    m_textures.clear();

    // Cleanup fonts (unique_ptr handles destruction)
    m_fonts.clear();

    // Reset state
    m_renderer = nullptr;
    m_audioSystem = nullptr;
    m_soundSystem = nullptr;
    m_initialized = false;

    logInfo("AssetManager shutdown complete");
}

SDL_Texture* AssetManager::loadTexture(const std::string& id, const std::string& filepath) {
    if (!validateRenderer()) {
        return nullptr;
    }

    // Check if already loaded
    auto it = m_textures.find(id);
    if (it != m_textures.end()) {
        logInfo("Texture already loaded: " + id);
        return it->second;
    }

    // Load new texture
    SDL_Texture* texture = loadTextureFromFile(filepath);
    if (!texture) {
        setError("Failed to load texture: " + filepath);
        return nullptr;
    }

    m_textures[id] = texture;
    logInfo("Loaded texture: " + id + " from " + filepath);
    return texture;
}

SDL_Texture* AssetManager::getTexture(const std::string& id) const {
    auto it = m_textures.find(id);
    if (it != m_textures.end()) return it->second;

    // Lazy fallback: attempt to load well-known short ids from configured asset paths.
    std::vector<std::string> candidates;
    if (id == "logo") {
        candidates.push_back(std::string(Assets::LOGO));
        candidates.push_back(Config::Assets::LOGO_BMP);
    } else if (id == "logo_small") {
        candidates.push_back(Config::Assets::LOGO_SMALL_BMP);
        candidates.push_back(std::string(Assets::LOGO));
    } else if (id == "background") {
        candidates.push_back(std::string(Assets::MAIN_SCREEN));
        candidates.push_back(Config::Assets::BACKGROUND_BMP);
    } else if (id == "blocks") {
        candidates.push_back(std::string(Assets::BLOCKS_SPRITE));
        candidates.push_back(Config::Assets::BLOCKS_BMP);
    } else if (id == "asteroids") {
        candidates.push_back(std::string(Assets::ASTEROID_SPRITE));
    }

    for (const auto &candidatePath : candidates) {
        if (candidatePath.empty()) continue;
        AssetManager* self = const_cast<AssetManager*>(this);
        SDL_Texture* tex = self->loadTexture(id, candidatePath);
        if (tex) return tex;
    }

    return nullptr;
}

bool AssetManager::unloadTexture(const std::string& id) {
    auto it = m_textures.find(id);
    if (it == m_textures.end()) {
        setError("Texture not found: " + id);
        return false;
    }

    if (it->second) {
        SDL_DestroyTexture(it->second);
    }
    
    m_textures.erase(it);
    logInfo("Unloaded texture: " + id);
    return true;
}

bool AssetManager::loadFont(const std::string& id, const std::string& filepath, int baseSize) {
    // Check if already loaded
    auto it = m_fonts.find(id);
    if (it != m_fonts.end()) {
        logInfo("Font already loaded: " + id);
        return true;
    }

    // Create new font
    std::string resolvedPath = AssetPath::resolveWithBase(filepath);
    auto font = std::make_unique<FontAtlas>();
    if (!font->init(resolvedPath, baseSize)) {
        setError("Failed to initialize font: " + resolvedPath);
        return false;
    }

    m_fonts[id] = std::move(font);
    logInfo("Loaded font: " + id + " from " + resolvedPath + " (size: " + std::to_string(baseSize) + ")");
    return true;
}

FontAtlas* AssetManager::getFont(const std::string& id) const {
    auto it = m_fonts.find(id);
    return (it != m_fonts.end()) ? it->second.get() : nullptr;
}

bool AssetManager::unloadFont(const std::string& id) {
    auto it = m_fonts.find(id);
    if (it == m_fonts.end()) {
        setError("Font not found: " + id);
        return false;
    }

    // Shutdown the font before removing
    it->second->shutdown();
    m_fonts.erase(it);
    logInfo("Unloaded font: " + id);
    return true;
}

bool AssetManager::loadMusicTrack(const std::string& filepath) {
    if (!m_audioSystem) {
        setError("Audio system not available");
        return false;
    }

    std::string resolvedPath = AssetPath::resolveWithBase(filepath);

    if (!fileExists(resolvedPath)) {
        setError("Music file not found: " + resolvedPath);
        return false;
    }

    try {
        m_audioSystem->addTrackAsync(resolvedPath);
        logInfo("Added music track for loading: " + resolvedPath);
        return true;
    } catch (const std::exception& e) {
        setError("Failed to add music track: " + std::string(e.what()));
        return false;
    }
}

bool AssetManager::loadSoundEffect(const std::string& id, const std::string& filepath) {
    if (!m_soundSystem) {
        setError("Sound effect system not available");
        return false;
    }

    std::string resolvedPath = AssetPath::resolveWithBase(filepath);

    if (!fileExists(resolvedPath)) {
        setError("Sound effect file not found: " + resolvedPath);
        return false;
    }

    if (m_soundSystem->loadSound(id, resolvedPath)) {
        logInfo("Loaded sound effect: " + id + " from " + resolvedPath);
        return true;
    } else {
        setError("Failed to load sound effect: " + id + " from " + resolvedPath);
        return false;
    }
}

bool AssetManager::loadSoundEffectWithFallback(const std::string& id, const std::string& baseName) {
    if (!m_soundSystem) {
        setError("Sound effect system not available");
        return false;
    }

    const std::string basePath = "assets/music/" + baseName;
    std::string resolved = AssetPath::resolveWithExtensions(basePath, { ".wav", ".mp3" });
    if (!resolved.empty() && m_soundSystem->loadSound(id, resolved)) {
        logInfo("Loaded sound effect: " + id + " from " + resolved);
        return true;
    }

    setError("Failed to load sound effect: " + id + " (no supported audio extension found)");
    return false;
}

void AssetManager::startBackgroundMusicLoading() {
    if (!m_audioSystem) {
        setError("Audio system not available");
        return;
    }

    m_audioSystem->startBackgroundLoading();
    logInfo("Started background music loading");
}

bool AssetManager::isMusicLoadingComplete() const {
    if (!m_audioSystem) {
        return false;
    }

    return m_audioSystem->isLoadingComplete();
}

int AssetManager::getLoadedMusicTrackCount() const {
    if (!m_audioSystem) {
        return 0;
    }

    return m_audioSystem->getLoadedTrackCount();
}

void AssetManager::addLoadingTask(const LoadingTask& task) {
    m_loadingTasks.push_back(task);
    logInfo("Added loading task: " + task.id + " (" + task.filepath + ")");
}

void AssetManager::executeLoadingTasks(std::function<void(float)> progressCallback) {
    if (m_loadingTasks.empty()) {
        m_loadingComplete = true;
        if (progressCallback) progressCallback(1.0f);
        return;
    }

    logInfo("Starting progressive loading of " + std::to_string(m_loadingTasks.size()) + " loading tasks...");

    m_totalLoadingTasks = m_loadingTasks.size();
    m_completedLoadingTasks = 0;
    m_currentTaskIndex = 0;
    m_loadingComplete = false;
    m_isProgressiveLoading = true;
    m_lastLoadTime = SDL_GetTicks();
    m_musicLoadingStarted = false;
    m_musicLoadingProgress = 0.0f;
    
    // Don't execute tasks immediately - let update() handle them progressively
}

void AssetManager::update(float deltaTime) {
    if (!m_isProgressiveLoading || m_loadingTasks.empty()) {
        // Handle music loading progress simulation if assets are done
        if (m_musicLoadingStarted && !m_loadingComplete) {
            m_musicLoadingProgress += deltaTime * 0.4f; // Simulate music loading progress
            if (m_musicLoadingProgress >= 1.0f) {
                m_musicLoadingProgress = 1.0f;
                m_loadingComplete = true;
                logInfo("Background music loading simulation complete");
            }
        }
        return;
    }
    
    Uint64 currentTime = SDL_GetTicks();
    
    // Add minimum delay between loading items (600ms per item for visual effect)
    if (currentTime - m_lastLoadTime < 600) {
        return;
    }
    
    // Load one item at a time
    if (m_currentTaskIndex < m_loadingTasks.size()) {
        const auto& task = m_loadingTasks[m_currentTaskIndex];
        
        bool success = false;
        switch (task.type) {
            case LoadingTask::TEXTURE:
                success = (loadTexture(task.id, task.filepath) != nullptr);
                break;
            case LoadingTask::FONT:
                success = loadFont(task.id, task.filepath, task.fontSize);
                break;
            case LoadingTask::MUSIC:
                success = loadMusicTrack(task.filepath);
                break;
            case LoadingTask::SOUND_EFFECT:
                success = loadSoundEffect(task.id, task.filepath);
                break;
        }
        
        if (!success) {
            logError("Failed to load asset: " + task.id + " (" + task.filepath + ")");
        }
        
        m_currentTaskIndex++;
        m_completedLoadingTasks = m_currentTaskIndex;
        m_lastLoadTime = currentTime;
        
        logInfo("Asset loading progress: " + std::to_string((float)m_completedLoadingTasks / m_totalLoadingTasks * 100.0f) + "%");
        
        // Check if all asset tasks are complete
        if (m_currentTaskIndex >= m_loadingTasks.size()) {
            m_isProgressiveLoading = false;
            logInfo("Completed " + std::to_string(m_completedLoadingTasks) + "/" + std::to_string(m_totalLoadingTasks) + " loading tasks");
            
            // Start background music loading simulation
            m_musicLoadingStarted = true;
            m_musicLoadingProgress = 0.0f;
            startBackgroundMusicLoading();
        }
    }
}

void AssetManager::clearLoadingTasks() {
    m_loadingTasks.clear();
    logInfo("Cleared loading tasks");
}

bool AssetManager::isResourceLoaded(const std::string& id) const {
    return (m_textures.find(id) != m_textures.end()) || 
           (m_fonts.find(id) != m_fonts.end());
}

std::string AssetManager::getAssetPath(const std::string& relativePath) {
    // Simple path construction - could be enhanced with proper path handling
    if (relativePath.find("assets/") == 0) {
        return relativePath; // Already has assets/ prefix
    }
    return "assets/" + relativePath;
}

bool AssetManager::fileExists(const std::string& filepath) {
    std::string resolved = AssetPath::resolveWithBase(filepath);
    // Use SDL file I/O for consistency with main.cpp pattern
    SDL_IOStream* file = SDL_IOFromFile(resolved.c_str(), "rb");
    if (file) {
        SDL_CloseIO(file);
        return true;
    }
    return false;
}

SDL_Texture* AssetManager::loadTextureFromFile(const std::string& filepath) {
    if (!validateRenderer()) {
        return nullptr;
    }

    const std::string resolvedPath = AssetPath::resolveImagePath(filepath);
    SDL_Texture* texture = IMG_LoadTexture(m_renderer, resolvedPath.c_str());
    if (!texture) {
        std::string message = "Failed to load texture from: ";
        message += filepath;
        message += " (resolved: ";
        message += resolvedPath;
        message += ") - ";
        message += SDL_GetError();
        setError(message);
        return nullptr;
    }

    if (resolvedPath != filepath) {
        std::string message = "Loaded alternative image path for ";
        message += filepath;
        message += ": ";
        message += resolvedPath;
        logInfo(message);
    }

    return texture;
}

bool AssetManager::validateRenderer() const {
    if (!m_initialized) {
        const_cast<AssetManager*>(this)->setError("AssetManager not initialized");
        return false;
    }

    if (!m_renderer) {
        const_cast<AssetManager*>(this)->setError("Invalid renderer");
        return false;
    }

    return true;
}

void AssetManager::setError(const std::string& error) {
    m_lastError = error;
    logError(error);
}

void AssetManager::logInfo(const std::string& message) const {
    SDL_LogInfo(SDL_LOG_CATEGORY_APPLICATION, "[AssetManager] %s", message.c_str());
}

void AssetManager::logError(const std::string& message) const {
    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "[AssetManager] %s", message.c_str());
}

// Loading progress tracking methods
bool AssetManager::isLoadingComplete() const {
    // Loading is complete when both asset tasks and music loading are done
    return m_loadingComplete && (!m_musicLoadingStarted || m_musicLoadingProgress >= 1.0f);
}

float AssetManager::getLoadingProgress() const {
    if (m_totalLoadingTasks == 0) {
        return 1.0f; // No tasks = complete
    }
    
    // Asset loading progress (80% of total progress)
    float assetProgress = static_cast<float>(m_completedLoadingTasks) / static_cast<float>(m_totalLoadingTasks) * 0.8f;
    
    // Music loading progress (20% of total progress)
    float musicProgress = m_musicLoadingStarted ? m_musicLoadingProgress * 0.2f : 0.0f;
    
    return assetProgress + musicProgress;
}

// IAssetLoader interface implementation
SDL_Texture* AssetManager::loadTextureFromPath(const std::string& path) {
    // Use the path as both ID and filepath for the interface implementation
    return loadTexture(path, path);
}

bool AssetManager::loadFontAsset(const std::string& name, const std::string& path, int size) {
    // Delegate to the existing loadFont method
    return loadFont(name, path, size);
}

bool AssetManager::loadAudioAsset(const std::string& name, const std::string& path) {
    return loadSoundEffect(name, path);
}

SDL_Texture* AssetManager::getTextureAsset(const std::string& name) {
    // Delegate to the existing getTexture method
    return getTexture(name);
}

bool AssetManager::hasAsset(const std::string& name) const {
    return m_textures.find(name) != m_textures.end() || 
           m_fonts.find(name) != m_fonts.end();
}

void AssetManager::unloadAsset(const std::string& name) {
    // Try to unload as texture first, then as font
    if (!unloadTexture(name)) {
        unloadFont(name);
    }
}

void AssetManager::unloadAll() {
    shutdown();
}
