#ifndef DAAS_WRAPPER_H
#define DAAS_WRAPPER_H

#include "daas.hpp"
#include "daas_types.hpp"
#include <string>
#include <unordered_map>
#include <vector>

/**
 * @file daas_wrapper.h
 * @brief High-level wrapper for the DaaS-IoT SDK.
 *
 * @details
 * This class encapsulates the low-level DaasAPI and exposes a simpler, safer API
 * to initialize a node, enable drivers, and send/receive DDO packets.
 * It also provides safe-by-default helpers to represent/print DDO payloads,
 * avoiding common pitfalls with C-strings and binary data.
 *
 * © 2025 Sebyone Srl. All rights reserved.
 */

namespace daas {
namespace api {

/**
 * @struct DriverConfig
 * @brief Driver configuration entry used by the wrapper when loading from JSON.
 */
struct DriverConfig {
  link_t      link;   ///< Driver/link type (e.g., LINK_INET4).
  std::string uri;    ///< Driver URI (e.g., "127.0.0.1:3000").
};

/**
 * @struct InterfaceInfo
 * @brief Basic information about a system network interface.
 */
struct InterfaceInfo {
  std::string name;    ///< Interface name (e.g., "eth0").
  std::string type;    ///< Interface type (implementation-defined).
  std::string status;  ///< Interface status (implementation-defined).
};

/**
 * @class DaasWrapper
 * @brief Convenience wrapper around libdaas.a (DaasAPI).
 *
 * Internally uses DaasAPI to expose high-level methods for initialization,
 * driver configuration, and message transmission/reception.
 *
 * @warning
 * Unless explicitly stated, returned references mirror the underlying DaasAPI
 * behavior. In particular, see @ref pull for DDO ownership and lifetime.
 */
class DaasWrapper {
private:
    /**
     * @brief Pointer to the internal DaaS API instance used to communicate with the core library.
     */
    DaasAPI* daasInstance{nullptr};

    /**
     * @brief Pointer to the event handler interface used to receive DaaS-related callbacks (not owned).
     */
    IDaasApiEvent* eventHandler{nullptr};

    /**
     * @brief Internal buffer used to store temporary information or debug strings.
     */
    char infoBuffer[256]{};

    /**
     * @brief In-memory cache of configuration key-value pairs loaded from a configuration file.
     */
    std::unordered_map<std::string, std::string> configCache;

    /**
     * @brief Cache of enabled drivers loaded from the configuration file or at runtime.
     */
    std::vector<DriverConfig> driversCache;

public:
    /**
     * @brief Constructor.
     *
     * @param config       Optional path to a configuration file (may be nullptr).
     * @param eventHandler Custom handler for DaaS events (not owned; may be nullptr).
     */
    DaasWrapper(const char* config, IDaasApiEvent* eventHandler);

    /**
     * @brief Destroys the wrapper and its DaasAPI instance.
     */
    ~DaasWrapper();

    // ---- Interfaces / Networking ----

    /**
     * @brief Returns the list of currently active network interfaces on the system.
     *
     * This method scans the system's network interfaces and returns the names of those
     * that are marked as "up" (IFF_UP). Duplicate names are removed.
     *
     * @return Vector of interface names that are active.
     */
    std::vector<std::string> getActiveInterfaces();

    /**
     * @brief Returns detailed information about all detected system interfaces.
     *
     * This method inspects all available interfaces using `getifaddrs()` and
     * `/sys/class/net/<iface>` to extract their name, type (Ethernet, WiFi, etc.),
     * and operational status ("UP"/"DOWN"). It also attempts to detect Bluetooth interfaces
     * under `/sys/class/bluetooth`, marking them as "Bluetooth (HCI)" with "UNKNOWN" status.
     *
     * @return Vector of InterfaceInfo objects with name, type, and status.
     */
    std::vector<InterfaceInfo> listSystemInterfaces();

    // ---- Lifecycle / State ----

    /**
     * @brief Initializes the DaaS node with SID and DIN.
     *
     * @param sid System Identifier (e.g., network ID).
     * @param din DaaS Identifier (node ID).
     * @return Error code ::daas_error_t.
     */
    daas_error_t doInit(din_t sid, din_t din);

    /**
     * @brief Starts the internal execution of the node.
     *
     * @param mode Execution mode (e.g., PERFORM_CORE_THREAD).
     * @return Error code ::daas_error_t.
     */
    daas_error_t doPerform(performs_mode_t mode);

    /**
     * @brief Terminates the node activities.
     *
     * @return Error code ::daas_error_t.
     */
    daas_error_t doEnd();

    /**
     * @brief Resets the node state.
     *
     * @return Error code ::daas_error_t.
     */
    daas_error_t doReset();

    /**
     * @brief Returns the version of the libdaas.a library.
     *
     * @return Version string (e.g., "0.20.1").
     */
    const char* getVersion();

    /**
     * @brief Returns a short information string about the library and/or node.
     *
     * Typical content might include version/build information or internal status,
     * as provided by the underlying DaasAPI.
     *
     * @return Pointer to an internal buffer valid until the next call.
     */
    const char* getInfos();

    // ---- Drivers / Setup ----

    /**
     * @brief Returns the list of available drivers.
     *
     * The format is implementation-defined, but a typical example is:
     * `"2.INET4;3.UART"`.
     *
     * @return Driver list string.
     */
    const char* listAvailableDrivers();

    /**
     * @brief Enables a driver on a specific link.
     *
     * @param link   Interface type (e.g., LINK_INET4).
     * @param driver URI to use (e.g., "127.0.0.1:3000").
     * @return Error code ::daas_error_t.
     */
    daas_error_t enableDriver(link_t link, const char* driver);

    /**
     * @brief Quickly configures the local node using inline parameters.
     *
     * This helper wraps a typical minimal setup:
     *  - Initializes the node with the given SID/DIN.
     *  - Enables the specified driver on the given link.
     *
     * Remote mapping can be performed later via @ref map.
     *
     * @param sid  System ID of the local node.
     * @param din  Node ID of the local node.
     * @param link Interface type.
     * @param uri  URI for the driver (e.g., "192.168.1.10:2020").
     * @return Error code ::daas_error_t.
     */
    daas_error_t setupNode(din_t sid, din_t din, link_t link, const char* uri);

    /**
     * @brief Loads and configures a node from a JSON setup file.
     *
     * The JSON file can describe SID/DIN, drivers, mappings and other
     * configuration details understood by the wrapper.
     *
     * @param setupFilePath Path to the JSON file.
     * @return Error code ::daas_error_t.
     */
    daas_error_t setupNode(const char* setupFilePath);

    // ---- Network / Nodes ----

    /**
     * @brief Returns the current status of the local node.
     *
     * @return Node status as ::nodestate_t.
     */
    nodestate_t getStatus();

    /**
     * @brief Returns the status of a given DIN.
     *
     * This mirrors the behavior of the underlying DaasAPI and returns a
     * reference to an internal status structure.
     *
     * @param din DaaS Identifier of the node.
     * @return Reference to node status.
     */
    const nodestate_t& status(din_t din);

    /**
     * @brief Fetches a node's status with optional flags.
     *
     * @param din  DaaS Identifier.
     * @param opts Fetch options (implementation-defined).
     * @return Reference to node status.
     */
    const nodestate_t& fetch(din_t din, uint16_t opts);

    /**
     * @brief Returns the list of known nodes.
     *
     * @return List of DINs as ::dinlist_t.
     */
    dinlist_t listNodes();

    /**
     * @brief Saves the current configuration using an IDepot interface.
     *
     * This may save SID/DIN, mappings, driver settings or other
     * configuration data, depending on the wrapper implementation.
     *
     * @param storage_interface Pointer to an ::IDepot object.
     * @return true if the save was successful, false otherwise.
     */
    bool storeConfiguration(IDepot* storage_interface);

    /**
     * @brief Loads the configuration using an IDepot interface.
     *
     * Restores configuration previously stored via @ref storeConfiguration.
     *
     * @param storage_interface Pointer to an ::IDepot object.
     * @return true if the load was successful, false otherwise.
     */
    bool loadConfiguration(IDepot* storage_interface);

    // ---- Mapping / Communication ----

    /**
     * @brief Maps a node with known DIN (basic form).
     *
     * @param din Remote node DIN.
     * @return Error code ::daas_error_t.
     */
    daas_error_t map(din_t din);

    /**
     * @brief Maps a node specifying link and URI.
     *
     * @param din  Remote node DIN.
     * @param link Interface type.
     * @param uri  Remote interface/address.
     * @return Error code ::daas_error_t.
     */
    daas_error_t map(din_t din, link_t link, const char* uri);

    /**
     * @brief Maps a node using a security key.
     *
     * @param din         Remote DIN.
     * @param link        Interface type.
     * @param uri         Remote address.
     * @param securityKey Security key (e.g., AES key).
     * @return Error code ::daas_error_t.
     */
    daas_error_t map(din_t din, link_t link, const char* uri, const char* securityKey);

    /**
     * @brief Removes a previously mapped node.
     *
     * @param din Remote node DIN to remove.
     * @return Error code ::daas_error_t.
     */
    daas_error_t remove(din_t din);

    /**
     * @brief Locates a node on the network.
     *
     * @param din Remote DIN to locate.
     * @return Error code ::daas_error_t.
     */
    daas_error_t locate(din_t din);

    /**
     * @brief Returns the number of packets available for reception.
     *
     * @param din   Node DIN to query.
     * @param count Output parameter set to the number of pending packets.
     * @return Error code ::daas_error_t.
     */
    daas_error_t availablesPull(din_t din, uint32_t& count);

    /**
     * @brief Receives a DDO from a remote node.
     *
     * @param din        Sender DIN.
     * @param inboundDDO Output pointer set to the received DDO.
     * @return Error code ::daas_error_t.
     *
     * @warning
     * The returned DDO pointer is owned by the underlying DaasAPI/libdaas.
     * Do not delete it. Copy or serialize the payload if you need to keep it
     * beyond its lifetime.
     */
    daas_error_t pull(din_t din, DDO** inboundDDO);

    /**
     * @brief Sends a DDO to a remote node.
     *
     * @param din         Destination DIN.
     * @param outboundDDO Pointer to the DDO to send; must remain valid for
     *                    the duration of the call.
     * @return Error code ::daas_error_t.
     */
    daas_error_t push(din_t din, DDO* outboundDDO);

    /**
     * @brief Executes a quick fire-and-forget send.
     *
     * The semantics are analogous to a "frisbee": the message is sent without
     * expecting a response or additional tracking.
     *
     * @param din Destination DIN.
     * @return Error code ::daas_error_t.
     */
    daas_error_t frisbee(din_t din);

    /**
     * @brief Sets a new event handler for asynchronous callbacks.
     *
     * The handler is not owned by the wrapper; the caller must ensure its
     * lifetime exceeds that of the wrapper or until another handler is set.
     *
     * @param event New handler to use (may be nullptr to disable callbacks).
     */
    void setEventHandler(IDaasApiEvent* event);

    // ---- Safe-by-default helpers for DDO payload ----

    /**
     * @brief Returns the DDO payload as a std::string honoring the exact size.
     *
     * This helper builds a std::string from the payload buffer using the
     * payload length stored in the DDO structure, without assuming a trailing
     * NUL terminator.
     *
     * @param ddo Reference to the DDO containing the payload.
     * @return std::string containing the payload bytes (may include '\0').
     */
    std::string ddoPayloadAsString(DDO& ddo);

    /**
     * @brief Returns the DDO payload as a NUL-terminated C-like string.
     *
     * A copy of the payload is created and a trailing '\0' is appended so
     * that the result can safely be used with legacy APIs expecting
     * C-strings (e.g., printf("%s")).
     *
     * @param ddo Reference to the DDO containing the payload.
     * @return std::string containing a NUL-terminated view of the payload.
     */
    std::string ddoPayloadAsCString(DDO& ddo);

    /**
     * @brief Returns a hexadecimal representation of the DDO payload.
     *
     * Each payload byte is converted to two lowercase hexadecimal characters.
     * Optionally, the number of bytes to convert can be limited.
     *
     * @param ddo       Reference to the DDO containing the payload.
     * @param max_bytes Optional limit on the number of bytes to convert
     *                  (0 = all bytes in the payload).
     * @return std::string with the hexadecimal representation of the payload.
     */
    std::string ddoPayloadAsHex(DDO& ddo, size_t max_bytes = 0);
};

} // namespace api
} // namespace daas

#endif // DAAS_WRAPPER_H
