pub mod http;
pub mod ipfs;
pub mod kv;
pub mod sc;
pub mod sxt;

use std::str::FromStr;

pub use dotenvy;
pub use web3;

use bytecodec::{
    bytes::{BytesEncoder, RemainingBytesDecoder},
    io::{IoDecodeExt, IoEncodeExt},
    Encode,
};
use httpcodec::{BodyDecoder, BodyEncoder, RequestDecoder, ResponseEncoder};

pub fn encode_response(res: http::Response<Vec<u8>>) -> u32 {
    let (parts, body) = res.into_parts();
    let version = if parts.version == http::Version::HTTP_10 {
        httpcodec::HttpVersion::V1_0
    } else if parts.version == http::Version::HTTP_11 {
        httpcodec::HttpVersion::V1_1
    } else {
        panic!("Only HTTP 1.0 and 1.1 are supported")
    };
    let mut enc_res = unsafe {
        httpcodec::Response::new(
            version,
            httpcodec::StatusCode::new_unchecked(parts.status.as_u16()),
            httpcodec::ReasonPhrase::new_unchecked(parts.status.canonical_reason().unwrap_or("")),
            body,
        )
    };
    let mut enc_headers = enc_res.header_mut();
    for header in parts.headers {
        let name = header
            .0
            .map(|name| name.to_string())
            .unwrap_or("".to_string());
        let value = header.1.to_str().unwrap();
        enc_headers.add_field(unsafe { httpcodec::HeaderField::new_unchecked(&name, value) });
    }

    let mut encoder = ResponseEncoder::<BodyEncoder<BytesEncoder>>::default();
    encoder.start_encoding(enc_res).unwrap();
    let mut res_buf = Vec::new();
    encoder.encode_all(&mut res_buf).unwrap();
    let mut res_buf_with_len = (res_buf.len() as u32).to_le_bytes().to_vec();
    res_buf_with_len.append(&mut res_buf);
    let ptr = res_buf_with_len.as_ptr();
    std::mem::forget(res_buf_with_len);
    ptr as u32
}

pub fn request_from_raw_parts(req_ptr: u32, req_len: u32) -> http::Request<Vec<u8>> {
    let req_buf = unsafe { std::slice::from_raw_parts(req_ptr as *const u8, req_len as usize) };
    let mut decoder = RequestDecoder::<BodyDecoder<RemainingBytesDecoder>>::default();
    let dec_req = decoder.decode_exact(req_buf).unwrap();
    let mut req = http::Request::builder()
        .version(match dec_req.http_version() {
            httpcodec::HttpVersion::V1_0 => http::Version::HTTP_10,
            httpcodec::HttpVersion::V1_1 => http::Version::HTTP_11,
        })
        .method(http::Method::from_str(dec_req.method().as_str()).unwrap())
        .uri(http::Uri::from_str(dec_req.request_target().as_str()).unwrap());
    for header in dec_req.header().fields() {
        req = req.header(header.name(), header.value());
    }

    let req = req.body(dec_req.into_body()).unwrap();
    req
}
