#pragma once

#include "common/types.hpp"
#include "common/utils.hpp"

#include <nlohmann/json.hpp>

#include <filesystem>
#include <map>
#include <string>
#include <tuple>
#include <vector>

namespace pldm::responder::events
{

/** @struct StateSensorEntry
 *
 *  StateSensorEntry is a key to uniquely identify a state sensor, so that a
 *  D-Bus action can be defined for PlatformEventMessage command with
 *  sensorEvent type. This struct is used as a key in a std::map so implemented
 *  operator== and operator<.
 */
struct StateSensorEntry
{
    pdr::ContainerID containerId;
    pdr::EntityType entityType;
    pdr::EntityInstance entityInstance;
    pdr::SensorOffset sensorOffset;
    pdr::StateSetId stateSetid;
    bool skipContainerId;

    bool operator==(const StateSensorEntry& e) const
    {
        if (!skipContainerId)
        {
            return ((containerId == e.containerId) &&
                    (entityType == e.entityType) &&
                    (entityInstance == e.entityInstance) &&
                    (sensorOffset == e.sensorOffset) &&
                    (stateSetid == e.stateSetid));
        }
        else
        {
            return ((entityType == e.entityType) &&
                    (entityInstance == e.entityInstance) &&
                    (sensorOffset == e.sensorOffset) &&
                    (stateSetid == e.stateSetid));
        }
    }

    bool operator<(const StateSensorEntry& e) const
    {
        if (!skipContainerId)
        {
            return std::tie(entityType, entityInstance, containerId,
                            sensorOffset, stateSetid) <
                   std::tie(e.entityType, e.entityInstance, e.containerId,
                            e.sensorOffset, e.stateSetid);
        }
        else
        {
            return std::tie(entityType, entityInstance, sensorOffset,
                            stateSetid) <
                   std::tie(e.entityType, e.entityInstance, e.sensorOffset,
                            e.stateSetid);
        }
    }
};

using StateToDBusValue = std::map<pdr::EventState, pldm::utils::PropertyValue>;
using EventDBusInfo = std::tuple<pldm::utils::DBusMapping, StateToDBusValue>;
using EventMap = std::map<StateSensorEntry, EventDBusInfo>;
using Json = nlohmann::json;

/** @class StateSensorHandler
 *
 *  @brief Parses the event state sensor configuration JSON file and build
 *         the lookup data structure, which can map the event state for a
 *         sensor in the PlatformEventMessage command to a D-Bus property and
 *         the property value.
 */
class StateSensorHandler
{
  public:
    StateSensorHandler() = delete;

    /** @brief Parse the event state sensor configuration JSON file and build
     *         the lookup data structure.
     *
     *  @param[in] dirPath - directory path which has the config JSONs
     */
    explicit StateSensorHandler(const std::string& dirPath);
    virtual ~StateSensorHandler() = default;
    StateSensorHandler(const StateSensorHandler&) = default;
    StateSensorHandler& operator=(const StateSensorHandler&) = default;
    StateSensorHandler(StateSensorHandler&&) = default;
    StateSensorHandler& operator=(StateSensorHandler&&) = default;

    /** @brief If the StateSensorEntry and EventState is valid, the D-Bus
     *         property corresponding to the StateSensorEntry is set based on
     *         the EventState
     *
     *  @param[in] entry - state sensor entry
     *  @param[in] state - event state
     *
     *  @return PLDM completion code
     */
    int eventAction(const StateSensorEntry& entry, pdr::EventState state);

    /** @brief Helper API to get D-Bus information for a StateSensorEntry
     *
     *  @param[in] entry - state sensor entry
     *
     *  @return D-Bus information corresponding to the SensorEntry
     */
    const EventDBusInfo& getEventInfo(const StateSensorEntry& entry) const
    {
        return eventMap.at(entry);
    }

  private:
    EventMap eventMap; //!< a map of StateSensorEntry to D-Bus information

    /** @brief Create a map of EventState to D-Bus property values from
     *         the information provided in the event state configuration
     *         JSON
     *
     *  @param[in] eventStates - a JSON array of event states
     *  @param[in] propertyValues - a JSON array of D-Bus property values
     *  @param[in] type - the type of D-Bus property
     *
     *  @return a map of EventState to D-Bus property values
     */
    StateToDBusValue mapStateToDBusVal(const Json& eventStates,
                                       const Json& propertyValues,
                                       std::string_view type);
};

} // namespace pldm::responder::events