//
//  Result.hpp
//  SwitchboardSDK
//
//  Copyright © 2026 Synervoz. All rights reserved.
//

#pragma once

#include "export.h"

#include <switchboard/SBAny.hpp>
#include <optional>
#include <string>
#include <variant>
#include <stdexcept>

namespace switchboard {

/**
 * @brief Represents an error that can occur during an operation.
 */
struct SWITCHBOARDSDK_EXPORT Error {
    /// @brief Error message.
    std::string message;

    /**
     * @brief Error constructor.
     *
     * @param msg Error message.
     */
    Error(std::string msg) : message(std::move(msg)) {}
};

/**
 * @brief Represents the result of an operation that can either succeed or fail.
 */
template <typename T>
class SWITCHBOARDSDK_EXPORT Result {
private:
    using ValueType = std::conditional_t<std::is_void_v<T>, std::monostate, T>;
    std::variant<ValueType, Error> result;

public:
    explicit Result(ValueType value) : result(std::move(value)) {}
    explicit Result(Error error) : result(std::move(error)) {}

    /**
     * @brief Returns true if the operation was successful.
     * 
     * @return True if the operation was successful.
     */
    [[nodiscard]] bool isSuccess() const {
        return std::holds_alternative<ValueType>(result);
    }

    /**
     * @brief Returns true if the operation failed.
     * 
     * @return True if the operation failed.
     */
    [[nodiscard]] bool isError() const {
        return std::holds_alternative<Error>(result);
    }

    /**
     * @brief Returns the value of the operation if it was successful.
     * 
     * @return The value of the operation if it was successful.
     */
    [[nodiscard]] ValueType value() const {
        if (!isSuccess()) {
            throw std::runtime_error("Result does not contain a value, operation failed.");
        }
        return std::get<ValueType>(result);
    }

    /**
     * @brief Returns the error of the operation if it failed.
     * 
     * @return The error of the operation if it failed.
     */
    [[nodiscard]] Error error() const {
        if (!isError()) {
            throw std::runtime_error("Result does not contain an error, operation was successful.");
        }
        return std::get<Error>(result);
    }

    /**
     * @brief Converts the result to a SBAny type.
     *
     * If the operation was successful, it returns a Result<SBAny> containing the value.
     * If the operation failed, it returns a Result<SBAny> containing the error message.
     *
     * @return A Result<SBAny> containing either the value or the error message.
     */
    [[nodiscard]] Result<SBAny> toAny() const {
        if (isSuccess()) {
            return Result<SBAny>(SBAny(value()));
        } else {
            return Result<SBAny>(Error(error().message));
        }
    }
};

/**
 * @brief Creates a successful result with a value.
 *
 * @param value The value of the successful result.
 *
 * @return A successful result with the given value.
 */
template <typename T>
Result<T> makeSuccess(T value) {
    return Result<T>(std::move(value));
}

/**
 * @brief Creates a successful result without a value.
 *
 * @return A successful result without a value.
 */
inline Result<void> makeSuccess() {
    return Result<void>(std::monostate {});
}

/**
 * @brief Creates an error result with an error code and message.
 *
 * @param errorMessage The error message.
 *
 * @return An error result with the given error code and message.
 */
template <typename T>
Result<T> makeError(std::string errorMessage) {
    return Result<T>(Error(std::move(errorMessage)));
}

}
