use std::pin::Pin;

use crate::absl::{Span, Status, StringView};
use crate::common::{Ast, Kind, OpaqueType, Value};
use crate::protobuf::{Arena, DescriptorPool, MessageFactory};

#[cxx::bridge]
mod ffi {
    #[namespace = "absl"]
    unsafe extern "C++" {
        include!(<absl/status/status.h>);
        type Status = super::Status;
        include!(<absl/strings/string_view.h>);
        type string_view<'a> = super::StringView<'a>;
    }

    #[namespace = "google::protobuf"]
    unsafe extern "C++" {
        include!(<google/protobuf/descriptor.h>);
        type Arena = super::Arena;
        type MessageFactory = super::MessageFactory;
        type DescriptorPool = super::DescriptorPool;
    }

    #[namespace = "cel"]
    unsafe extern "C++" {
        include!(<runtime/runtime.h>);
        include!(<runtime/function.h>);
        include!(<runtime/standard_functions.h>);
        include!(<common/ast.h>);

        type Kind = super::Kind;
        type Value<'a> = super::Value<'a>;
        type OpaqueType<'a> = super::OpaqueType<'a>;
        type Ast = super::Ast;

        type UnknownProcessingOptions = super::UnknownProcessingOptions;
        #[rust_name = "Activation"]
        type ActivationInterface<'f>;
        type Runtime<'a, 'f>;
        type RuntimeBuilder<'a, 'f>;
        fn type_registry<'this, 'a, 'f>(
            self: Pin<&'this mut RuntimeBuilder<'a, 'f>>,
        ) -> Pin<&'this mut TypeRegistry<'this, 'a>>;
        fn function_registry<'this, 'a, 'f>(
            self: Pin<&'this mut RuntimeBuilder<'a, 'f>>,
        ) -> Pin<&'this mut FunctionRegistry<'f>>;

        type RuntimeOptions;

        type Function<'a>;
        type FunctionDescriptor;
        fn name(self: &FunctionDescriptor) -> &CxxString;
        fn receiver_style(self: &FunctionDescriptor) -> bool;
        fn types(self: &FunctionDescriptor) -> &CxxVector<Kind>;
        fn is_strict(self: &FunctionDescriptor) -> bool;
        #[rust_name = "shape_matches"]
        fn ShapeMatches(self: &FunctionDescriptor, other: &FunctionDescriptor) -> bool;

        type FunctionOverloadReference<'a, 'f> = super::FunctionOverloadReference<'a, 'f>;

        type FunctionRegistry<'f>;
        #[rust_name = "register"]
        fn Register<'f>(
            self: Pin<&mut FunctionRegistry<'f>>,
            descriptor: &FunctionDescriptor,
            function: UniquePtr<Function<'f>>,
        ) -> Status;
        #[rust_name = "register_lazy"]
        fn RegisterLazyFunction<'f>(
            self: Pin<&mut FunctionRegistry<'f>>,
            descriptor: &FunctionDescriptor,
        ) -> Status;

        fn RegisterStandardFunctions(
            function_registry: Pin<&mut FunctionRegistry>,
            options: &RuntimeOptions,
        ) -> Status;

        type Program<'a, 'f>;
        type TypeRegistry<'d, 'a>;
        #[rust_name = "register_type"]
        fn RegisterType<'d, 'a>(
            self: Pin<&mut TypeRegistry<'d, 'a>>,
            opaque_type: &OpaqueType<'a>,
        ) -> Status;
    }

    // We use our patched version of EnableOptionalTypes
    //
    //#[namespace = "cel::extensions"]
    //unsafe extern "C++" {
    //    include!(<runtime/optional_types.h>);
    //    fn EnableOptionalTypes(runtime_builder: Pin<&mut RuntimeBuilder>) -> Status;
    //}

    #[namespace = "cel::runtime_internal"]
    unsafe extern "C++" {
        include!(<runtime/function_provider.h>);
        type FunctionProvider;
    }

    #[namespace = "rust::cel_cxx"]
    unsafe extern "C++" {
        include!(<cel-cxx-ffi/include/runtime.h>);
        type LazyOverload<'a> = super::LazyOverload<'a>;

        type Span_Value<'a, 'v> = super::Span<'a, super::Value<'v>>;
        type Span_CxxString<'a> = super::Span<'a, cxx::CxxString>;
        type Span_Kind<'a> = super::Span<'a, super::Kind>;

        type FunctionRegistryIterator<'a> = super::FunctionRegistryIterator<'a>;
        fn FunctionRegistryIterator_next<'a>(
            iter: &mut FunctionRegistryIterator<'a>,
            key: &mut UniquePtr<CxxString>,
            value: &mut UniquePtr<CxxVector<FunctionDescriptor>>,
        ) -> bool;
        fn FunctionRegistryIterator_drop<'a>(iter: &mut FunctionRegistryIterator<'a>);

        // Activation
        fn Activation_new<'f>(ffi: Box<AnyFfiActivation<'f>>) -> UniquePtr<Activation<'f>>;

        // FunctionRegistry
        fn FunctionRegistry_find_static_overloads<'this, 'f>(
            function_registry: &'this FunctionRegistry<'f>,
            name: string_view<'_>,
            receiver_style: bool,
            types: Span_Kind<'_>,
        ) -> Vec<FunctionOverloadReference<'this, 'f>>;
        fn FunctionRegistry_find_static_overloads_by_arity<'this, 'f>(
            function_registry: &'this FunctionRegistry<'f>,
            name: string_view<'_>,
            receiver_style: bool,
            arity: usize,
        ) -> Vec<FunctionOverloadReference<'this, 'f>>;
        fn FunctionRegistry_find_lazy_overloads<'this, 'f>(
            function_registry: &'this FunctionRegistry<'f>,
            name: string_view<'_>,
            receiver_style: bool,
            types: Span_Kind<'_>,
        ) -> Vec<LazyOverload<'this>>;
        fn FunctionRegistry_find_lazy_overloads_by_arity<'this, 'f>(
            function_registry: &'this FunctionRegistry<'f>,
            name: string_view<'_>,
            receiver_style: bool,
            arity: usize,
        ) -> Vec<LazyOverload<'this>>;
        fn FunctionRegistry_list_functions<'this, 'f>(
            function_registry: &'this FunctionRegistry<'f>,
        ) -> FunctionRegistryIterator<'this>;

        // Runtime
        fn Runtime_create_program<'a, 'f>(
            runtime: &Runtime<'a, 'f>,
            ast: UniquePtr<Ast>,
            result: &mut UniquePtr<Program<'a, 'f>>,
        ) -> Status;

        // RuntimeBuilder
        fn RuntimeBuilder_new<'this, 'a, 'f>(
            descriptor_pool: SharedPtr<DescriptorPool>,
            options: &RuntimeOptions,
            result: &'this mut UniquePtr<RuntimeBuilder<'a, 'f>>,
        ) -> Status;
        fn RuntimeBuilder_options<'this, 'a, 'f>(
            runtime_builder: &'this RuntimeBuilder<'a, 'f>,
        ) -> &'this RuntimeOptions;
        fn RuntimeBuilder_build<'a, 'f>(
            runtime_builder: Pin<&mut RuntimeBuilder<'a, 'f>>,
            result: &mut UniquePtr<Runtime<'a, 'f>>,
        ) -> Status;

        // RuntimeOptions
        fn RuntimeOptions_new() -> UniquePtr<RuntimeOptions>;

        // RuntimeOptions getters and setters
        fn RuntimeOptions_container(runtime_options: &RuntimeOptions) -> &str;
        fn RuntimeOptions_container_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> Pin<&mut CxxString>;
        fn RuntimeOptions_unknown_processing(
            runtime_options: &RuntimeOptions,
        ) -> UnknownProcessingOptions;
        fn RuntimeOptions_unknown_processing_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut UnknownProcessingOptions;
        fn RuntimeOptions_enable_missing_attribute_errors(runtime_options: &RuntimeOptions)
            -> bool;
        fn RuntimeOptions_enable_missing_attribute_errors_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_timestamp_duration_overflow_errors(
            runtime_options: &RuntimeOptions,
        ) -> bool;
        fn RuntimeOptions_enable_timestamp_duration_overflow_errors_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_short_circuiting(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_short_circuiting_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_comprehension(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_comprehension_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_comprehension_max_iterations(runtime_options: &RuntimeOptions) -> i32;
        fn RuntimeOptions_comprehension_max_iterations_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut i32;
        fn RuntimeOptions_enable_comprehension_list_append(
            runtime_options: &RuntimeOptions,
        ) -> bool;
        fn RuntimeOptions_enable_comprehension_list_append_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_regex(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_regex_mut(runtime_options: Pin<&mut RuntimeOptions>) -> &mut bool;
        fn RuntimeOptions_regex_max_program_size(runtime_options: &RuntimeOptions) -> i32;
        fn RuntimeOptions_regex_max_program_size_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut i32;
        fn RuntimeOptions_enable_string_conversion(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_string_conversion_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_string_concat(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_string_concat_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_list_concat(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_list_concat_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_list_contains(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_list_contains_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_fail_on_warnings(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_fail_on_warnings_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_qualified_type_identifiers(
            runtime_options: &RuntimeOptions,
        ) -> bool;
        fn RuntimeOptions_enable_qualified_type_identifiers_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_heterogeneous_equality(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_heterogeneous_equality_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_empty_wrapper_null_unboxing(
            runtime_options: &RuntimeOptions,
        ) -> bool;
        fn RuntimeOptions_enable_empty_wrapper_null_unboxing_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_lazy_bind_initialization(runtime_options: &RuntimeOptions)
            -> bool;
        fn RuntimeOptions_enable_lazy_bind_initialization_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_max_recursion_depth(runtime_options: &RuntimeOptions) -> i32;
        fn RuntimeOptions_max_recursion_depth_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut i32;
        fn RuntimeOptions_enable_recursive_tracing(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_recursive_tracing_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;
        fn RuntimeOptions_enable_fast_builtins(runtime_options: &RuntimeOptions) -> bool;
        fn RuntimeOptions_enable_fast_builtins_mut(
            runtime_options: Pin<&mut RuntimeOptions>,
        ) -> &mut bool;

        // FunctionDescriptor
        fn FunctionDescriptor_new(
            name: &str,
            receiver_style: bool,
            types: &[Kind],
            is_strict: bool,
        ) -> UniquePtr<FunctionDescriptor>;

        fn FunctionDescriptor_new_shared(
            name: &str,
            receiver_style: bool,
            types: &[Kind],
            is_strict: bool,
        ) -> SharedPtr<FunctionDescriptor>;

        // Program
        fn Program_evaluate<'a, 'f, 'a2, 'f2>(
            program: &Program<'a, 'f>,
            arena: &'a2 Arena,
            message_factory: &MessageFactory,
            activation: &Activation<'f2>,
            result: &mut UniquePtr<Value<'a2>>,
        ) -> Status;
        fn Program_evaluate2<'a, 'f, 'a2, 'f2>(
            program: &Program<'a, 'f>,
            arena: &'a2 Arena,
            activation: &Activation<'f2>,
            result: &mut UniquePtr<Value<'a2>>,
        ) -> Status;

        // Function
        fn Function_new<'a>(ffi_function: Box<AnyFfiFunction<'a>>) -> UniquePtr<Function<'a>>;

        include!(<cel-cxx-ffi/include/optional.h>);
        fn EnableOptionalTypes(runtime_builder: Pin<&mut RuntimeBuilder>) -> Status;
    }
    #[namespace = "rust::cel_cxx"]
    extern "Rust" {
        type AnyFfiActivation<'f>;
        #[cxx_name = "FindVariable"]
        unsafe fn find_variable<'f, 'a>(
            self: &AnyFfiActivation<'f>,
            name: &str,
            descriptor_pool: &'a DescriptorPool,
            message_factory: &'a MessageFactory,
            arena: &'a Arena,
            result: &mut UniquePtr<Value<'a>>,
        ) -> Status;
        #[cxx_name = "FindFunctionOverloads"]
        unsafe fn find_function_overloads<'this, 'f>(
            self: &'this AnyFfiActivation<'f>,
            name: &str,
        ) -> Vec<FunctionOverloadReference<'this, 'f>>;

        type AnyFfiFunction<'f>;
        #[cxx_name = "Invoke"]
        unsafe fn invoke<'f, 'a>(
            self: &AnyFfiFunction<'f>,
            args: Span_Value<'_, 'a>,
            descriptor_pool: &'a DescriptorPool,
            message_factory: &'a MessageFactory,
            arena: &'a Arena,
            overload_id: Span_CxxString<'_>,
            result: Pin<&mut Value<'a>>,
        ) -> Status;
    }

    impl<'a, 'f> Vec<FunctionOverloadReference<'a, 'f>> {}
    impl<'a> Vec<LazyOverload<'a>> {}
}

// Activation
pub use ffi::Activation;
unsafe impl<'f> Send for Activation<'f> {}
unsafe impl<'f> Sync for Activation<'f> {}

impl<'f> Activation<'f> {
    pub fn new<T: FfiActivation<'f> + 'f>(ffi: T) -> cxx::UniquePtr<Self> {
        ffi::Activation_new(Box::new(AnyFfiActivation::new(ffi)))
    }
}

// FunctionOverloadReference
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FunctionOverloadReference<'a, 'f> {
    descriptor: &'a FunctionDescriptor,
    implementation: &'a Function<'f>,
}

unsafe impl<'a, 'f> cxx::ExternType for FunctionOverloadReference<'a, 'f> {
    type Id = cxx::type_id!("cel::FunctionOverloadReference");
    type Kind = cxx::kind::Trivial;
}

impl<'a, 'f> FunctionOverloadReference<'a, 'f> {
    pub fn new(descriptor: &'a FunctionDescriptor, implementation: &'a Function<'f>) -> Self {
        Self {
            descriptor,
            implementation,
        }
    }

    pub fn descriptor(&self) -> &'a FunctionDescriptor {
        self.descriptor
    }

    pub fn implementation(&self) -> &'a Function<'f> {
        self.implementation
    }
}

// FunctionProvider
pub use ffi::FunctionProvider;
unsafe impl Send for FunctionProvider {}
unsafe impl Sync for FunctionProvider {}

// LazyOverload
#[repr(C)]
#[derive(Clone, Copy)]
pub struct LazyOverload<'a> {
    descriptor: &'a FunctionDescriptor,
    function_provider: &'a FunctionProvider,
}

unsafe impl<'a> cxx::ExternType for LazyOverload<'a> {
    type Id = cxx::type_id!("rust::cel_cxx::LazyOverload");
    type Kind = cxx::kind::Trivial;
}

impl<'a> LazyOverload<'a> {
    pub fn new(
        descriptor: &'a FunctionDescriptor,
        function_provider: &'a FunctionProvider,
    ) -> Self {
        Self {
            descriptor,
            function_provider,
        }
    }

    pub fn descriptor(&self) -> &'a FunctionDescriptor {
        self.descriptor
    }

    pub fn function_provider(&self) -> &'a FunctionProvider {
        self.function_provider
    }
}

// FfiActivation
pub trait FfiActivation<'f> {
    fn find_variable<'a>(
        &self,
        name: &str,
        descriptor_pool: &'a DescriptorPool,
        message_factory: &'a MessageFactory,
        arena: &'a Arena,
    ) -> Result<Option<cxx::UniquePtr<Value<'a>>>, Status>;

    fn find_function_overloads<'this>(
        &'this self,
        name: &str,
    ) -> Vec<FunctionOverloadReference<'this, 'f>>;
}

// AnyFfiActivation
struct AnyFfiActivation<'f>(Box<dyn FfiActivation<'f> + 'f>);

impl<'f> AnyFfiActivation<'f> {
    fn new<T: FfiActivation<'f> + 'f>(activation: T) -> Self {
        Self(Box::new(activation))
    }

    fn find_variable<'a>(
        &self,
        name: &str,
        descriptor_pool: &'a DescriptorPool,
        message_factory: &'a MessageFactory,
        arena: &'a Arena,
        result: &mut cxx::UniquePtr<Value<'a>>,
    ) -> Status {
        *result = cxx::UniquePtr::null();

        match self
            .0
            .find_variable(name, descriptor_pool, message_factory, arena)
        {
            Ok(Some(value)) => {
                *result = value;
                Status::ok()
            }
            Ok(None) => Status::ok(),
            Err(status) => status,
        }
    }

    fn find_function_overloads<'this>(
        &'this self,
        name: &str,
    ) -> Vec<FunctionOverloadReference<'this, 'f>> {
        self.0.find_function_overloads(name)
    }
}

// Runtime
pub use ffi::Runtime;
unsafe impl<'a, 'f> Send for Runtime<'a, 'f> {}
unsafe impl<'a, 'f> Sync for Runtime<'a, 'f> {}

impl<'a, 'f> Runtime<'a, 'f> {
    pub fn create_program(
        &self,
        ast: cxx::UniquePtr<Ast>,
    ) -> Result<cxx::UniquePtr<Program<'a, 'f>>, Status> {
        let mut result = cxx::UniquePtr::null();
        let status = ffi::Runtime_create_program(self, ast, &mut result);
        if status.is_ok() {
            Ok(result)
        } else {
            Err(status)
        }
    }
}

impl<'a, 'f> std::fmt::Debug for Runtime<'a, 'f> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let ptr = self as *const Runtime<'a, 'f>;
        write!(f, "Runtime {{ ptr: {ptr:p} }}")
    }
}

// RuntimeBuilder
pub use ffi::RuntimeBuilder;
unsafe impl<'a, 'f> Send for RuntimeBuilder<'a, 'f> {}
unsafe impl<'a, 'f> Sync for RuntimeBuilder<'a, 'f> {}

impl<'a, 'f> RuntimeBuilder<'a, 'f> {
    pub fn new(
        descriptor_pool: cxx::SharedPtr<DescriptorPool>,
        options: &RuntimeOptions,
    ) -> Result<cxx::UniquePtr<Self>, Status> {
        let mut result = cxx::UniquePtr::null();
        let status = ffi::RuntimeBuilder_new(descriptor_pool, options, &mut result);
        if status.is_ok() {
            Ok(result)
        } else {
            Err(status)
        }
    }

    pub fn enable_standard(self: Pin<&mut Self>, options: &RuntimeOptions) -> Result<(), Status> {
        let status = ffi::RegisterStandardFunctions(self.function_registry(), options);
        if status.is_ok() {
            Ok(())
        } else {
            Err(status)
        }
    }

    #[allow(unused_variables)]
    pub fn enable_optional(self: Pin<&mut Self>, options: &RuntimeOptions) -> Result<(), Status> {
        let status = ffi::EnableOptionalTypes(self);
        if status.is_ok() {
            Ok(())
        } else {
            Err(status)
        }
    }

    pub fn options(&self) -> &RuntimeOptions {
        ffi::RuntimeBuilder_options(self)
    }

    pub fn build(self: Pin<&mut Self>) -> Result<cxx::UniquePtr<Runtime<'a, 'f>>, Status> {
        let mut result = cxx::UniquePtr::null();
        let status = ffi::RuntimeBuilder_build(self, &mut result);
        if status.is_ok() {
            Ok(result)
        } else {
            Err(status)
        }
    }
}

impl<'a, 'f> std::fmt::Debug for RuntimeBuilder<'a, 'f> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let ptr = self as *const RuntimeBuilder<'a, 'f>;
        write!(f, "RuntimeBuilder {{ ptr: {ptr:p} }}")
    }
}

// RuntimeOptions
#[repr(i32)]
#[derive(Clone, Copy, PartialEq, Eq, Default)]
pub enum UnknownProcessingOptions {
    #[default]
    /// No unknown processing.
    Disabled,
    /// Only attributes supported.
    AttributeOnly,
    /// Attributes and functions supported. Function results are dependent on the
    /// logic for handling unknown_attributes, so clients must opt in to both.
    AttributeAndFunction,
}

unsafe impl cxx::ExternType for UnknownProcessingOptions {
    type Id = cxx::type_id!("cel::UnknownProcessingOptions");
    type Kind = cxx::kind::Trivial;
}

pub use ffi::RuntimeOptions;
unsafe impl Send for RuntimeOptions {}
unsafe impl Sync for RuntimeOptions {}

impl RuntimeOptions {
    pub fn new() -> cxx::UniquePtr<Self> {
        ffi::RuntimeOptions_new()
    }

    pub fn container(&self) -> &str {
        ffi::RuntimeOptions_container(self)
    }

    pub fn container_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut cxx::CxxString> {
        ffi::RuntimeOptions_container_mut(self)
    }

    pub fn unknown_processing(&self) -> UnknownProcessingOptions {
        ffi::RuntimeOptions_unknown_processing(self)
    }

    pub fn unknown_processing_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut UnknownProcessingOptions {
        ffi::RuntimeOptions_unknown_processing_mut(self)
    }

    pub fn enable_missing_attribute_errors(&self) -> bool {
        ffi::RuntimeOptions_enable_missing_attribute_errors(self)
    }

    pub fn enable_missing_attribute_errors_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_missing_attribute_errors_mut(self)
    }

    pub fn enable_timestamp_duration_overflow_errors(&self) -> bool {
        ffi::RuntimeOptions_enable_timestamp_duration_overflow_errors(self)
    }

    pub fn enable_timestamp_duration_overflow_errors_mut<'a>(
        self: Pin<&'a mut Self>,
    ) -> &'a mut bool {
        ffi::RuntimeOptions_enable_timestamp_duration_overflow_errors_mut(self)
    }

    pub fn short_circuiting(&self) -> bool {
        ffi::RuntimeOptions_short_circuiting(self)
    }

    pub fn short_circuiting_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_short_circuiting_mut(self)
    }

    pub fn enable_comprehension(&self) -> bool {
        ffi::RuntimeOptions_enable_comprehension(self)
    }

    pub fn enable_comprehension_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_comprehension_mut(self)
    }

    pub fn comprehension_max_iterations(&self) -> i32 {
        ffi::RuntimeOptions_comprehension_max_iterations(self)
    }

    pub fn comprehension_max_iterations_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut i32 {
        ffi::RuntimeOptions_comprehension_max_iterations_mut(self)
    }

    pub fn enable_comprehension_list_append(&self) -> bool {
        ffi::RuntimeOptions_enable_comprehension_list_append(self)
    }

    pub fn enable_comprehension_list_append_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_comprehension_list_append_mut(self)
    }

    pub fn enable_regex(&self) -> bool {
        ffi::RuntimeOptions_enable_regex(self)
    }

    pub fn enable_regex_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_regex_mut(self)
    }

    pub fn regex_max_program_size(&self) -> i32 {
        ffi::RuntimeOptions_regex_max_program_size(self)
    }

    pub fn regex_max_program_size_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut i32 {
        ffi::RuntimeOptions_regex_max_program_size_mut(self)
    }

    pub fn enable_string_conversion(&self) -> bool {
        ffi::RuntimeOptions_enable_string_conversion(self)
    }

    pub fn enable_string_conversion_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_string_conversion_mut(self)
    }

    pub fn enable_string_concat(&self) -> bool {
        ffi::RuntimeOptions_enable_string_concat(self)
    }

    pub fn enable_string_concat_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_string_concat_mut(self)
    }

    pub fn enable_list_concat(&self) -> bool {
        ffi::RuntimeOptions_enable_list_concat(self)
    }

    pub fn enable_list_concat_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_list_concat_mut(self)
    }

    pub fn enable_list_contains(&self) -> bool {
        ffi::RuntimeOptions_enable_list_contains(self)
    }

    pub fn enable_list_contains_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_list_contains_mut(self)
    }

    pub fn fail_on_warnings(&self) -> bool {
        ffi::RuntimeOptions_fail_on_warnings(self)
    }

    pub fn fail_on_warnings_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_fail_on_warnings_mut(self)
    }

    pub fn enable_qualified_type_identifiers(&self) -> bool {
        ffi::RuntimeOptions_enable_qualified_type_identifiers(self)
    }

    pub fn enable_qualified_type_identifiers_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_qualified_type_identifiers_mut(self)
    }

    pub fn enable_heterogeneous_equality(&self) -> bool {
        ffi::RuntimeOptions_enable_heterogeneous_equality(self)
    }

    pub fn enable_heterogeneous_equality_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_heterogeneous_equality_mut(self)
    }

    pub fn enable_empty_wrapper_null_unboxing(&self) -> bool {
        ffi::RuntimeOptions_enable_empty_wrapper_null_unboxing(self)
    }

    pub fn enable_empty_wrapper_null_unboxing_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_empty_wrapper_null_unboxing_mut(self)
    }

    pub fn enable_lazy_bind_initialization(&self) -> bool {
        ffi::RuntimeOptions_enable_lazy_bind_initialization(self)
    }

    pub fn enable_lazy_bind_initialization_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_lazy_bind_initialization_mut(self)
    }

    pub fn max_recursion_depth(&self) -> i32 {
        ffi::RuntimeOptions_max_recursion_depth(self)
    }

    pub fn max_recursion_depth_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut i32 {
        ffi::RuntimeOptions_max_recursion_depth_mut(self)
    }

    pub fn enable_recursive_tracing(&self) -> bool {
        ffi::RuntimeOptions_enable_recursive_tracing(self)
    }

    pub fn enable_recursive_tracing_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_recursive_tracing_mut(self)
    }

    pub fn enable_fast_builtins(&self) -> bool {
        ffi::RuntimeOptions_enable_fast_builtins(self)
    }

    pub fn enable_fast_builtins_mut<'a>(self: Pin<&'a mut Self>) -> &'a mut bool {
        ffi::RuntimeOptions_enable_fast_builtins_mut(self)
    }
}

// FunctionDescriptor
pub use ffi::FunctionDescriptor;
unsafe impl Send for FunctionDescriptor {}
unsafe impl Sync for FunctionDescriptor {}

impl FunctionDescriptor {
    pub fn new(
        name: &str,
        receiver_style: bool,
        types: &[Kind],
        is_strict: bool,
    ) -> cxx::UniquePtr<Self> {
        ffi::FunctionDescriptor_new(name, receiver_style, types, is_strict)
    }

    pub fn new_shared(
        name: &str,
        receiver_style: bool,
        types: &[Kind],
        is_strict: bool,
    ) -> cxx::SharedPtr<Self> {
        ffi::FunctionDescriptor_new_shared(name, receiver_style, types, is_strict)
    }
}

// FunctionRegistry
pub use ffi::FunctionRegistry;
unsafe impl<'f> Send for FunctionRegistry<'f> {}
unsafe impl<'f> Sync for FunctionRegistry<'f> {}

impl<'f> FunctionRegistry<'f> {
    pub fn find_static_overloads<'this>(
        &'this self,
        name: StringView<'_>,
        receiver_style: bool,
        types: Span<'_, Kind>,
    ) -> Vec<FunctionOverloadReference<'this, 'f>> {
        ffi::FunctionRegistry_find_static_overloads(self, name, receiver_style, types)
    }

    pub fn find_static_overloads_by_arity<'this>(
        &'this self,
        name: StringView<'_>,
        receiver_style: bool,
        arity: usize,
    ) -> Vec<FunctionOverloadReference<'this, 'f>> {
        ffi::FunctionRegistry_find_static_overloads_by_arity(self, name, receiver_style, arity)
    }

    pub fn find_lazy_overloads<'this>(
        &'this self,
        name: StringView<'_>,
        receiver_style: bool,
        types: Span<'_, Kind>,
    ) -> Vec<LazyOverload<'this>> {
        ffi::FunctionRegistry_find_lazy_overloads(self, name, receiver_style, types)
    }

    pub fn find_lazy_overloads_by_arity<'this>(
        &'this self,
        name: StringView<'_>,
        receiver_style: bool,
        arity: usize,
    ) -> Vec<LazyOverload<'this>> {
        ffi::FunctionRegistry_find_lazy_overloads_by_arity(self, name, receiver_style, arity)
    }

    pub fn list_functions<'this>(&'this self) -> FunctionRegistryIterator<'this> {
        ffi::FunctionRegistry_list_functions(self)
    }
}

// FunctionRegistryIterator
#[repr(C)]
pub struct FunctionRegistryIterator<'a>(crate::Rep<'a, usize, 1>);

unsafe impl<'a> cxx::ExternType for FunctionRegistryIterator<'a> {
    type Id = cxx::type_id!("rust::cel_cxx::FunctionRegistryIterator");
    type Kind = cxx::kind::Trivial;
}

impl<'a> Drop for FunctionRegistryIterator<'a> {
    fn drop(&mut self) {
        ffi::FunctionRegistryIterator_drop(self);
    }
}

//unsafe impl<'a> Send for FunctionRegistryIterator<'a> {}
//unsafe impl<'a> Sync for FunctionRegistryIterator<'a> {}

impl<'a> std::iter::Iterator for FunctionRegistryIterator<'a> {
    type Item = (
        cxx::UniquePtr<cxx::CxxString>,
        cxx::UniquePtr<cxx::CxxVector<FunctionDescriptor>>,
    );

    fn next(&mut self) -> Option<Self::Item> {
        let mut key = cxx::UniquePtr::null();
        let mut value = cxx::UniquePtr::null();
        if ffi::FunctionRegistryIterator_next(self, &mut key, &mut value) {
            Some((key, value))
        } else {
            None
        }
    }
}

// Program
pub use ffi::Program;
unsafe impl<'a, 'f> Send for Program<'a, 'f> {}
unsafe impl<'a, 'f> Sync for Program<'a, 'f> {}

impl<'a, 'f> Program<'a, 'f> {
    pub fn evaluate<'a2, 'f2>(
        &self,
        arena: &'a2 Arena,
        message_factory: Option<&MessageFactory>,
        activation: &Activation<'f2>,
    ) -> Result<cxx::UniquePtr<Value<'a2>>, Status> {
        let mut result = cxx::UniquePtr::null();
        let status = match message_factory {
            Some(message_factory) => {
                ffi::Program_evaluate(self, arena, message_factory, activation, &mut result)
            }
            None => ffi::Program_evaluate2(self, arena, activation, &mut result),
        };
        if status.is_ok() {
            Ok(result)
        } else {
            Err(status)
        }
    }
}

impl<'a, 'f> std::fmt::Debug for Program<'a, 'f> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let ptr = self as *const Program<'a, 'f>;
        write!(f, "Program {{ ptr: {ptr:p} }}")
    }
}

// Function
pub use ffi::Function;
unsafe impl<'f> Send for Function<'f> {}
unsafe impl<'f> Sync for Function<'f> {}

impl<'f> Function<'f> {
    pub fn new<T: FfiFunction + 'f>(ffi_function: T) -> cxx::UniquePtr<Self> {
        ffi::Function_new(Box::new(AnyFfiFunction::new(ffi_function)))
    }
}

// FfiFunction
pub trait FfiFunction {
    fn invoke<'a>(
        &self,
        args: Span<'_, Value<'a>>,
        descriptor_pool: &'a DescriptorPool,
        message_factory: &'a MessageFactory,
        arena: &'a Arena,
        overload_id: Span<'_, cxx::CxxString>,
    ) -> Result<cxx::UniquePtr<Value<'a>>, Status>;
}

// AnyFfiFunction
struct AnyFfiFunction<'f>(Box<dyn FfiFunction + 'f>);

impl<'f> AnyFfiFunction<'f> {
    fn new<T: FfiFunction + 'f>(function: T) -> Self {
        Self(Box::new(function))
    }

    fn invoke<'a>(
        &self,
        args: Span<'_, Value<'a>>,
        descriptor_pool: &'a DescriptorPool,
        message_factory: &'a MessageFactory,
        arena: &'a Arena,
        overload_id: Span<'_, cxx::CxxString>,
        result: Pin<&mut Value<'a>>,
    ) -> Status {
        match self
            .0
            .invoke(args, descriptor_pool, message_factory, arena, overload_id)
        {
            Ok(mut value) => {
                result.swap(value.pin_mut());
                Status::ok()
            }
            Err(status) => status,
        }
    }
}

// TypeRegistry
pub use ffi::TypeRegistry;
unsafe impl<'d, 'a> Send for TypeRegistry<'d, 'a> {}
unsafe impl<'d, 'a> Sync for TypeRegistry<'d, 'a> {}
