// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

use proxy_wasm_stub::stub::Host;
use proxy_wasm_stub::types::{BufferType, Bytes, LogLevel, MapType, MetricType, Status};
use std::cell::RefCell;
use std::rc::Rc;
use std::time::{Duration, SystemTime};

/// A facade to be able to hold several "mutable" references to the host to share between
/// the root context and the http context  
pub struct HostFacade<H> {
    host: Rc<RefCell<H>>,
}

impl<H> HostFacade<H> {
    pub fn new(host: Rc<RefCell<H>>) -> Self {
        Self { host }
    }
}

impl<H: Host> Host for HostFacade<H> {
    fn log(&self, level: LogLevel, message: &str) -> Result<(), Status> {
        self.host.borrow().log(level, message)
    }

    fn get_property(&self, path: Vec<&str>) -> Result<Option<Bytes>, Status> {
        self.host.borrow().get_property(path)
    }

    fn set_property(&mut self, path: Vec<&str>, value: Option<&[u8]>) -> Result<(), Status> {
        self.host.borrow_mut().set_property(path, value)
    }

    fn get_shared_data(&self, key: &str) -> Result<(Option<Bytes>, Option<u32>), Status> {
        self.host.borrow().get_shared_data(key)
    }

    fn set_shared_data(
        &mut self,
        key: &str,
        value: Option<&[u8]>,
        cas: Option<u32>,
    ) -> Result<(), Status> {
        self.host.borrow_mut().set_shared_data(key, value, cas)
    }

    fn get_buffer(
        &self,
        buffer_type: BufferType,
        start: usize,
        max_size: usize,
    ) -> Result<Option<Bytes>, Status> {
        self.host.borrow().get_buffer(buffer_type, start, max_size)
    }

    fn set_buffer(
        &mut self,
        buffer_type: BufferType,
        start: usize,
        size: usize,
        value: &[u8],
    ) -> Result<(), Status> {
        self.host
            .borrow_mut()
            .set_buffer(buffer_type, start, size, value)
    }

    fn set_effective_context(&mut self, context_id: u32) -> Result<(), Status> {
        self.host.borrow_mut().set_effective_context(context_id)
    }

    fn get_map(&self, map_type: MapType) -> Result<Vec<(String, String)>, Status> {
        self.host.borrow().get_map(map_type)
    }

    fn get_map_bytes(&self, map_type: MapType) -> Result<Vec<(String, Bytes)>, Status> {
        self.host.borrow().get_map_bytes(map_type)
    }

    fn set_map(&mut self, map_type: MapType, map: Vec<(&str, &str)>) -> Result<(), Status> {
        self.host.borrow_mut().set_map(map_type, map)
    }

    fn set_map_bytes(&mut self, map_type: MapType, map: Vec<(&str, &[u8])>) -> Result<(), Status> {
        self.host.borrow_mut().set_map_bytes(map_type, map)
    }

    fn get_map_value(&self, map_type: MapType, key: &str) -> Result<Option<String>, Status> {
        self.host.borrow().get_map_value(map_type, key)
    }

    fn get_map_value_bytes(&self, map_type: MapType, key: &str) -> Result<Option<Bytes>, Status> {
        self.host.borrow().get_map_value_bytes(map_type, key)
    }

    fn set_map_value(
        &mut self,
        map_type: MapType,
        key: &str,
        value: Option<&str>,
    ) -> Result<(), Status> {
        self.host.borrow_mut().set_map_value(map_type, key, value)
    }

    fn set_map_value_bytes(
        &mut self,
        map_type: MapType,
        key: &str,
        value: Option<&[u8]>,
    ) -> Result<(), Status> {
        self.host
            .borrow_mut()
            .set_map_value_bytes(map_type, key, value)
    }

    fn add_map_value(&mut self, map_type: MapType, key: &str, value: &str) -> Result<(), Status> {
        self.host.borrow_mut().add_map_value(map_type, key, value)
    }

    fn add_map_value_bytes(
        &mut self,
        map_type: MapType,
        key: &str,
        value: &[u8],
    ) -> Result<(), Status> {
        self.host
            .borrow_mut()
            .add_map_value_bytes(map_type, key, value)
    }

    fn send_http_response(
        &mut self,
        status_code: u32,
        headers: Vec<(&str, &str)>,
        body: Option<&[u8]>,
    ) -> Result<(), Status> {
        self.host
            .borrow_mut()
            .send_http_response(status_code, headers, body)
    }

    fn get_current_time(&self) -> Result<SystemTime, Status> {
        self.host.borrow().get_current_time()
    }

    fn set_tick_period(&mut self, period: Duration) -> Result<(), Status> {
        self.host.borrow_mut().set_tick_period(period)
    }

    fn resume_http_request(&mut self) -> Result<(), Status> {
        self.host.borrow_mut().resume_http_request()
    }

    fn resume_http_response(&mut self) -> Result<(), Status> {
        self.host.borrow_mut().resume_http_response()
    }

    fn define_metric(&mut self, metric_type: MetricType, name: &str) -> Result<u32, Status> {
        self.host.borrow_mut().define_metric(metric_type, name)
    }

    fn get_metric(&self, metric_id: u32) -> Result<u64, Status> {
        self.host.borrow().get_metric(metric_id)
    }

    fn record_metric(&mut self, metric_id: u32, value: u64) -> Result<(), Status> {
        self.host.borrow_mut().record_metric(metric_id, value)
    }

    fn increment_metric(&mut self, metric_id: u32, offset: i64) -> Result<(), Status> {
        self.host.borrow_mut().increment_metric(metric_id, offset)
    }

    fn dispatch_http_call(
        &mut self,
        upstream: &str,
        headers: Vec<(&str, &str)>,
        body: Option<&[u8]>,
        trailers: Vec<(&str, &str)>,
        timeout: Duration,
    ) -> Result<u32, Status> {
        self.host
            .borrow_mut()
            .dispatch_http_call(upstream, headers, body, trailers, timeout)
    }

    fn dispatch_grpc_call(
        &mut self,
        upstream_name: &str,
        service_name: &str,
        method_name: &str,
        initial_metadata: Vec<(&str, &[u8])>,
        message: Option<&[u8]>,
        timeout: Duration,
    ) -> Result<u32, Status> {
        self.host.borrow_mut().dispatch_grpc_call(
            upstream_name,
            service_name,
            method_name,
            initial_metadata,
            message,
            timeout,
        )
    }

    fn get_grpc_status(&self) -> Result<(u32, Option<String>), Status> {
        self.host.borrow().get_grpc_status()
    }

    fn cancel_grpc_call(&mut self, token_id: u32) -> Result<(), Status> {
        self.host.borrow_mut().cancel_grpc_call(token_id)
    }

    fn call_foreign_function(
        &mut self,
        function_name: &str,
        arguments: Option<&[u8]>,
    ) -> Result<Option<Bytes>, Status> {
        self.host
            .borrow_mut()
            .call_foreign_function(function_name, arguments)
    }
}
