#ifndef SENSORDEVICESPEC_H
#define SENSORDEVICESPEC_H

#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <stdexcept>

namespace smo {
namespace device {

class DeviceAttachmentSpec
{
public:
    friend std::ostream& operator<<(
        std::ostream& os, const DeviceAttachmentSpec& spec)
    {
        os << spec.stringify();
        return os;
    }

    bool operator==(const DeviceAttachmentSpec& other) const
    {
        return deviceIdentifier == other.deviceIdentifier &&
               sensorType == other.sensorType &&
               provider == other.provider &&
               deviceSelector == other.deviceSelector;
    }

public:
    std::string deviceIdentifier;
    char sensorType;
    std::string implexor;
    std::string api;
    std::vector<std::pair<std::string,std::string>> apiParams;
    std::string provider;
    std::vector<std::pair<std::string,std::string>> providerParams;
    std::string deviceSelector;

    std::string stringify() const
    {
        std::ostringstream os;
        os << "Device Identifier: " << deviceIdentifier
            << ", Sensor Type: " << sensorType
            << ", Implexor: " << implexor << ", API: " << api
            << ", API Params: (";
        for (const auto& param : apiParams)
        {
            os << param.first;
            if (!param.second.empty()) {
                os << "=" << param.second;
            }
            os << " ";
        }
        os << "), Provider: " << provider << ", Provider Params: (";
        for (const auto& param : providerParams)
        {
            os << param.first;
            if (!param.second.empty()) {
                os << "=" << param.second;
            }
            os << " ";
        }
        os << "), Device Selector: " << deviceSelector << std::endl;

        return os.str();
    }

    /**
     * @brief Parse a required integer parameter from provider parameters
     * @param spec The device attachment specification
     * @param paramName The name of the parameter to parse
     * @return The parsed integer value
     * @throws std::runtime_error if parameter is not found or cannot be parsed
     */
    static int parseRequiredParamAsInt(
		const DeviceAttachmentSpec& spec, const std::string& paramName
		)
    {
        auto it = std::find_if(
            spec.providerParams.begin(),
            spec.providerParams.end(),
            [&paramName](const auto& param) {
                return param.first == paramName;
            }
        );

        if (it == spec.providerParams.end())
        {
            throw std::runtime_error(
                "No " + paramName + " specified in provider params");
        }

        try {
            return std::stoi(it->second);
        } catch (const std::exception& e) {
            throw std::runtime_error(
                "Failed to parse '" + paramName + "' param value '"
                + it->second + "' as integer: " + e.what());
        }
    }
};

class InteroceptorDevAttachmentSpec : public DeviceAttachmentSpec
{
};

class ExtrospectorDevAttachmentSpec : public DeviceAttachmentSpec
{
};

} // namespace device
} // namespace smo

#endif // SENSORDEVICESPEC_H
