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

use crate::types::{BufferType, Bytes, LogLevel, MapType, MetricType, Status};
use std::cell::RefCell;
use std::time::{Duration, SystemTime};

thread_local! {
    pub(crate) static STUB: RefCell<Box<RefCell<dyn Host + 'static>>> = RefCell::new(Box::new(RefCell::new(UnimplementedHost)));
}

pub fn set_host<H: Host + 'static>(host: H) {
    STUB.with(|stub| *stub.borrow_mut() = Box::new(RefCell::new(host)));
}

pub fn clear_host() {
    STUB.with(|stub| *stub.borrow_mut() = Box::new(RefCell::new(UnimplementedHost)));
}

pub trait Host {
    fn log(&self, level: LogLevel, message: &str) -> Result<(), Status>;

    fn get_property(&self, path: Vec<&str>) -> Result<Option<Bytes>, Status>;

    fn set_property(&mut self, path: Vec<&str>, value: Option<&[u8]>) -> Result<(), Status>;

    fn get_shared_data(&self, key: &str) -> Result<(Option<Bytes>, Option<u32>), Status>;

    fn set_shared_data(
        &mut self,
        key: &str,
        value: Option<&[u8]>,
        cas: Option<u32>,
    ) -> Result<(), Status>;

    fn get_buffer(
        &self,
        buffer_type: BufferType,
        start: usize,
        max_size: usize,
    ) -> Result<Option<Bytes>, Status>;

    fn set_buffer(
        &mut self,
        buffer_type: BufferType,
        start: usize,
        size: usize,
        value: &[u8],
    ) -> Result<(), Status>;

    fn set_effective_context(&mut self, context_id: u32) -> Result<(), Status>;

    fn get_map(&self, map_type: MapType) -> Result<Vec<(String, String)>, Status>;

    fn get_map_bytes(&self, map_type: MapType) -> Result<Vec<(String, Bytes)>, Status>;

    fn set_map(&mut self, map_type: MapType, map: Vec<(&str, &str)>) -> Result<(), Status>;

    fn set_map_bytes(&mut self, map_type: MapType, map: Vec<(&str, &[u8])>) -> Result<(), Status>;

    fn get_map_value(&self, map_type: MapType, key: &str) -> Result<Option<String>, Status>;

    fn get_map_value_bytes(&self, map_type: MapType, key: &str) -> Result<Option<Bytes>, Status>;

    fn set_map_value(
        &mut self,
        map_type: MapType,
        key: &str,
        value: Option<&str>,
    ) -> Result<(), Status>;

    fn set_map_value_bytes(
        &mut self,
        map_type: MapType,
        key: &str,
        value: Option<&[u8]>,
    ) -> Result<(), Status>;

    fn add_map_value(&mut self, map_type: MapType, key: &str, value: &str) -> Result<(), Status>;

    fn add_map_value_bytes(
        &mut self,
        map_type: MapType,
        key: &str,
        value: &[u8],
    ) -> Result<(), Status>;

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

    fn get_current_time(&self) -> Result<SystemTime, Status>;

    fn set_tick_period(&mut self, period: Duration) -> Result<(), Status>;

    fn resume_http_request(&mut self) -> Result<(), Status>;

    fn resume_http_response(&mut self) -> Result<(), Status>;

    fn define_metric(&mut self, _metric_type: MetricType, name: &str) -> Result<u32, Status>;

    fn get_metric(&self, metric_id: u32) -> Result<u64, Status>;

    fn record_metric(&mut self, metric_id: u32, value: u64) -> Result<(), Status>;

    fn increment_metric(&mut self, metric_id: u32, offset: i64) -> Result<(), Status>;

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

    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>;

    fn get_grpc_status(&self) -> Result<(u32, Option<String>), Status>;

    fn cancel_grpc_call(&mut self, token_id: u32) -> Result<(), Status>;

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

struct UnimplementedHost;

impl Host for UnimplementedHost {
    fn log(&self, _level: LogLevel, _message: &str) -> Result<(), Status> {
        Ok(())
    }

    fn get_property(&self, _path: Vec<&str>) -> Result<Option<Bytes>, Status> {
        Ok(None)
    }

    fn set_property(&mut self, _path: Vec<&str>, _value: Option<&[u8]>) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn get_shared_data(&self, _key: &str) -> Result<(Option<Bytes>, Option<u32>), Status> {
        panic!("Not implemented for current host!")
    }

    fn set_shared_data(
        &mut self,
        _key: &str,
        _value: Option<&[u8]>,
        _cas: Option<u32>,
    ) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn get_buffer(
        &self,
        _buffer_type: BufferType,
        _start: usize,
        _max_size: usize,
    ) -> Result<Option<Bytes>, Status> {
        panic!("Not implemented for current host!")
    }

    fn set_buffer(
        &mut self,
        _buffer_type: BufferType,
        _start: usize,
        _size: usize,
        _value: &[u8],
    ) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn set_effective_context(&mut self, _context_id: u32) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn get_map(&self, _map_type: MapType) -> Result<Vec<(String, String)>, Status> {
        panic!("Not implemented for current host!")
    }

    fn get_map_bytes(&self, _map_type: MapType) -> Result<Vec<(String, Bytes)>, Status> {
        panic!("Not implemented for current host!")
    }

    fn set_map(&mut self, _map_type: MapType, _map: Vec<(&str, &str)>) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn set_map_bytes(
        &mut self,
        _map_type: MapType,
        _map: Vec<(&str, &[u8])>,
    ) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn get_map_value(&self, _map_type: MapType, _key: &str) -> Result<Option<String>, Status> {
        panic!("Not implemented for current host!")
    }

    fn get_map_value_bytes(&self, _map_type: MapType, _key: &str) -> Result<Option<Bytes>, Status> {
        panic!("Not implemented for current host!")
    }

    fn set_map_value(
        &mut self,
        _map_type: MapType,
        _key: &str,
        _value: Option<&str>,
    ) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn set_map_value_bytes(
        &mut self,
        _map_type: MapType,
        _key: &str,
        _value: Option<&[u8]>,
    ) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn add_map_value(
        &mut self,
        _map_type: MapType,
        _key: &str,
        _value: &str,
    ) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn add_map_value_bytes(
        &mut self,
        _map_type: MapType,
        _key: &str,
        _value: &[u8],
    ) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn send_http_response(
        &mut self,
        _status_code: u32,
        _headers: Vec<(&str, &str)>,
        _body: Option<&[u8]>,
    ) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn get_current_time(&self) -> Result<SystemTime, Status> {
        panic!("Not implemented for current host!")
    }

    fn set_tick_period(&mut self, _period: Duration) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn resume_http_request(&mut self) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn resume_http_response(&mut self) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn define_metric(&mut self, _metric_type: MetricType, _name: &str) -> Result<u32, Status> {
        panic!("Not implemented for current host!")
    }

    fn get_metric(&self, _metric_id: u32) -> Result<u64, Status> {
        panic!("Not implemented for current host!")
    }

    fn record_metric(&mut self, _metric_id: u32, _value: u64) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn increment_metric(&mut self, _metric_id: u32, _offset: i64) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn dispatch_http_call(
        &mut self,
        _upstream: &str,
        _headers: Vec<(&str, &str)>,
        _body: Option<&[u8]>,
        _trailers: Vec<(&str, &str)>,
        _timeout: Duration,
    ) -> Result<u32, Status> {
        panic!("Not implemented for current host!")
    }

    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> {
        panic!("Not implemented for current host!")
    }

    fn get_grpc_status(&self) -> Result<(u32, Option<String>), Status> {
        panic!("Not implemented for current host!")
    }

    fn cancel_grpc_call(&mut self, _token_id: u32) -> Result<(), Status> {
        panic!("Not implemented for current host!")
    }

    fn call_foreign_function(
        &mut self,
        _function_name: &str,
        _arguments: Option<&[u8]>,
    ) -> Result<Option<Bytes>, Status> {
        panic!("Not implemented for current host!")
    }
}
