#pragma once
#include "Concepts.hpp"
#include "Validators.hpp"
#include <type_traits>
#include <utility>
template <std::copyable Value_Type, Value_Validator<Value_Type> Validator>
class Validated_Value {
public:
    using Value = Value_Type;
    Validated_Value() = delete;
    template <Validated_Value_Input<Value_Type, Validated_Value> Input>
    Validated_Value(Input&& input) : value(make_validated(std::forward<Input>(input))) {}
    Validated_Value(const Validated_Value&) = default;
    Validated_Value(Validated_Value&&) noexcept(std::is_nothrow_move_constructible_v<Value_Type>) = default;
    Validated_Value& operator=(const Validated_Value&) = default;
    Validated_Value& operator=(Validated_Value&&) noexcept(std::is_nothrow_move_assignable_v<Value_Type>) = default;
    template <Validated_Value_Input<Value_Type, Validated_Value> Input>
    Validated_Value& operator=(Input&& input) {
        value = make_validated(std::forward<Input>(input));
        return *this;
    }
    const Value_Type& get() const noexcept {
        return value;
    }
    operator const Value_Type&() const noexcept {
        return value;
    }
private:
    template <Validated_Value_Input<Value_Type, Validated_Value> Input>
    static Value_Type make_validated(Input&& input) {
        Value_Type result(std::forward<Input>(input));
        Validator{}(result);
        return result;
    }
    Value_Type value;
};
template <std::totally_ordered Value, Value Min, Value Max>
using Range_Value = Validated_Value<Value, Range_Validator<Value, Min, Max>>;
