// src/ResourceManager.cpp

#include "ResourceManager.h"
#include "Shader.h"
#include "Mesh.h"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <unordered_set>
#include <algorithm>

// Static member definitions
std::unordered_map<std::string, std::shared_ptr<Shader>> ResourceManager::shaders_;
std::unordered_map<std::string, std::shared_ptr<Texture>> ResourceManager::textures_;
std::unordered_map<std::string, std::shared_ptr<Mesh>>    ResourceManager::meshes_;
bool                                                       ResourceManager::hot_reload_enabled_ = false;
std::unordered_map<std::string, std::filesystem::file_time_type> ResourceManager::shader_timestamps_;
std::unordered_map<std::string, std::filesystem::path> ResourceManager::shader_paths_;
std::unordered_map<std::string, std::filesystem::file_time_type> ResourceManager::texture_timestamps_;
std::unordered_map<std::string, std::filesystem::path> ResourceManager::texture_paths_;

std::expected<std::shared_ptr<Shader>, std::string> 
ResourceManager::LoadShader(const std::string& name,
                            const std::filesystem::path& vert,
                            const std::filesystem::path& frag) {
    try {
        auto shader = std::make_shared<Shader>(vert, frag);
        if (!shader->IsValid()) {
            return std::unexpected("Shader compilation/linking failed: " + name);
        }
        shaders_[name] = shader;
        if (hot_reload_enabled_) {
            shader_timestamps_[name] = std::filesystem::last_write_time(vert);
            shader_paths_[name] = vert;
        }
        return shader;
    } catch (const std::exception& e) {
        return std::unexpected(std::string("Exception loading shader ") + name + ": " + e.what());
    }
}

std::expected<std::shared_ptr<Shader>, std::string>
ResourceManager::LoadShader(const std::string& name,
                            const std::filesystem::path& vert,
                            const std::filesystem::path& geom,
                            const std::filesystem::path& frag) {
    try {
        auto shader = std::make_shared<Shader>(vert, geom, frag);
        if (!shader->IsValid()) {
            return std::unexpected("Shader compilation/linking failed: " + name);
        }
        shaders_[name] = shader;
        if (hot_reload_enabled_) {
            shader_timestamps_[name] = std::filesystem::last_write_time(vert);
            shader_paths_[name] = vert;
        }
        return shader;
    } catch (const std::exception& e) {
        return std::unexpected(std::string("Exception loading shader ") + name + ": " + e.what());
    }
}

std::shared_ptr<Shader> ResourceManager::GetShader(const std::string& name) {
    auto it = shaders_.find(name);
    return (it != shaders_.end()) ? it->second : nullptr;
}

std::expected<std::shared_ptr<Texture>, std::string>
ResourceManager::LoadTexture(const std::string& name,
                             const std::filesystem::path& path,
                             bool generate_mipmaps) {
    try {
        // TODO: Replace this stub with actual texture loading
        // auto texture = std::make_shared<Texture>(path, generate_mipmaps);
        // if (!texture->IsValid()) {
        //     return std::unexpected("Texture loading failed: " + name);
        // }
        // textures_[name] = texture;
        
        if (hot_reload_enabled_) {
            texture_timestamps_[name] = std::filesystem::last_write_time(path);
            texture_paths_[name] = path;
        }
        
        // For now, return error since texture loading is not implemented
        return std::unexpected("Texture loading not implemented");
    } catch (const std::exception& e) {
        return std::unexpected(std::string("Exception loading texture ") + name + ": " + e.what());
    }
}

std::shared_ptr<Texture> ResourceManager::GetTexture(const std::string& name) {
    auto it = textures_.find(name);
    return (it != textures_.end()) ? it->second : nullptr;
}

void ResourceManager::RegisterMesh(const std::string& name, std::shared_ptr<Mesh> mesh) {
    meshes_[name] = std::move(mesh);
}

std::shared_ptr<Mesh> ResourceManager::GetMesh(const std::string& name) {
    auto it = meshes_.find(name);
    return (it != meshes_.end()) ? it->second : nullptr;
}

void ResourceManager::ClearShaders() {
    shaders_.clear();
    shader_timestamps_.clear();
    shader_paths_.clear();
}

void ResourceManager::ClearTextures() {
    textures_.clear();
    texture_timestamps_.clear();
    texture_paths_.clear();
}

void ResourceManager::ClearMeshes() {
    meshes_.clear();
}

void ResourceManager::ClearAll() {
    ClearShaders();
    ClearTextures();
    ClearMeshes();
}

void ResourceManager::PrintResourceInfo() {
    std::cout << "Shaders: " << shaders_.size()
              << ", Textures: " << textures_.size()
              << ", Meshes: " << meshes_.size() << "\n";
}

bool ResourceManager::HasShader(const std::string& name) {
    return shaders_.count(name) > 0;
}

bool ResourceManager::HasTexture(const std::string& name) {
    return textures_.count(name) > 0;
}

bool ResourceManager::HasMesh(const std::string& name) {
    return meshes_.count(name) > 0;
}

void ResourceManager::LoadShadersFromDirectory(const std::filesystem::path& dir) {
    if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
        std::cerr << "Directory does not exist: " << dir << "\n";
        return;
    }

    std::unordered_map<std::string, std::filesystem::path> vert_files;
    std::unordered_map<std::string, std::filesystem::path> frag_files;
    std::unordered_map<std::string, std::filesystem::path> geom_files;

    // Collect shader files by base name
    for (const auto& entry : std::filesystem::directory_iterator(dir)) {
        if (!entry.is_regular_file()) continue;

        auto path = entry.path();
        auto base_name = path.stem().string();
        auto extension = path.extension().string();

        if (extension == ".vert") {
            vert_files[base_name] = path;
        } else if (extension == ".frag") {
            frag_files[base_name] = path;
        } else if (extension == ".geom") {
            geom_files[base_name] = path;
        }
    }

    // Match and load shader pairs/triplets
    for (const auto& [base_name, vert_path] : vert_files) {
        auto frag_it = frag_files.find(base_name);
        if (frag_it != frag_files.end()) {
            auto geom_it = geom_files.find(base_name);
            
            if (geom_it != geom_files.end()) {
                // Load with geometry shader
                auto result = LoadShader(base_name, vert_path, geom_it->second, frag_it->second);
                if (result) {
                    std::cout << "Loaded shader with geometry: " << base_name << "\n";
                } else {
                    std::cerr << "Failed to load shader " << base_name << ": " << result.error() << "\n";
                }
            } else {
                // Load without geometry shader
                auto result = LoadShader(base_name, vert_path, frag_it->second);
                if (result) {
                    std::cout << "Loaded shader: " << base_name << "\n";
                } else {
                    std::cerr << "Failed to load shader " << base_name << ": " << result.error() << "\n";
                }
            }
        }
    }
}

void ResourceManager::LoadTexturesFromDirectory(const std::filesystem::path& dir) {
    if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
        std::cerr << "Directory does not exist: " << dir << "\n";
        return;
    }

    // Common image extensions using unordered_set instead of set
    std::unordered_set<std::string> image_extensions = {".png", ".jpg", ".jpeg", ".bmp", ".tga", ".dds"};

    for (const auto& entry : std::filesystem::directory_iterator(dir)) {
        if (!entry.is_regular_file()) continue;

        auto path = entry.path();
        auto extension = path.extension().string();
        
        // Convert to lowercase for comparison
        std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);

        if (image_extensions.count(extension)) {
            auto name = path.stem().string();
            auto result = LoadTexture(name, path);
            if (result) {
                std::cout << "Loaded texture: " << name << "\n";
            } else {
                std::cerr << "Failed to load texture " << name << ": " << result.error() << "\n";
            }
        }
    }
}

void ResourceManager::ReloadShader(const std::string& name) {
    auto shader_it = shaders_.find(name);
    if (shader_it == shaders_.end()) {
        std::cerr << "Cannot reload shader: " << name << " not found\n";
        return;
    }

    auto path_it = shader_paths_.find(name);
    if (path_it == shader_paths_.end()) {
        std::cerr << "Cannot reload shader: " << name << " path not stored\n";
        return;
    }

    try {
        // Determine fragment shader path (assume same base name, different extension)
        auto vert_path = path_it->second;
        auto frag_path = vert_path;
        frag_path.replace_extension(".frag");

        // Check if geometry shader exists
        auto geom_path = vert_path;
        geom_path.replace_extension(".geom");

        if (std::filesystem::exists(geom_path)) {
            // Reload with geometry shader
            auto new_shader = std::make_shared<Shader>(vert_path, geom_path, frag_path);
            if (new_shader->IsValid()) {
                shaders_[name] = new_shader;
                shader_timestamps_[name] = std::filesystem::last_write_time(vert_path);
                std::cout << "Reloaded shader with geometry: " << name << "\n";
            } else {
                std::cerr << "Failed to reload shader with geometry: " << name << "\n";
            }
        } else {
            // Reload without geometry shader
            auto new_shader = std::make_shared<Shader>(vert_path, frag_path);
            if (new_shader->IsValid()) {
                shaders_[name] = new_shader;
                shader_timestamps_[name] = std::filesystem::last_write_time(vert_path);
                std::cout << "Reloaded shader: " << name << "\n";
            } else {
                std::cerr << "Failed to reload shader: " << name << "\n";
            }
        }
    } catch (const std::exception& e) {
        std::cerr << "Exception reloading shader " << name << ": " << e.what() << "\n";
    }
}

void ResourceManager::ReloadTexture(const std::string& name) {
    auto texture_it = textures_.find(name);
    if (texture_it == textures_.end()) {
        std::cerr << "Cannot reload texture: " << name << " not found\n";
        return;
    }

    auto path_it = texture_paths_.find(name);
    if (path_it == texture_paths_.end()) {
        std::cerr << "Cannot reload texture: " << name << " path not stored\n";
        return;
    }

    try {
        // TODO: Replace this with actual texture reloading
        // auto new_texture = std::make_shared<Texture>(path_it->second);
        // if (new_texture->IsValid()) {
        //     textures_[name] = new_texture;
        //     texture_timestamps_[name] = std::filesystem::last_write_time(path_it->second);
        //     std::cout << "Reloaded texture: " << name << "\n";
        // } else {
        //     std::cerr << "Failed to reload texture: " << name << "\n";
        // }
        
        // For now, just update timestamp
        texture_timestamps_[name] = std::filesystem::last_write_time(path_it->second);
        std::cout << "Texture reload placeholder for: " << name << "\n";
    } catch (const std::exception& e) {
        std::cerr << "Exception reloading texture " << name << ": " << e.what() << "\n";
    }
}

void ResourceManager::CheckForChanges() {
    if (!hot_reload_enabled_) return;

    // Check for shader changes
    for (const auto& [name, shader] : shaders_) {
        auto path_it = shader_paths_.find(name);
        if (path_it == shader_paths_.end()) continue;

        try {
            auto current = std::filesystem::last_write_time(path_it->second);
            auto stored_it = shader_timestamps_.find(name);
            
            if (stored_it != shader_timestamps_.end() && current != stored_it->second) {
                std::cout << "Detected change in shader: " << name << "\n";
                ReloadShader(name);
            }
        } catch (const std::filesystem::filesystem_error& e) {
            std::cerr << "Error checking shader file " << name << ": " << e.what() << "\n";
        }
    }

    // Check for texture changes
    for (const auto& [name, texture] : textures_) {
        auto path_it = texture_paths_.find(name);
        if (path_it == texture_paths_.end()) continue;

        try {
            auto current = std::filesystem::last_write_time(path_it->second);
            auto stored_it = texture_timestamps_.find(name);
            
            if (stored_it != texture_timestamps_.end() && current != stored_it->second) {
                std::cout << "Detected change in texture: " << name << "\n";
                ReloadTexture(name);
                texture_timestamps_[name] = current;
            }
        } catch (const std::filesystem::filesystem_error& e) {
            std::cerr << "Error checking texture file " << name << ": " << e.what() << "\n";
        }
    }
}
