#include "JsonLogic.h"

namespace json_logic
{
    Json JsonLogic::operation_string_substr(JsonArrayView values, JsonView data) const
    {
        if (values.size() < 2 || values.size() > 3)
            return Fail(__FUNCTION__, "Expected 2 or 3 arguments, but received " +
                                          std::to_string(values.size()));
        const Json string_value = ApplyInternal(values[0], data);
        if (HasError()) return Json{};
        const Json start_value = ApplyInternal(values[1], data);
        if (HasError()) return Json{};
        if (!string_value.is<const char*>())
            return Fail(__FUNCTION__, "First argument must be a string");
        if (!IsInteger(start_value))
            return Fail(__FUNCTION__, "Second argument (start index) must be an integer");

        const std::string input = string_value.as<const char*>();
        const auto raw_start = start_value.as<ArduinoJson::JsonInteger>();
        const auto start = raw_start >= 0
                               ? raw_start
                               : static_cast<ArduinoJson::JsonInteger>(input.size()) + raw_start;
        if (start < 0 || static_cast<std::size_t>(start) >= input.size())
            return Fail(__FUNCTION__, "Start index must resolve to a position inside the string");

        std::size_t length = std::string::npos;
        if (values.size() == 3)
        {
            const Json length_value = ApplyInternal(values[2], data);
            if (HasError()) return Json{};
            if (!IsInteger(length_value))
                return Fail(__FUNCTION__, "Third argument (substr length) must be an integer");

            const auto raw_length = length_value.as<ArduinoJson::JsonInteger>();
            const auto remaining = static_cast<ArduinoJson::JsonInteger>(input.size()) - start;
            const auto resolved_length = raw_length >= 0 ? raw_length : remaining + raw_length;
            if (resolved_length < 0 || resolved_length >= remaining)
                return Fail(__FUNCTION__,
                            "Substr length must be less than the remaining string length");
            length = static_cast<std::size_t>(resolved_length);
        }

        return MakeString(input.substr(static_cast<std::size_t>(start), length));
    }
}
