/**
 * @file mcp_resource.cpp
 * @brief Resource implementation for MCP
 * 
 * This file implements the resource classes for the Model Context Protocol.
 * Follows the 2024-11-05 basic protocol specification.
 */
#include "mcp_resource.h"
#include <filesystem>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <stdexcept>

namespace fs = std::filesystem;

namespace mcp {

// file_resource implementation
file_resource::file_resource(const std::string& base_path)
    : base_path_(base_path) {
    // Ensure base path exists
    if (!fs::exists(base_path_)) {
        throw mcp_exception(error_code::internal_error, 
                           "Base path does not exist: " + base_path_);
    }
}

json file_resource::get_metadata() const {
    return {
        {"type", "file_resource"},
        {"base_path", base_path_},
        {"description", "File system resource for accessing files"}
    };
}

json file_resource::access(const json& params) const {
    // Extract the path from the parameters
    if (!params.contains("path")) {
        throw mcp_exception(error_code::invalid_params, "Missing 'path' parameter");
    }
    
    std::string path = params["path"];
    
    // Remove trailing slash if present
    if (!path.empty() && path.back() == '/') {
        path.pop_back();
    }
    
    // Handle different operations
    if (params.contains("operation")) {
        std::string operation = params["operation"];
        
        if (operation == "read") {
            return read_file(path);
        } else if (operation == "write") {
            if (!params.contains("content")) {
                throw mcp_exception(error_code::invalid_params, "Missing 'content' parameter for write operation");
            }
            return write_file(path, params["content"]);
        } else if (operation == "delete") {
            return delete_file(path);
        } else if (operation == "list") {
            return list_directory(path);
        } else {
            throw mcp_exception(error_code::invalid_params, "Invalid operation: " + operation);
        }
    }
    
    // Default to read operation
    std::string full_path = base_path_ + path;
    if (fs::is_directory(full_path)) {
        return list_directory(path);
    } else {
        return read_file(path);
    }
}

json file_resource::read_file(const std::string& path) const {
    std::string full_path = base_path_ + path;
    
    // Check if file exists
    if (!fs::exists(full_path) || !fs::is_regular_file(full_path)) {
        throw mcp_exception(error_code::invalid_params, "File not found: " + path);
    }
    
    // Read file content
    std::ifstream file(full_path, std::ios::binary);
    if (!file) {
        throw mcp_exception(error_code::internal_error, 
                           "Failed to open file: " + path);
    }
    
    std::stringstream buffer;
    buffer << file.rdbuf();
    
    // Get file info
    std::string content = buffer.str();
    std::string ext = fs::path(full_path).extension().string();
    
    return {
        {"path", path},
        {"content", content},
        {"size", content.size()},
        {"extension", ext},
        {"last_modified", fs::last_write_time(full_path).time_since_epoch().count()}
    };
}

json file_resource::write_file(const std::string& path, const std::string& content) const {
    std::string full_path = base_path_ + path;
    
    // Create directories if they don't exist
    fs::path dir_path = fs::path(full_path).parent_path();
    if (!fs::exists(dir_path)) {
        try {
            fs::create_directories(dir_path);
        } catch (const std::exception& e) {
            throw mcp_exception(error_code::internal_error, 
                               "Failed to create directory: " + std::string(e.what()));
        }
    }
    
    // Write file content
    std::ofstream file(full_path, std::ios::binary);
    if (!file) {
        throw mcp_exception(error_code::internal_error, 
                           "Failed to open file for writing: " + path);
    }
    
    file << content;
    
    return {
        {"success", true},
        {"message", "File written successfully"},
        {"path", path},
        {"size", content.size()}
    };
}

json file_resource::delete_file(const std::string& path) const {
    std::string full_path = base_path_ + path;
    
    // Check if file exists
    if (!fs::exists(full_path)) {
        throw mcp_exception(error_code::invalid_params, "File not found: " + path);
    }
    
    // Delete file
    try {
        fs::remove(full_path);
    } catch (const std::exception& e) {
        throw mcp_exception(error_code::internal_error, 
                           "Failed to delete file: " + std::string(e.what()));
    }
    
    return {
        {"success", true},
        {"message", "File deleted successfully"},
        {"path", path}
    };
}

json file_resource::list_directory(const std::string& path) const {
    std::string full_path = base_path_ + path;
    
    // Check if directory exists
    if (!fs::exists(full_path) || !fs::is_directory(full_path)) {
        throw mcp_exception(error_code::invalid_params, "Directory not found: " + path);
    }
    
    // List directory contents
    json entries = json::array();
    
    for (const auto& entry : fs::directory_iterator(full_path)) {
        std::string entry_path = entry.path().string();
        // Convert to relative path
        entry_path = entry_path.substr(base_path_.length());
        
        json entry_json = {
            {"name", entry.path().filename().string()},
            {"path", entry_path},
            {"is_directory", entry.is_directory()}
        };
        
        if (!entry.is_directory()) {
            entry_json["size"] = entry.file_size();
            entry_json["last_modified"] = fs::last_write_time(entry.path()).time_since_epoch().count();
        }
        
        entries.push_back(entry_json);
    }
    
    return {
        {"path", path},
        {"entries", entries}
    };
}

// api_resource implementation
api_resource::api_resource(const std::string& name, const std::string& description)
    : name_(name), description_(description) {
}

json api_resource::get_metadata() const {
    json endpoints = json::object();
    
    for (const auto& [endpoint, info] : endpoints_) {
        endpoints[endpoint] = {
            {"description", info.description}
        };
    }
    
    return {
        {"type", "api_resource"},
        {"name", name_},
        {"description", description_},
        {"endpoints", endpoints}
    };
}

json api_resource::access(const json& params) const {
    // Extract the endpoint from the parameters
    if (!params.contains("endpoint")) {
        throw mcp_exception(error_code::invalid_params, "Missing 'endpoint' parameter");
    }
    
    std::string endpoint = params["endpoint"];
    
    // Find the endpoint handler
    auto it = endpoints_.find(endpoint);
    if (it == endpoints_.end()) {
        throw mcp_exception(error_code::invalid_params, "Endpoint not found: " + endpoint);
    }
    
    // Call the handler
    try {
        return it->second.handler(params);
    } catch (const mcp_exception& e) {
        throw; // Re-throw MCP exceptions
    } catch (const std::exception& e) {
        throw mcp_exception(error_code::internal_error, 
                           "Error in endpoint handler: " + std::string(e.what()));
    }
}

void api_resource::register_handler(const std::string& endpoint, 
                                   handler_func handler,
                                   const std::string& description) {
    endpoint_info info;
    info.handler = handler;
    info.description = description;
    
    endpoints_[endpoint] = info;
}

} // namespace mcp