#pragma once

#include <memory>
#include <unordered_map>
#include <typeindex>
#include <stdexcept>

/**
 * @brief Dependency injection container for managing services
 * 
 * Provides a centralized way to register and retrieve services,
 * enabling loose coupling and better testability.
 */
class ServiceContainer {
private:
    std::unordered_map<std::type_index, std::shared_ptr<void>> services_;
    
public:
    /**
     * @brief Register a service instance
     * @tparam T Service type
     * @param service Shared pointer to service instance
     */
    template<typename T>
    void registerService(std::shared_ptr<T> service) {
        services_[std::type_index(typeid(T))] = service;
    }
    
    /**
     * @brief Get a service instance
     * @tparam T Service type
     * @return Shared pointer to service instance
     * @throws std::runtime_error if service is not registered
     */
    template<typename T>
    std::shared_ptr<T> getService() {
        auto it = services_.find(std::type_index(typeid(T)));
        if (it != services_.end()) {
            return std::static_pointer_cast<T>(it->second);
        }
        throw std::runtime_error("Service not registered: " + std::string(typeid(T).name()));
    }
    
    /**
     * @brief Get a service instance (const version)
     * @tparam T Service type
     * @return Shared pointer to service instance
     * @throws std::runtime_error if service is not registered
     */
    template<typename T>
    std::shared_ptr<const T> getService() const {
        auto it = services_.find(std::type_index(typeid(T)));
        if (it != services_.end()) {
            return std::static_pointer_cast<const T>(it->second);
        }
        throw std::runtime_error("Service not registered: " + std::string(typeid(T).name()));
    }
    
    /**
     * @brief Check if a service is registered
     * @tparam T Service type
     * @return true if service is registered, false otherwise
     */
    template<typename T>
    bool hasService() const {
        return services_.find(std::type_index(typeid(T))) != services_.end();
    }
    
    /**
     * @brief Unregister a service
     * @tparam T Service type
     */
    template<typename T>
    void unregisterService() {
        services_.erase(std::type_index(typeid(T)));
    }
    
    /**
     * @brief Clear all registered services
     */
    void clear() {
        services_.clear();
    }
    
    /**
     * @brief Get the number of registered services
     * @return Number of registered services
     */
    size_t getServiceCount() const {
        return services_.size();
    }
};
