//------------------------------------------------------------------------------
/*
    This file is part of clio: https://github.com/XRPLF/clio
    Copyright (c) 2024, the clio developers.

    Permission to use, copy, modify, and distribute this software for any
    purpose with or without fee is hereby granted, provided that the above
    copyright notice and this permission notice appear in all copies.

    THE  SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    WITH  REGARD  TO  THIS  SOFTWARE  INCLUDING  ALL  IMPLIED  WARRANTIES  OF
    MERCHANTABILITY  AND  FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    ANY  SPECIAL,  DIRECT,  INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    WHATSOEVER  RESULTING  FROM  LOSS  OF USE, DATA OR PROFITS, WHETHER IN AN
    ACTION  OF  CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================

#pragma once

#include "util/config/ConfigValue.hpp"
#include "util/config/Error.hpp"
#include "util/config/Types.hpp"

#include <cstddef>
#include <optional>
#include <ostream>
#include <string_view>
#include <vector>

namespace util::config {

/**
 * @brief Array definition to store multiple values provided by the user from Json/Yaml
 *
 * Used in ClioConfigDefinition to represent multiple potential values (like whitelist)
 * Is constructed with only 1 element which states which type/constraint must every element
 * In the array satisfy
 */
class Array {
public:
    /**
     * @brief Constructs an Array with provided Arg
     *
     * @param arg Argument to set the type and constraint of ConfigValues in Array
     */
    Array(ConfigValue arg);

    /**
     * @brief Extract array prefix from a key, For example for a key foo.[].bar the method will return foo.[]
     * @note Provided key must contain '.[]'
     * @warning Be careful with string_view! Returned value is valid only while the key is valid
     *
     * @param key The key to extract the array prefix from
     * @return Prefix of array extracted from the key
     */
    static std::string_view
    prefix(std::string_view key);

    /**
     * @brief Add ConfigValues to Array class
     *
     * @param value The ConfigValue to add
     * @param key optional string key to include that will show in error message
     * @return optional error if adding config value to array fails. nullopt otherwise
     */
    [[nodiscard]] std::optional<Error>
    addValue(Value value, std::optional<std::string_view> key = std::nullopt);

    /**
     * @brief Add null value to the array
     * @note The error will be returned if item pattern of the array is neither optional nor has a default value
     *
     * @param key An optional key which will be used for error message only (if any)
     * @return An error if any or nullopt if the operation succeeded
     */
    [[nodiscard]] std::optional<Error>
    addNull(std::optional<std::string_view> key = std::nullopt);

    /**
     * @brief Returns the number of values stored in the Array
     *
     * @return Number of values stored in the Array
     */
    [[nodiscard]] size_t
    size() const;

    /**
     * @brief Returns the ConfigValue at the specified index
     *
     * @param idx Index of the ConfigValue to retrieve
     * @return ConfigValue at the specified index
     */
    [[nodiscard]] ConfigValue const&
    at(std::size_t idx) const;

    /**
     * @brief Returns the ConfigValue that defines the type/constraint every
     * ConfigValue must follow in Array
     *
     * @return The item_pattern
     */
    [[nodiscard]] ConfigValue const&
    getArrayPattern() const;

    /**
     * @brief Returns an iterator to the beginning of the ConfigValue vector.
     *
     * @return A constant iterator to the beginning of the vector.
     */
    [[nodiscard]] std::vector<ConfigValue>::const_iterator
    begin() const;

    /**
     * @brief Returns an iterator to the end of the ConfigValue vector.
     *
     * @return A constant iterator to the end of the vector.
     */
    [[nodiscard]] std::vector<ConfigValue>::const_iterator
    end() const;

private:
    ConfigValue itemPattern_;
    std::vector<ConfigValue> elements_;
};

/**
 * @brief Custom output stream for Array
 *
 * @param stream The output stream
 * @param arr The Array
 * @return The same ostream we were given
 */
inline std::ostream&
operator<<(std::ostream& stream, Array arr)
{
    stream << arr.getArrayPattern();
    return stream;
}

}  // namespace util::config
