#pragma once

/// @file fl/task/PromiseResult.h
/// @brief Result type for promise operations with ok() semantics
///
/// PromiseResult<T> provides a Rust-like Result type that wraps either a success
/// value of type T or an Error. It provides convenient ok() checking and safe
/// value access with assertions on misuse.

#include "fl/stl/variant.h"
#include "fl/task/promise.h"  // For Error type

namespace fl {
namespace task {

/// @brief Result type for promise operations
/// @tparam T The success value type
///
/// PromiseResult provides a clean API for handling success/error results from
/// async operations. It wraps a variant<T, Error> but provides more ergonomic
/// access patterns with ok() checking and automatic assertions.
///
/// @section Usage
/// @code
/// auto result = fl::task::await_top_level<int>(some_promise);
///
/// if (result.ok()) {
///     int value = result.value();  // Safe access
///     FL_WARN("Success: " << value);
/// } else {
///     FL_WARN("Error: " << result.error().message);
/// }
///
/// // Or use operator bool
/// if (result) {
///     FL_WARN("Got: " << result.value());
/// }
/// @endcode
template<typename T>
class PromiseResult {
public:
    /// @brief Construct a successful PromiseResult
    /// @param value The success value
    PromiseResult(const T& value) : mResult(value) {}

    /// @brief Construct a successful PromiseResult (move)
    /// @param value The success value (moved)
    PromiseResult(T&& value) : mResult(fl::move(value)) {}

    /// @brief Construct an error PromiseResult
    /// @param error The error value
    PromiseResult(const Error& error) : mResult(error) {}

    /// @brief Construct an error PromiseResult (move)
    /// @param error The error value (moved)
    PromiseResult(Error&& error) : mResult(fl::move(error)) {}

    /// @brief Check if the result is successful
    /// @return True if the result contains a value, false if it contains an error
    bool ok() const {
        return mResult.template is<T>();
    }

    /// @brief Boolean conversion operator (same as ok())
    /// @return True if the result is successful
    ///
    /// Allows usage like: if (result) { ... }
    explicit operator bool() const {
        return ok();
    }

    /// @brief Get the success value (const)
    /// @return Reference to the success value
    /// @warning Returns static empty object if called on an error result
    /// @note Use ok() to check before calling for proper error handling
    const T& value() const {
        if (!ok()) {
            // Return static empty object instead of crashing
            static const T empty{};
            return empty;
        }
        return mResult.template get<T>();
    }

    /// @brief Get the success value (mutable)
    /// @return Reference to the success value
    /// @warning Returns static empty object if called on an error result
    /// @note Use ok() to check before calling for proper error handling
    T& value() {
        if (!ok()) {
            // Return static empty object instead of crashing
            static T empty{};  // okay static in header
            return empty;
        }
        return mResult.template get<T>();
    }

    /// @brief Get the error value
    /// @return Reference to the error
    /// @warning Returns static descriptive error if called on a success result
    /// @note Use !ok() to check before calling for proper error handling
    const Error& error() const {
        if (ok()) {
            // Return descriptive error for misuse detection
            static const Error empty_error("No error - result contains success value");
            return empty_error;
        }
        return mResult.template get<Error>();
    }

    /// @brief Get the error message as a convenience
    /// @return Error message string, or empty string if successful
    /// @note Safe to call on success results (returns empty string)
    fl::string error_message() const {
        return ok() ? fl::string() : error().message;
    }

    /// @brief Access the underlying variant (for advanced usage)
    /// @return Reference to the internal variant
    const fl::variant<T, Error>& variant() const {
        return mResult;
    }

private:
    fl::variant<T, Error> mResult;
};

/// @brief Helper function to create a successful result
/// @tparam T The value type
/// @param value The success value
/// @return result containing the value
template<typename T>
PromiseResult<T> make_success(const T& value) {
    return PromiseResult<T>(value);
}

/// @brief Helper function to create a successful result (move)
/// @tparam T The value type
/// @param value The success value (moved)
/// @return result containing the value
template<typename T>
PromiseResult<T> make_success(T&& value) {
    return PromiseResult<T>(fl::move(value));
}

/// @brief Helper function to create an error result
/// @tparam T The value type
/// @param error The error
/// @return result containing the error
template<typename T>
PromiseResult<T> make_error(const Error& error) {
    return PromiseResult<T>(error);
}

/// @brief Helper function to create an error result (move)
/// @tparam T The value type
/// @param error The error (moved)
/// @return result containing the error
template<typename T>
PromiseResult<T> make_error(Error&& error) {
    return PromiseResult<T>(fl::move(error));
}

/// @brief Helper function to create an error result from string
/// @tparam T The value type
/// @param message The error message
/// @return result containing the error
template<typename T>
PromiseResult<T> make_error(const fl::string& message) {
    return PromiseResult<T>(Error(message));
}

/// @brief Helper function to create an error result from C-string
/// @tparam T The value type
/// @param message The error message
/// @return result containing the error
template<typename T>
PromiseResult<T> make_error(const char* message) {
    return PromiseResult<T>(Error(message));
}

} // namespace task
} // namespace fl
