use crate::execute::ClientState;
use crate::measure_cpu_time::SystemClock;
use std::sync::Arc;
use wasmtime::Engine;
use wasmtime::component::{Component, Linker};
use wasmtime_wasi_http::p3::bindings::ServicePre;

pub struct Bundle {
    pub service_pre: ServicePre<ClientState<SystemClock>>,
    pub js: Option<String>,
    pub env_vars: Vec<(String, String)>,
}

#[allow(async_fn_in_trait)]
pub trait BundleCache: Send + Sync + 'static {
    async fn get(&self, project_id: &str) -> Result<Arc<Bundle>, Error>;

    async fn invalidate(&self, project_id: &str);
}

#[derive(Debug)]
pub enum Error {
    NotFound,
    Storage(anyhow::Error),
    Compile(wasmtime::Error),
    Decode(anyhow::Error),
    SingleflightLeaderFailed,
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Error::NotFound => write!(f, "bundle not found"),
            Error::Storage(e) => write!(f, "storage error: {e:?}"),
            Error::Compile(e) => write!(f, "wasm compile error: {e:?}"),
            Error::Decode(e) => write!(f, "bundle decode error: {e:?}"),
            Error::SingleflightLeaderFailed => write!(f, "singleflight leader failed"),
        }
    }
}

impl std::error::Error for Error {}

pub fn build_service_pre(
    engine: &Engine,
    linker: &Linker<ClientState<SystemClock>>,
    cwasm: &[u8],
) -> Result<ServicePre<ClientState<SystemClock>>, wasmtime::Error> {
    let component = unsafe { Component::deserialize(engine, cwasm) }?;
    let instance_pre = linker.instantiate_pre(&component)?;
    ServicePre::new(instance_pre)
}
