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

use std::ptr::null;
use std::time::{Duration, SystemTime};

use crate::types::{BufferType, Bytes, GrpcStatusCode, LogLevel, MapType, MetricType, Status};

/// Logs a message.
pub fn log(level: LogLevel, message: &str) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().log(level, message))
}

/// Gets the log level.
pub fn get_log_level() -> Result<LogLevel, Status> {
    unimplemented!("Not implemented for current target")
}

/// Gets the current time.
pub fn get_current_time() -> Result<SystemTime, Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_current_time())
}

/// Sets the tick period.
pub fn set_tick_period(period: Duration) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().set_tick_period(period))
}

/// Gets the buffer.
pub fn get_buffer(
    buffer_type: BufferType,
    start: usize,
    max_size: usize,
) -> Result<Option<Bytes>, Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow()
            .get_buffer(buffer_type, start, max_size)
    })
}

/// Sets the buffer.
pub fn set_buffer(
    buffer_type: BufferType,
    start: usize,
    size: usize,
    value: &[u8],
) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .set_buffer(buffer_type, start, size, value)
    })
}

/// Gets the map.
pub fn get_map(map_type: MapType) -> Result<Vec<(String, String)>, Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_map(map_type))
}

/// Gets the map bytes.
pub fn get_map_bytes(map_type: MapType) -> Result<Vec<(String, Bytes)>, Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_map_bytes(map_type))
}

/// Sets the map.
pub fn set_map(map_type: MapType, map: Vec<(&str, &str)>) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().set_map(map_type, map))
}

/// Sets the map bytes.
pub fn set_map_bytes(map_type: MapType, map: Vec<(&str, &[u8])>) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().set_map_bytes(map_type, map))
}

/// Gets the map value.
pub fn get_map_value(map_type: MapType, key: &str) -> Result<Option<String>, Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_map_value(map_type, key))
}

/// Gets the map value bytes.
pub fn get_map_value_bytes(map_type: MapType, key: &str) -> Result<Option<Bytes>, Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_map_value_bytes(map_type, key))
}

/// Sets the map value.
pub fn set_map_value(map_type: MapType, key: &str, value: Option<&str>) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .set_map_value(map_type, key, value)
    })
}

/// Sets the map value bytes.
pub fn set_map_value_bytes(
    map_type: MapType,
    key: &str,
    value: Option<&[u8]>,
) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .set_map_value_bytes(map_type, key, value)
    })
}

/// Adds a map value.
pub fn add_map_value(map_type: MapType, key: &str, value: &str) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .add_map_value(map_type, key, value)
    })
}

/// Adds a map value bytes.
pub fn add_map_value_bytes(map_type: MapType, key: &str, value: &[u8]) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .add_map_value_bytes(map_type, key, value)
    })
}

/// Gets the property.
pub fn get_property(path: Vec<&str>) -> Result<Option<Bytes>, Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_property(path))
}

/// Sets the property.
pub fn set_property(path: Vec<&str>, value: Option<&[u8]>) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().set_property(path, value))
}

/// Gets the shared data.
pub fn get_shared_data(key: &str) -> Result<(Option<Bytes>, Option<u32>), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_shared_data(key))
}

/// Sets the shared data.
pub fn set_shared_data(key: &str, value: Option<&[u8]>, cas: Option<u32>) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().set_shared_data(key, value, cas))
}

/// Registers a shared queue.
pub fn register_shared_queue(_name: &str) -> Result<u32, Status> {
    unimplemented!("Not implemented for current target")
}

/// Resolves a shared queue.
pub fn resolve_shared_queue(_vm_id: &str, _name: &str) -> Result<Option<u32>, Status> {
    unimplemented!("Not implemented for current target")
}

/// Dequeues a shared queue.
pub fn dequeue_shared_queue(_queue_id: u32) -> Result<Option<Bytes>, Status> {
    unimplemented!("Not implemented for current target")
}

/// Enqueues a shared queue.
pub fn enqueue_shared_queue(_queue_id: u32, _value: Option<&[u8]>) -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Resumes the downstream.
pub fn resume_downstream() -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Resumes the upstream.
pub fn resume_upstream() -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Resumes the HTTP request.
pub fn resume_http_request() -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().resume_http_request())
}

/// Resumes the HTTP response.
pub fn resume_http_response() -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().resume_http_response())
}

/// Closes the downstream.
pub fn close_downstream() -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Closes the upstream.
pub fn close_upstream() -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Resets the HTTP request.
pub fn reset_http_request() -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Resets the HTTP response.
pub fn reset_http_response() -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Sends the HTTP response.
pub fn send_http_response(
    status_code: u32,
    headers: Vec<(&str, &str)>,
    body: Option<&[u8]>,
) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .send_http_response(status_code, headers, body)
    })
}

/// Dispatches an HTTP call.
pub fn dispatch_http_call(
    upstream: &str,
    headers: Vec<(&str, &str)>,
    body: Option<&[u8]>,
    trailers: Vec<(&str, &str)>,
    timeout: Duration,
) -> Result<u32, Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .dispatch_http_call(upstream, headers, body, trailers, timeout)
    })
}

/// Dispatches a gRPC call.
pub fn dispatch_grpc_call(
    upstream_name: &str,
    service_name: &str,
    method_name: &str,
    initial_metadata: Vec<(&str, &[u8])>,
    message: Option<&[u8]>,
    timeout: Duration,
) -> Result<u32, Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow().borrow_mut().dispatch_grpc_call(
            upstream_name,
            service_name,
            method_name,
            initial_metadata,
            message,
            timeout,
        )
    })
}

/// Opens a gRPC stream.
pub fn open_grpc_stream(
    _upstream_name: &str,
    _service_name: &str,
    _method_name: &str,
    _initial_metadata: Vec<(&str, &[u8])>,
) -> Result<u32, Status> {
    unimplemented!("Not implemented for current target")
}

/// Sends a gRPC stream message.
pub fn send_grpc_stream_message(
    _token: u32,
    _message: Option<&[u8]>,
    _end_stream: bool,
) -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Cancels a gRPC call.
pub fn cancel_grpc_call(token_id: u32) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().cancel_grpc_call(token_id))
}

/// Cancels a gRPC stream.
pub fn cancel_grpc_stream(_token_id: u32) -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Closes a gRPC stream.
pub fn close_grpc_stream(_token_id: u32) -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Gets the gRPC status.
pub fn get_grpc_status() -> Result<(u32, Option<String>), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_grpc_status())
}

/// Sets the effective context.
pub fn set_effective_context(context_id: u32) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().set_effective_context(context_id))
}

/// Calls a foreign function.
pub fn call_foreign_function(
    function_name: &str,
    arguments: Option<&[u8]>,
) -> Result<Option<Bytes>, Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .call_foreign_function(function_name, arguments)
    })
}

/// Indicates done state.
pub fn done() -> Result<(), Status> {
    unimplemented!("Not implemented for current target")
}

/// Defines a metric.
pub fn define_metric(metric_type: MetricType, name: &str) -> Result<u32, Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().define_metric(metric_type, name))
}

/// Gets the metric.
pub fn get_metric(metric_id: u32) -> Result<u64, Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow().get_metric(metric_id))
}

/// Records a metric.
pub fn record_metric(metric_id: u32, value: u64) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| stub.borrow().borrow_mut().record_metric(metric_id, value))
}

/// Increments a metric.
pub fn increment_metric(metric_id: u32, offset: i64) -> Result<(), Status> {
    crate::stub::STUB.with(|stub| {
        stub.borrow()
            .borrow_mut()
            .increment_metric(metric_id, offset)
    })
}

pub fn proxy_remove_header_map_value(
    _map_type: MapType,
    _key_data: *const u8,
    _key_size: usize,
) -> Status {
    unimplemented!("Not implemented for current target")
}

pub fn proxy_send_local_response(
    _status_code: u32,
    _status_code_details_data: *const u8,
    _status_code_details_size: usize,
    _body_data: *const u8,
    _body_size: usize,
    _headers_data: *const u8,
    _headers_size: usize,
    _grpc_status: i32,
) -> Status {
    unimplemented!("Not implemented for current target")
}

pub fn remove_map_value(map_type: MapType, key: &str) -> Result<(), Status> {
    unsafe {
        match proxy_remove_header_map_value(map_type, key.as_ptr(), key.len()) {
            Status::Ok => Ok(()),
            status => panic!("unexpected status: {}", status as u32),
        }
    }
}

pub fn send_grpc_response(
    grpc_status: GrpcStatusCode,
    grpc_status_message: Option<&str>,
    custom_metadata: Vec<(&str, &[u8])>,
) -> Result<(), Status> {
    let serialized_custom_metadata = utils::serialize_map_bytes(&custom_metadata);
    unsafe {
        match proxy_send_local_response(
            200,
            null(),
            0,
            grpc_status_message.map_or(null(), |grpc_status_message| grpc_status_message.as_ptr()),
            grpc_status_message.map_or(0, |grpc_status_message| grpc_status_message.len()),
            serialized_custom_metadata.as_ptr(),
            serialized_custom_metadata.len(),
            grpc_status as i32,
        ) {
            Status::Ok => Ok(()),
            status => panic!("unexpected status: {}", status as u32),
        }
    }
}

mod utils {
    use crate::types::Bytes;

    pub(super) fn serialize_map_bytes(map: &[(&str, &[u8])]) -> Bytes {
        let mut size: usize = 4;
        for (name, value) in map {
            size += name.len() + value.len() + 10;
        }
        let mut bytes: Bytes = Vec::with_capacity(size);
        bytes.extend_from_slice(&(map.len() as u32).to_le_bytes());
        for (name, value) in map {
            bytes.extend_from_slice(&(name.len() as u32).to_le_bytes());
            bytes.extend_from_slice(&(value.len() as u32).to_le_bytes());
        }
        for (name, value) in map {
            bytes.extend_from_slice(name.as_bytes());
            bytes.push(0);
            bytes.extend_from_slice(value);
            bytes.push(0);
        }
        bytes
    }
}
