#pragma once
#include "Concepts.hpp"
#include "Validators.hpp"
#include "renderive/base/memory/Memory_Resource.hpp"
#include <functional>
#include <memory>
#include <utility>
template <Property_Product Product_Type, Property_Set Properties_Type, Property_Validator<Properties_Type> Validator_Type = No_Property_Validator<Properties_Type>>
class Property_Builder {
public:
    using Product = Product_Type;
    using Properties = Properties_Type;
    using Validator = Validator_Type;
    using Self = Property_Builder;
    Property_Builder() requires std::default_initializable<Properties> && std::default_initializable<Validator> : properties{}, validator{} {}
    explicit Property_Builder(Properties properties) requires std::default_initializable<Validator> : properties(std::move(properties)), validator{} {}
    Property_Builder(Properties properties, Validator validator) : properties(std::move(properties)), validator(std::move(validator)) {}
    template <auto Member, Property_Member_Assignable<Properties, Member> Value>
    Self& set(Value&& value) {
        properties.*Member = std::forward<Value>(value);
        return *this;
    }
    template <Property_Configurator<Properties> Configure>
    Self& configure(Configure&& configure) {
        std::invoke(std::forward<Configure>(configure), properties);
        return *this;
    }
    const Properties& properties_value() const noexcept {
        return properties;
    }
    template <class... Args>
    requires Property_Constructible<Product, Properties, Args&&...>
    std::shared_ptr<Product> build(Args&&... args) const {
        validator(properties);
        return std::make_shared<Product>(properties, std::forward<Args>(args)...);
    }
    template <class... Args>
    requires Property_Constructible<Product, Properties, Args&&...>
    std::shared_ptr<Product> build_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const {
        validator(properties);
        return std::allocate_shared<Product>(std::pmr::polymorphic_allocator<Product>(&memory_resource), properties, std::forward<Args>(args)...);
    }
    template <class... Args>
    requires Property_Constructible<Product, Properties, Args&&...>
    std::unique_ptr<Product> build_unique(Args&&... args) const {
        validator(properties);
        return std::make_unique<Product>(properties, std::forward<Args>(args)...);
    }
    template <class... Args>
    requires Property_Constructible<Product, Properties, Args&&...>
    Memory_Resource_Unique_Ptr<Product> build_unique_with_resource(std::pmr::memory_resource& memory_resource, Args&&... args) const {
        validator(properties);
        return make_memory_resource_unique<Product>(memory_resource, properties, std::forward<Args>(args)...);
    }
private:
    Properties properties;
    [[no_unique_address]] Validator validator;
};
