wit_bindgen_host_wasmer_rust::export!({
    custom_error: true,
    paths: ["contract_system_api.wit"],
});
wit_bindgen_host_wasmer_rust::export!({
    custom_error: true,
    paths: ["service_system_api.wit"],
});
wit_bindgen_host_wasmer_rust::export!({
    custom_error: true,
    paths: ["view_system_api.wit"],
});
wit_bindgen_host_wasmer_rust::import!("contract.wit");
wit_bindgen_host_wasmer_rust::import!("service.wit");
#[path = "conversions_from_wit.rs"]
mod conversions_from_wit;
#[path = "conversions_to_wit.rs"]
mod conversions_to_wit;
use super::{module_cache::ModuleCache, WasmExecutionError};
use crate::{
    wasm::{WasmContractModule, WasmServiceModule},
    ApplicationCallOutcome, BaseRuntime, Bytecode, CalleeContext, ContractRuntime, ExecutionError,
    MessageContext, OperationContext, QueryContext, RawExecutionOutcome, ServiceRuntime,
    SessionCallOutcome,
};
use bytes::Bytes;
use linera_base::{identifiers::SessionId, sync::Lazy};
use std::{marker::Unpin, sync::Arc};
use tokio::sync::Mutex;
use wasmer::{
    imports, wasmparser::Operator, CompilerConfig, Engine, EngineBuilder, Instance, Module,
    Singlepass, Store,
};
use wasmer_middlewares::metering::{self, Metering, MeteringPoints};
use wit_bindgen_host_wasmer_rust::Le;
static SERVICE_ENGINE: Lazy<Engine> = Lazy::new(|| {
    let compiler_config = Singlepass::default();
    EngineBuilder::new(compiler_config).into()
});
static CONTRACT_CACHE: Lazy<Mutex<ModuleCache<CachedContractModule>>> = Lazy::new(Mutex::default);
static SERVICE_CACHE: Lazy<Mutex<ModuleCache<Module>>> = Lazy::new(Mutex::default);
pub(crate) struct WasmerContractInstance<Runtime> {
    application: contract::Contract,
    store: Store,
    runtime: Runtime,
    instance: Instance,
}
impl<Runtime> WasmerContractInstance<Runtime>
where
    Runtime: ContractRuntime + Send + Unpin,
{
    fn configure_initial_fuel(&mut self) -> Result<(), ExecutionError> {
        let remaining_points = self.runtime.remaining_fuel()?;
        metering::set_remaining_points(&mut self.store, &self.instance, remaining_points);
        Ok(())
    }
    fn persist_remaining_fuel(&mut self) -> Result<(), ExecutionError> {
        let remaining_fuel = match metering::get_remaining_points(&mut self.store, &self.instance) {
            MeteringPoints::Exhausted => 0,
            MeteringPoints::Remaining(fuel) => fuel,
        };
        self.runtime.set_remaining_fuel(remaining_fuel)
    }
}
pub struct WasmerServiceInstance {
    application: service::Service,
    store: Store,
}
impl WasmContractModule {
    pub async fn from_wasmer(contract_bytecode: Bytecode) -> Result<Self, WasmExecutionError> {
        let mut contract_cache = CONTRACT_CACHE.lock().await;
        let (engine, module) = contract_cache
            .get_or_insert_with(contract_bytecode, CachedContractModule::new)
            .map_err(WasmExecutionError::LoadContractModule)?
            .create_execution_instance()
            .map_err(WasmExecutionError::LoadContractModule)?;
        Ok(WasmContractModule::Wasmer { engine, module })
    }
}
impl<Runtime> WasmerContractInstance<Runtime>
where
    Runtime: ContractRuntime + Clone + Send + Sync + Unpin + 'static,
{
    pub fn prepare(
        contract_engine: &Engine,
        contract_module: &Module,
        runtime: Runtime,
    ) -> Result<Self, WasmExecutionError> {
        let mut store = Store::new(contract_engine);
        let mut imports = imports! {};
        let system_api_setup =
            contract_system_api::add_to_imports(&mut store, &mut imports, runtime.clone());
        let views_api_setup =
            view_system_api::add_to_imports(&mut store, &mut imports, runtime.clone());
        let (application, instance) =
            contract::Contract::instantiate(&mut store, contract_module, &mut imports)
                .map_err(WasmExecutionError::LoadContractModule)?;
        system_api_setup(&instance, &store).map_err(WasmExecutionError::LoadContractModule)?;
        views_api_setup(&instance, &store).map_err(WasmExecutionError::LoadContractModule)?;
        Ok(Self {
            application,
            store,
            runtime,
            instance,
        })
    }
}
impl WasmContractModule {
    fn operation_cost(operator: &Operator) -> u64 {
        match operator {
            Operator::Nop
            | Operator::Drop
            | Operator::Block { .. }
            | Operator::Loop { .. }
            | Operator::Unreachable
            | Operator::Else
            | Operator::End => 0,
            _ => 1,
        }
    }
}
impl WasmServiceModule {
    pub async fn from_wasmer(service_bytecode: Bytecode) -> Result<Self, WasmExecutionError> {
        let mut service_cache = SERVICE_CACHE.lock().await;
        let module = service_cache
            .get_or_insert_with(service_bytecode, |bytecode| {
                Module::new(&*SERVICE_ENGINE, bytecode)
                    .map_err(wit_bindgen_host_wasmer_rust::anyhow::Error::from)
            })
            .map_err(WasmExecutionError::LoadServiceModule)?;
        Ok(WasmServiceModule::Wasmer { module })
    }
}
impl WasmerServiceInstance {
    pub fn prepare<Runtime>(
        service_module: &Module,
        runtime: Runtime,
    ) -> Result<Self, WasmExecutionError>
    where
        Runtime: ServiceRuntime + Clone + Send + Sync + Unpin + 'static,
    {
        let mut store = Store::new(&*SERVICE_ENGINE);
        let mut imports = imports! {};
        let system_api_setup =
            service_system_api::add_to_imports(&mut store, &mut imports, runtime.clone());
        let views_api_setup = view_system_api::add_to_imports(&mut store, &mut imports, runtime);
        let (application, instance) =
            service::Service::instantiate(&mut store, service_module, &mut imports)
                .map_err(WasmExecutionError::LoadServiceModule)?;
        system_api_setup(&instance, &store).map_err(WasmExecutionError::LoadServiceModule)?;
        views_api_setup(&instance, &store).map_err(WasmExecutionError::LoadServiceModule)?;
        Ok(Self { application, store })
    }
}
impl<Runtime> crate::UserContract for WasmerContractInstance<Runtime>
where
    Runtime: ContractRuntime + Send + Unpin,
{
    fn initialize(
        &mut self,
        context: OperationContext,
        argument: Vec<u8>,
    ) -> Result<RawExecutionOutcome<Vec<u8>>, ExecutionError> {
        self.configure_initial_fuel()?;
        let result = contract::Contract::initialize(
            &self.application,
            &mut self.store,
            context.into(),
            &argument,
        )
        .map(|inner| inner.map(RawExecutionOutcome::from));
        self.persist_remaining_fuel()?;
        result?.map_err(ExecutionError::UserError)
    }
    fn execute_operation(
        &mut self,
        context: OperationContext,
        operation: Vec<u8>,
    ) -> Result<RawExecutionOutcome<Vec<u8>>, ExecutionError> {
        self.configure_initial_fuel()?;
        let result = contract::Contract::execute_operation(
            &self.application,
            &mut self.store,
            context.into(),
            &operation,
        )
        .map(|inner| inner.map(RawExecutionOutcome::from));
        self.persist_remaining_fuel()?;
        result?.map_err(ExecutionError::UserError)
    }
    fn execute_message(
        &mut self,
        context: MessageContext,
        message: Vec<u8>,
    ) -> Result<RawExecutionOutcome<Vec<u8>>, ExecutionError> {
        self.configure_initial_fuel()?;
        let result = contract::Contract::execute_message(
            &self.application,
            &mut self.store,
            context.into(),
            &message,
        )
        .map(|inner| inner.map(RawExecutionOutcome::from));
        self.persist_remaining_fuel()?;
        result?.map_err(ExecutionError::UserError)
    }
    fn handle_application_call(
        &mut self,
        context: CalleeContext,
        argument: Vec<u8>,
        forwarded_sessions: Vec<SessionId>,
    ) -> Result<ApplicationCallOutcome, ExecutionError> {
        let forwarded_sessions = forwarded_sessions
            .into_iter()
            .map(contract::SessionId::from)
            .collect::<Vec<_>>();
        self.configure_initial_fuel()?;
        let result = contract::Contract::handle_application_call(
            &self.application,
            &mut self.store,
            context.into(),
            &argument,
            &forwarded_sessions,
        )
        .map(|inner| inner.map(ApplicationCallOutcome::from));
        self.persist_remaining_fuel()?;
        result?.map_err(ExecutionError::UserError)
    }
    fn handle_session_call(
        &mut self,
        context: CalleeContext,
        session: Vec<u8>,
        argument: Vec<u8>,
        forwarded_sessions: Vec<SessionId>,
    ) -> Result<(SessionCallOutcome, Vec<u8>), ExecutionError> {
        let forwarded_sessions = forwarded_sessions
            .into_iter()
            .map(contract::SessionId::from)
            .collect::<Vec<_>>();
        self.configure_initial_fuel()?;
        let result = contract::Contract::handle_session_call(
            &self.application,
            &mut self.store,
            context.into(),
            &session,
            &argument,
            &forwarded_sessions,
        )
        .map(|inner| inner.map(<(SessionCallOutcome, Vec<u8>)>::from));
        self.persist_remaining_fuel()?;
        result?.map_err(ExecutionError::UserError)
    }
}
impl crate::UserService for WasmerServiceInstance {
    fn handle_query(
        &mut self,
        context: QueryContext,
        argument: Vec<u8>,
    ) -> Result<Vec<u8>, ExecutionError> {
        service::Service::handle_query(
            &self.application,
            &mut self.store,
            context.into(),
            &argument,
        )?
        .map_err(ExecutionError::UserError)
    }
}
impl_contract_system_api!(wasmer::RuntimeError);
impl_service_system_api!(wasmer::RuntimeError);
impl_view_system_api!(wasmer::RuntimeError);
impl From<ExecutionError> for wasmer::RuntimeError {
    fn from(error: ExecutionError) -> Self {
        wasmer::RuntimeError::user(Box::new(error))
    }
}
impl From<wasmer::RuntimeError> for ExecutionError {
    fn from(error: wasmer::RuntimeError) -> Self {
        error
            .downcast::<ExecutionError>()
            .unwrap_or_else(|unknown_error| {
                ExecutionError::WasmError(WasmExecutionError::ExecuteModuleInWasmer(unknown_error))
            })
    }
}
pub struct CachedContractModule {
    compiled_bytecode: Bytes,
}
impl CachedContractModule {
    pub fn new(contract_bytecode: Bytecode) -> Result<Self, anyhow::Error> {
        let module = Module::new(&Self::create_compilation_engine(), contract_bytecode)?;
        let compiled_bytecode = module.serialize()?;
        Ok(CachedContractModule { compiled_bytecode })
    }
    fn create_compilation_engine() -> Engine {
        let metering = Arc::new(Metering::new(0, WasmContractModule::operation_cost));
        let mut compiler_config = Singlepass::default();
        compiler_config.push_middleware(metering);
        compiler_config.canonicalize_nans(true);
        EngineBuilder::new(compiler_config).into()
    }
    pub fn create_execution_instance(&self) -> Result<(Engine, Module), anyhow::Error> {
        let engine = Engine::headless();
        let store = Store::new(&engine);
        let module = unsafe { Module::deserialize(&store, &*self.compiled_bytecode) }?;
        Ok((engine, module))
    }
}