#pragma once

#include <utility>

namespace pp::foundation {

enum class StatusCode {
    ok,
    invalid_argument,
    out_of_range,
};

struct Status {
    StatusCode code = StatusCode::ok;
    const char* message = "ok";

    [[nodiscard]] constexpr bool ok() const noexcept
    {
        return code == StatusCode::ok;
    }

    [[nodiscard]] static constexpr Status success() noexcept
    {
        return {};
    }

    [[nodiscard]] static constexpr Status invalid_argument(const char* message) noexcept
    {
        return { StatusCode::invalid_argument, message };
    }

    [[nodiscard]] static constexpr Status out_of_range(const char* message) noexcept
    {
        return { StatusCode::out_of_range, message };
    }
};

template <typename T>
class Result {
public:
    [[nodiscard]] static constexpr Result success(T value) noexcept
    {
        return Result(std::move(value), Status::success());
    }

    [[nodiscard]] static constexpr Result failure(Status status) noexcept
    {
        return Result(T{}, status);
    }

    [[nodiscard]] constexpr bool ok() const noexcept
    {
        return status_.ok();
    }

    [[nodiscard]] constexpr explicit operator bool() const noexcept
    {
        return ok();
    }

    [[nodiscard]] constexpr const T& value() const noexcept
    {
        return value_;
    }

    [[nodiscard]] constexpr T& value() noexcept
    {
        return value_;
    }

    [[nodiscard]] constexpr Status status() const noexcept
    {
        return status_;
    }

private:
    constexpr Result(T value, Status status) noexcept
        : value_(std::move(value))
        , status_(status)
    {
    }

    T value_{};
    Status status_{};
};

}
