#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 &&
               qualeIfaceApi == other.qualeIfaceApi &&
               stimBuffApi == other.stimBuffApi &&
               provider == other.provider &&
               deviceSelector == other.deviceSelector;
    }

public:
    std::string deviceIdentifier;
    char sensorType;
    std::string qualeIfaceApi;
    std::vector<std::pair<std::string,std::string>> qualeIfaceApiParams;
    std::string stimBuffApi;
    std::vector<std::pair<std::string,std::string>> stimBuffApiParams;
    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
            << ", QualeIface API: " << qualeIfaceApi << ", QualeIface API Params: (";
        for (const auto& param : qualeIfaceApiParams)
        {
            os << param.first;
            if (!param.second.empty()) {
                os << "=" << param.second;
            }
            os << " ";
        }
        os << "), StimBuff API: " << stimBuffApi
            << ", StimBuff API Params: (";
        for (const auto& param : stimBuffApiParams)
        {
            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 a parameter list
     * @param params The parameter vector to search in
     * @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 std::vector<std::pair<std::string,std::string>>& params,
		const std::string& paramName
		)
    {
        auto it = std::find_if(
            params.begin(),
            params.end(),
            [&paramName](const auto& param) {
                return param.first == paramName;
            }
        );

        if (it == params.end())
        {
            throw std::runtime_error(
                "No " + paramName + " specified in 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());
        }
    }

    /**
     * @brief Parse an optional integer parameter from a parameter list using synonyms
     * @param params The parameter vector to search in
     * @param synonymNames The collection of synonymous parameter names to try
     * @param defaultValue The default value to return if no parameter is found
     * @return The parsed integer value, or defaultValue if none found
     * @note Synonyms are tried in reverse order; lattermost synonym wins if multiple are present
     */
    static int parseOptionalParamAsIntWithSynonyms(
		const std::vector<std::pair<std::string,std::string>>& params,
		const std::vector<std::string>& synonymNames,
		int defaultValue
		)
    {
        // Loop through synonyms in reverse order; lattermost synonym wins.
        for (auto synIt = synonymNames.rbegin();
            synIt != synonymNames.rend(); ++synIt)
        {
            const auto& paramName = *synIt;
            try {
                return parseRequiredParamAsInt(params, paramName);
            } catch (const std::exception&) {
                // Parameter not found or parse error, continue to next synonym
                continue;
            }
        }

        return defaultValue;
    }
};

class InteroceptorDevAttachmentSpec : public DeviceAttachmentSpec
{
};

class ExtrospectorDevAttachmentSpec : public DeviceAttachmentSpec
{
};

} // namespace device
} // namespace smo

#endif // SENSORDEVICESPEC_H
