use crate::ffi;
use crate::types::Header;

#[derive(Clone, Debug)]
pub struct CRequest {
    pub method: String,
    pub path: String,
    pub headers: Vec<Header>,
    pub body: Vec<u8>,
    pub request_id: [u8; 16],
    pub trace_id: [u8; 16],
}

impl CRequest {
    pub fn from_ffi(req: &ffi::nwep_request) -> Self {
        let method = unsafe { std::slice::from_raw_parts(req.method as *const u8, req.method_len) };
        let path = unsafe { std::slice::from_raw_parts(req.path as *const u8, req.path_len) };
        let headers: Vec<Header> = (0..req.header_count).map(|i| {
            let h = unsafe { &*req.headers.add(i) };
            let name = unsafe { std::slice::from_raw_parts(h.name, h.name_len) };
            let value = unsafe { std::slice::from_raw_parts(h.value, h.value_len) };
            Header {
                name: String::from_utf8_lossy(name).into_owned(),
                value: String::from_utf8_lossy(value).into_owned(),
            }
        }).collect();
        let body = if req.body.is_null() || req.body_len == 0 {
            Vec::new()
        } else {
            unsafe { std::slice::from_raw_parts(req.body, req.body_len).to_vec() }
        };
        CRequest {
            method: String::from_utf8_lossy(method).into_owned(),
            path: String::from_utf8_lossy(path).into_owned(),
            headers,
            body,
            request_id: req.request_id,
            trace_id: req.trace_id,
        }
    }
}

#[derive(Clone, Debug)]
pub struct CResponse {
    pub status: String,
    pub status_details: String,
    pub headers: Vec<Header>,
    pub body: Vec<u8>,
}

impl CResponse {
    pub fn from_ffi(resp: &ffi::nwep_response) -> Self {
        let status = unsafe { std::slice::from_raw_parts(resp.status as *const u8, resp.status_len) };
        let details = if resp.status_details.is_null() || resp.status_details_len == 0 {
            &[] as &[u8]
        } else {
            unsafe { std::slice::from_raw_parts(resp.status_details as *const u8, resp.status_details_len) }
        };
        let headers: Vec<Header> = (0..resp.header_count).map(|i| {
            let h = unsafe { &*resp.headers.add(i) };
            let name = unsafe { std::slice::from_raw_parts(h.name, h.name_len) };
            let value = unsafe { std::slice::from_raw_parts(h.value, h.value_len) };
            Header {
                name: String::from_utf8_lossy(name).into_owned(),
                value: String::from_utf8_lossy(value).into_owned(),
            }
        }).collect();
        let body = if resp.body.is_null() || resp.body_len == 0 {
            Vec::new()
        } else {
            unsafe { std::slice::from_raw_parts(resp.body, resp.body_len).to_vec() }
        };
        CResponse {
            status: String::from_utf8_lossy(status).into_owned(),
            status_details: String::from_utf8_lossy(details).into_owned(),
            headers,
            body,
        }
    }

    pub fn is_ok(&self) -> bool {
        crate::protocol::status_is_success(&self.status)
    }

    pub fn header(&self, name: &str) -> Option<&str> {
        self.headers.iter().find(|h| h.name == name).map(|h| h.value.as_str())
    }
}

#[derive(Clone, Debug)]
pub struct CNotify {
    pub event: String,
    pub path: String,
    pub notify_id: [u8; 16],
    pub has_notify_id: bool,
    pub headers: Vec<Header>,
    pub body: Vec<u8>,
}

impl CNotify {
    pub fn from_ffi(n: &ffi::nwep_notify) -> Self {
        let event = unsafe { std::slice::from_raw_parts(n.event as *const u8, n.event_len) };
        let path = unsafe { std::slice::from_raw_parts(n.path as *const u8, n.path_len) };
        let headers: Vec<Header> = (0..n.header_count).map(|i| {
            let h = unsafe { &*n.headers.add(i) };
            let name = unsafe { std::slice::from_raw_parts(h.name, h.name_len) };
            let value = unsafe { std::slice::from_raw_parts(h.value, h.value_len) };
            Header {
                name: String::from_utf8_lossy(name).into_owned(),
                value: String::from_utf8_lossy(value).into_owned(),
            }
        }).collect();
        let body = if n.body.is_null() || n.body_len == 0 {
            Vec::new()
        } else {
            unsafe { std::slice::from_raw_parts(n.body, n.body_len).to_vec() }
        };
        CNotify {
            event: String::from_utf8_lossy(event).into_owned(),
            path: String::from_utf8_lossy(path).into_owned(),
            notify_id: n.notify_id,
            has_notify_id: n.has_notify_id != 0,
            headers,
            body,
        }
    }
}
