//
//  SBAny.hpp
//  SwitchboardSDK
//
//  Copyright © 2026 Synervoz. All rights reserved.
//

#pragma once

#include "switchboard/export.h"
#include <initializer_list>
#include <utility>
#include <string>
#include <any>
#include <map>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include <functional>
#include <cstdint>


namespace switchboard {

template <typename T>
class Result;

class SBAnyMap;
class SBAnyVector;

/// Enumeration of supported SBAny types.
enum class SBAnyType : uint32_t {
    Null, /// Null type
    Bool, /// Boolean type
    Int, /// Integer types (int32_t, int64_t, uint32_t, uint64_t)
    Float, /// Float type
    Double, /// Double type
    String, /// String type
    Vector, /// Vector type (SBAnyVector)
    Map, /// Map type (SBAnyMap)
    Function, /// Function type (std::function)
    Pointer /// Pointer type (void*)
};

/**
 * @brief A versatile container class that can hold values of various types.
 *
 * SBAny can store and manage different data types, including primitive types,
 * strings, maps, vectors, functions, and pointers. It provides constructors
 * for each supported type and methods to retrieve the stored value in a type-safe manner.
 */
class SWITCHBOARDSDK_EXPORT SBAny final {
public:
    // Default constructor - Null type
    SBAny();
    SBAny(std::monostate);

    // Boolean type
    SBAny(bool);

    // Integer types
    SBAny(int32_t);
    SBAny(int64_t);
    SBAny(uint32_t);
    SBAny(uint64_t);

    // SFINAE to avoid ambiguity with int32_t and int64_t
    static constexpr bool int_is_distinct =
        !std::is_same<int, int32_t>::value &&
        !std::is_same<int, int64_t>::value;
    template<bool Enable = int_is_distinct,
             typename std::enable_if<Enable, int>::type = 0>
    SBAny(int value) : SBAny(static_cast<int64_t>(value)) {}

    static constexpr bool uint_is_distinct =
        !std::is_same<unsigned int, uint32_t>::value &&
        !std::is_same<unsigned int, uint64_t>::value;
    template<bool Enable = uint_is_distinct,
             typename std::enable_if<Enable, int>::type = 0>
    SBAny(unsigned int value) : SBAny(static_cast<uint64_t>(value)) {}

    // Floating point types
    SBAny(float);
    SBAny(double);

    // String types
    SBAny(const char* utf8);
    SBAny(const std::string& str);

    // Map type
    SBAny(std::initializer_list<std::pair<std::string, SBAny>> init);
    SBAny(const SBAnyMap& init);

    // Vector type
    // SBAny(std::vector<SBAny> init);
    SBAny(const SBAnyVector& init);
    explicit SBAny(std::initializer_list<SBAny> init);

    // Function wrapper constructors
    SBAny(std::function<Result<SBAny>()> init); // Property Getter
    SBAny(std::function<SBAny()> init); // Property Getter (no failure)
    SBAny(std::function<Result<void>(const SBAny&)> init); // Property Setter
    SBAny(std::function<void(const SBAny&)> init); // Property Setter (no failure)
    SBAny(std::function<Result<SBAny>(const SBAnyMap&)> init); // Action

    // Pointers
    SBAny(float* init);

    // Copy constructor and assignment
    SBAny(const SBAny&);
    SBAny& operator=(const SBAny&);

    ~SBAny();

    /**
     * Gets the type of the SBAny value.
     *
     * @return The SBAnyType.
     */
    SBAnyType type() const noexcept;

    /**
     * Converts the SBAny value to a std::any.
     *
     * @return The std::any value.
     */
    std::any toAny() const;

    /**
     * Converts an any value to a pointer.
     *
     * @return The pointer.
     */
    void* toPtr() const;

    /**
     * Converts an any value to a specific type.
     *
     * @tparam T The type to convert to.
     * @param value The value.
     *
     * @return The converted value.
     */
    template <typename T>
    static T convert(const SBAny& value);

private:
    struct Impl;
    Impl* pimpl;
};

/**
 * @brief Vector of SBAny values, used for representing lists or arrays of values.
 */
class SWITCHBOARDSDK_EXPORT SBAnyVector final {
public:
    /**
     * @brief Default constructor for SBAnyVector, initializes an empty vector.
     */
    SBAnyVector();

    /**
     * @brief Copy constructor for SBAnyVector.
     *
     * @param init The SBAnyVector to copy from.
     */
    SBAnyVector(const SBAnyVector& init);

    /**
     * @brief Assignment operator for SBAnyVector.
     *
     * @param other The SBAnyVector to assign from.
     *
     * @return Reference to the assigned SBAnyVector.
     */
    SBAnyVector& operator=(const SBAnyVector& other);

    /**
     * @brief Constructor for SBAnyVector from a range of iterators.
     *
     * @param first The beginning iterator of the range.
     * @param last The ending iterator of the range.
     */
    template<typename Iterator>
    SBAnyVector(Iterator first, Iterator last);

    /**
     * @brief Constructor for SBAnyVector from a std::vector of arbitrary type T.
     * @details This constructor converts each element of the input vector to an SBAny
     *          and stores it in the SBAnyVector.
     *
     * @tparam T The type of elements in the input vector.
     * @param vec The input std::vector to convert.
     */
    template<typename T>
    explicit SBAnyVector(const std::vector<T>& vec) : SBAnyVector() {
        reserve(vec.size());
        for (const auto& item : vec) {
            emplace_back(SBAny(item));
        }
    }

    /**
     * @brief Constructor for SBAnyVector from an initializer list of SBAny values.
     *
     * @param init The initializer list of SBAny values.
     */
    explicit SBAnyVector(std::initializer_list<SBAny> init);

    /**
     * @brief Destructor for SBAnyVector, cleans up any resources used by the vector.
     */
    ~SBAnyVector();

    /**
     * @brief Reserves space in the vector for a specified number of elements.
     *
     * @param newCapacity The number of elements to reserve space for.
     */
    void reserve(size_t newCapacity);

    /**
     * @brief Gets the number of elements in the vector.
     *
     * @return The size of the vector.
     */
    [[nodiscard]] size_t size() const;

    /**
     * @brief Adds a new SBAny value to the end of the vector.
     * @details This method constructs the SBAny value in place, avoiding unnecessary copies.
     *
     * @param value The SBAny value to add.
     */
    void emplace_back(const SBAny& value);

    /**
     * @brief Adds a new SBAny value to the end of the vector.
     * @details This method is similar to emplace_back, but may involve an additional copy operation.
     *
     * @param value The SBAny value to add.
     */
    void push_back(const SBAny& value);

    /**
     * @brief Accesses an element in the vector by index.
     *
     * @param index The index of the element to access.
     *
     * @return Reference to the SBAny element at the specified index.
     */
    SBAny& operator[](size_t index);

    /**
     * @brief Accesses an element in the vector by index (const version).
     *
     * @param index The index of the element to access.
     *
     * @return Const reference to the SBAny element at the specified index.
     */
    const SBAny& operator[](size_t index) const;

    // Iterators
    using iterator = std::vector<SBAny>::iterator;
    using const_iterator = std::vector<SBAny>::const_iterator;

    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;

private:
    struct Impl;
    Impl* pimpl;
};

/**
 * @brief Map class to store and retrieve values.
 */
class SWITCHBOARDSDK_EXPORT SBAnyMap {
public:
    /**
     * Default constructor, creates an empty map.
     */
    SBAnyMap();

    /**
     * Constructor.
     * @param jsonString The JSON string.
     */
    SBAnyMap(const std::string& jsonString);

    /**
     * Copy constructor.
     *
     * @param other The SBAnyMap to copy from.
     */
    SBAnyMap(const SBAnyMap& other);

    /**
     * Assignment operator.
     *
     * @param other The other SBAnyMap to assign from.
     *
     * @return Reference to the assigned SBAnyMap.
     */
    SBAnyMap& operator=(const SBAnyMap& other);

    /**
     * Constructor with initializer list.
     *
     * @param init Initializer list of key-value pairs.
     */
    SBAnyMap(std::initializer_list<std::pair<std::string, SBAny>> init);

    /**
     * Destructor.
     */
    ~SBAnyMap();

    /**
     * Checks if a key exists in the map.
     *
     * @param key The key to check.
     *
     * @return True if the key exists, false otherwise.
     */
    [[nodiscard]] bool hasKey(const std::string& key) const;

    /**
     * Checks if the map is empty.
     *
     * @return True if the map is empty, false otherwise.
     */
    [[nodiscard]] bool empty() const;

    /**
     * Gets the size of the map.
     *
     * @return The size of the map.
     */
    [[nodiscard]] size_t size() const;

    /**
     * Gets a value from the map.
     *
     * @param key The key.
     *
     * @return The value as SBAny.
     */
    [[nodiscard]] SBAny getAny(const std::string& key) const;

    /**
     * Gets a value from the map.
     *
     * @param key The key.
     *
     * @return The value as SBAny.
     */
    [[nodiscard]] SBAny at(const std::string& key) const;

    /**
     * Gets a value from the map.
     *
     * @tparam T The type of the value.
     * @param key The key.
     *
     * @return The value.
     */
    template <typename T>
    [[nodiscard]] T get(const std::string& key) const;

    /**
     * Gets a value from the map, falling back to a default value if the key is not present.
     *
     * @tparam T The type of the value.
     * @param key The key.
     * @param defaultValue The default value to return if the key is not found.
     *
     * @return The value if found, otherwise defaultValue.
     */
    template <typename T>
    [[nodiscard]] T get(const std::string& key, std::optional<T> defaultValue) const;

    /**
     * Sets a value in the map.
     *
     * @param key The key.
     * @param value The value as SBAny.
     */
    void set(const std::string& key, const SBAny& value);

    /**
     * Erases a key from the map.
     *
     * @param key The key.
     */
    void erase(const std::string& key);

    /**
     * Gets a value from the map with an optional default value.
     *
     * @deprecated Prefer the instance method @c get(key, defaultValue) instead.
     *
     * @tparam T The type of the value.
     * @param map The map.
     * @param key The key.
     * @param defaultValue The default value if the key is not found.
     *
     * @return The value if found, otherwise the default value.
     */
    template <typename T>
    static T get(const SBAnyMap& map, const std::string& key, std::optional<T> defaultValue = std::nullopt);

    /**
     * Converts a JSON string to a map.
     *
     * @param jsonString The JSON string.
     *
     * @return The map.
     */
    static SBAnyMap jsonToMap(const std::string& jsonString);

    /**
     * Converts a map to a JSON string.
     *
     * @param map The map.
     *
     * @return The JSON string.
     */
    static std::string mapToJson(const SBAnyMap& map);

    // Iterators
    using iterator = std::map<std::string, SBAny>::iterator;
    using const_iterator = std::map<std::string, SBAny>::const_iterator;

    iterator begin();
    iterator end();
    const_iterator begin() const;
    const_iterator end() const;

    // Operator overloads
    SBAny& operator[](const std::string& key);
    const SBAny& operator[](const std::string& key) const;

private:
    struct Impl;
    Impl* pimpl;
};

}