#pragma once

#include <switchboard/export.h>
#include <switchboard/SBAny.hpp>
#include <map>
#include <stdexcept>
#include <string>
#include <vector>

namespace switchboard {

const std::string SWITCHBOARD_NODE_NAMESPACE = "Switchboard";

const std::string NODE_CATEGORY_AUDIO_PROCESSING = "Audio Processing";
const std::string NODE_CATEGORY_MIXING = "Mixing";
const std::string NODE_CATEGORY_MUSIC = "Music";
const std::string NODE_CATEGORY_VOICE = "Voice";
const std::string NODE_CATEGORY_GENERATION = "Generation";
const std::string NODE_CATEGORY_ANALYSIS = "Analysis";
const std::string NODE_CATEGORY_EFFECTS = "Effects";
const std::string NODE_CATEGORY_UTILITY = "Utility";
const std::string NODE_CATEGORY_AI = "AI";
const std::string NODE_CATEGORY_RTC = "RTC";

struct SWITCHBOARDSDK_EXPORT NodeTypeInfo {
    std::string namespaceName;
    std::string type;
    std::string displayName;
    std::string description;
    std::vector<std::string> categories;
    SBAnyMap configuration;
    SBAnyMap properties;
    SBAnyMap actions;
    SBAnyMap events;

    std::string getFQTypeName() const {
        return namespaceName + "." + type;
    }

    /**
     * @brief Returns the schema for a single configuration key from this node type's
     *        static configuration map (populated in the per-node getNodeTypeInfo()).
     *
     * @throws std::runtime_error if @p key is not present in the configuration map.
     */
    SBAnyMap getConfiguration(const std::string& key) const {
        if (!configuration.hasKey(key)) {
            throw std::runtime_error(
                "[NodeTypeInfo] Configuration key '" + key + "' not found for node type '" + getFQTypeName() + "'."
            );
        }
        return SBAny::convert<SBAnyMap>(configuration.at(key));
    }

    /**
     * @brief Returns the schema for a single property key from this node type's
     *        static properties map (populated in the per-node getNodeTypeInfo()).
     *
     * @throws std::runtime_error if @p key is not present in the properties map.
     */
    SBAnyMap getProperty(const std::string& key) const {
        if (!properties.hasKey(key)) {
            throw std::runtime_error(
                "[NodeTypeInfo] Property key '" + key + "' not found for node type '" + getFQTypeName() + "'."
            );
        }
        return SBAny::convert<SBAnyMap>(properties.at(key));
    }

    /**
     * @brief Returns the schema for a single action key from this node type's
     *        static actions map (populated in the per-node getNodeTypeInfo()).
     *
     * @throws std::runtime_error if @p key is not present in the actions map.
     */
    SBAnyMap getAction(const std::string& key) const {
        if (!actions.hasKey(key)) {
            throw std::runtime_error(
                "[NodeTypeInfo] Action key '" + key + "' not found for node type '" + getFQTypeName() + "'."
            );
        }
        return SBAny::convert<SBAnyMap>(actions.at(key));
    }

    /**
     * @brief Returns the schema for a single event key from this node type's
     *        static events map (populated in the per-node getNodeTypeInfo()).
     *
     * @throws std::runtime_error if @p key is not present in the events map.
     */
    SBAnyMap getEvent(const std::string& key) const {
        if (!events.hasKey(key)) {
            throw std::runtime_error(
                "[NodeTypeInfo] Event key '" + key + "' not found for node type '" + getFQTypeName() + "'."
            );
        }
        return SBAny::convert<SBAnyMap>(events.at(key));
    }

    SBAnyMap toMap() const {
        return SBAnyMap({
            { "namespace", namespaceName },
            { "type", type },
            { "fqType", getFQTypeName() },
            { "displayName", displayName },
            { "description", description },
            { "categories", SBAnyVector(categories) },
            { "configuration", configuration },
            { "properties", properties },
            { "actions", actions },
            { "events", events }
        });
    }
};

}
