/// HTTP utilities for making requests and handling headers
/// 
/// Provides functionality for:
/// - Creating HTTP clients (sync and async)
/// - Building and parsing headers
/// - Making HTTP requests with custom headers
/// 
/// # Examples
/// 
/// ```
/// use doe::httprs;
/// 
/// // Create a client with custom headers
/// let headers = httprs::headers![
///     "Content-Type" => "application/json",
///     "Authorization" => "Bearer token"
/// ];
/// 
/// // Make a GET request
/// let client = httprs::sync_client_with_headers(headers);
/// let response = client.sync_get_bytes("https://api.example.com/data");
/// ```
#[allow(warnings)]
#[cfg(feature = "httprs")]
pub mod httprs {
    use std::ptr::replace;

    pub use reqwest::blocking::Client;
    pub use reqwest::header::HeaderMap;
    pub use reqwest::header::HeaderName;
    pub use reqwest::*;

    use crate::hashmap;
    use crate::DebugPrint;
    use crate::DynError;
    use crate::Str;

    /// Macro for creating HeaderMap from key-value pairs
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs;
    /// 
    /// let headers = httprs::headers![
    ///     "Content-Type" => "application/json",
    ///     "Authorization" => "Bearer token"
    /// ];
    /// ```
    #[macro_export]
    macro_rules! headers {
        ($($key:expr => $val:expr),*) => {
            {
            let mut headers = $crate::ctf::HeaderMap::new();
            $(
                let header_name = $crate::ctf::HeaderName::from_lowercase($key.to_lowercase().as_bytes()).unwrap();
                headers.insert(header_name,$val.parse().unwrap());
            )*
            headers
            }
        };
    }
    /// Trait for converting types to HTTP headers and URLs
    /// 
    /// Provides methods for:
    /// - Extracting headers from strings
    /// - Parsing URLs from strings
    /// - Combining both operations
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs::ToHeader;
    /// 
    /// let http_request = r#"
    /// GET /api/data HTTP/1.1
    /// Host: api.example.com
    /// Authorization: Bearer token
    /// "#;
    /// 
    /// let (url, headers) = http_request.url_and_headers();
    /// ```
    pub trait ToHeader {
        /// Convert the implementing type to HTTP headers
        fn to_headers(&self) -> HeaderMap;

        /// Extract URL from the implementing type
        fn to_url(&self) -> Option<String>;

        /// Extract both URL and headers from the implementing type
        /// 
        /// Returns a tuple containing:
        /// - Option<String>: The parsed URL if available
        /// - HeaderMap: The parsed headers
        fn url_and_headers(&self) -> (Option<String>, HeaderMap);
    }
    
    impl<T: ToString> ToHeader for T {
        /// Extracts both URL and headers from the implementing type
        /// 
        /// # Returns
        /// A tuple containing:
        /// - Option<String>: The parsed URL if available
        /// - HeaderMap: The parsed headers
        /// 
        /// # Examples
        /// 
        /// ```
        /// use doe::httprs::ToHeader;
        /// 
        /// let http_request = r#"
        /// GET /api/data HTTP/1.1
        /// Host: api.example.com
        /// Authorization: Bearer token
        /// "#;
        /// 
        /// let (url, headers) = http_request.url_and_headers();
        /// ```
        fn url_and_headers(&self) -> (Option<String>, HeaderMap) {
            let header_str = self.to_string();
            (header_str.to_url(), header_str.to_headers())
        }

        /// Extracts URL from the implementing type
        /// 
        /// Parses the first line of the HTTP request to extract the URL
        /// using the Host header and request path
        /// 
        /// # Returns
        /// Option<String> containing the parsed URL if available
        /// 
        /// # Examples
        /// 
        /// ```
        /// use doe::httprs::ToHeader;
        /// 
        /// let http_request = r#"
        /// GET /api/data HTTP/1.1
        /// Host: api.example.com
        /// Authorization: Bearer token
        /// "#;
        /// 
        /// let url = http_request.to_url();
        /// ```
        fn to_url(&self) -> Option<String> {
            let header_str = self.to_string();
            let mut header_str_split = header_str.split("\n");
            let mut hash_map: std::collections::HashMap<String, String> = hashmap!();
            header_str_split
                .clone()
                .filter(|s| !s.is_empty())
                .map(|s| {
                    s.to_string()
                        .trim()
                        .split(":")
                        .filter(|s| !s.is_empty())
                        .map(|s| s.to_string())
                        .collect()
                })
                .filter(|s: &Vec<String>| s.to_owned().len() > 1)
                .map(|s| {
                    let header_name = s.first().expect("get header_name error").to_string();
                    let header_val = &s[1..].join(":").to_string().replace("\"", "'");
                    vec![header_name, header_val.to_string()]
                })
                .collect::<Vec<Vec<_>>>()
                .iter()
                .for_each(|s| {
                    let header_name = s
                        .first()
                        .expect("get header_name error")
                        .to_string()
                        .to_ascii_lowercase();
                    let header_val = s.get(1).expect("get header_val error").to_string();
                    let _ = hash_map
                        .insert(header_name, header_val);
                });

            if let Some(fist_line) = header_str_split.filter(|s|!s.is_empty()).next() {
                if let Some(host) = hash_map.get("host") {
                    let path = &fist_line.trim().split(" ").collect::<Vec<_>>()[1].trim();
                    let url = host.to_string().push_back(path.to_string());
                    Some(url.trim().to_string())
                } else {
                    None
                }
            } else {
                None
            }
        }

        /// Converts the implementing type to HTTP headers
        /// 
        /// Parses the input string to extract key-value pairs and
        /// converts them into a HeaderMap
        /// 
        /// # Returns
        /// HeaderMap containing the parsed headers
        /// 
        /// # Examples
        /// 
        /// ```
        /// use doe::httprs::ToHeader;
        /// 
        /// let http_request = r#"
        /// GET /api/data HTTP/1.1
        /// Host: api.example.com
        /// Authorization: Bearer token
        /// "#;
        /// 
        /// let headers = http_request.to_headers();
        /// ```
        fn to_headers(&self) -> HeaderMap {
            let header_str = self.to_string();
            let header_str_split = header_str.split("\n");
            let head_vec = header_str_split
                .filter(|s| !s.is_empty())
                .map(|s| {
                    s.to_string()
                        .trim()
                        .split(":")
                        .filter(|s| !s.is_empty())
                        .map(|s| s.to_string())
                        .collect()
                })
                .filter(|s: &Vec<String>| s.to_owned().len() > 1)
                .map(|s| {
                    let header_name = s.first().expect("get header_name error").to_string();
                    let header_val = &s[1..].join(":").to_string().replace("\"", "'");
                    vec![header_name, header_val.to_string()]
                })
                .collect::<Vec<Vec<_>>>();
            let mut headers = HeaderMap::new();
            for kv in head_vec.iter() {
                let mut kv = kv.iter();
                let key = kv.next().unwrap();
                let val = kv.next().unwrap();
                let header_name =
                    HeaderName::from_lowercase(key.to_lowercase().as_bytes()).unwrap();
                headers.insert(header_name, val.parse().unwrap());
            }
            headers
        }
    }

    /// Trait for asynchronous HTTP client operations
    /// 
    /// Provides methods for making asynchronous HTTP requests and receiving raw byte responses
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs::{ClientAsyncSend, async_client};
    /// 
    /// #[tokio::main]
    /// async fn main() {
    ///     let client = async_client().await;
    ///     
    ///     // Make GET request
    ///     let response = client.async_get_bytes("https://api.example.com/data").await;
    ///     
    ///     // Make POST request
    ///     let data = b"example data";
    ///     let response = client.async_post_bytes("https://api.example.com/data", data).await;
    /// }
    /// ```
    pub trait ClientAsyncSend {
        /// Make an asynchronous POST request and return the raw response bytes
        /// 
        /// # Arguments
        /// * `url` - The target URL as a string
        /// * `body` - The request body as a byte slice
        /// 
        /// # Returns
        /// Vec<u8> containing the raw response bytes
        async fn async_post_bytes(&self, url: impl ToString, body: &[u8]) -> Vec<u8>;

        /// Make an asynchronous GET request and return the raw response bytes
        /// 
        /// # Arguments
        /// * `url` - The target URL as a string
        /// 
        /// # Returns
        /// Vec<u8> containing the raw response bytes
        async fn async_get_bytes(&self, url: impl ToString) -> Vec<u8>;
    }

    impl ClientAsyncSend for reqwest::Client {
        async fn async_post_bytes(&self, url: impl ToString, body: &[u8]) -> Vec<u8> {
            let mut resp = self
                .post(url.to_string())
                .body(body.to_vec())
                .send()
                .await
                .unwrap();

            resp.bytes().await.unwrap().to_vec()
        }

        async fn async_get_bytes(&self, url: impl ToString) -> Vec<u8> {
            let mut resp = self.get(url.to_string()).send().await.unwrap();
            resp.bytes().await.unwrap().to_vec()
        }
    }

    /// Trait for synchronous HTTP client operations
    /// 
    /// Provides methods for making synchronous HTTP requests and receiving raw byte responses
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs::{ClientSyncSend, sync_client};
    /// 
    /// fn main() {
    ///     let client = sync_client();
    ///     
    ///     // Make GET request
    ///     let response = client.sync_get_bytes("https://api.example.com/data");
    ///     
    ///     // Make POST request
    ///     let data = b"example data";
    ///     let response = client.sync_post_bytes("https://api.example.com/data", data);
    /// }
    /// ```
    pub trait ClientSyncSend {
        /// Make a synchronous POST request and return the raw response bytes
        /// 
        /// # Arguments
        /// * `url` - The target URL as a string
        /// * `body` - The request body as a byte slice
        /// 
        /// # Returns
        /// Vec<u8> containing the raw response bytes
        fn sync_post_bytes(&self, url: impl ToString, body: &[u8]) -> Vec<u8>;

        /// Make a synchronous GET request and return the raw response bytes
        /// 
        /// # Arguments
        /// * `url` - The target URL as a string
        /// 
        /// # Returns
        /// Vec<u8> containing the raw response bytes
        fn sync_get_bytes(&self, url: impl ToString) -> Vec<u8>;
    }

    impl ClientSyncSend for reqwest::blocking::Client {
        fn sync_post_bytes(&self, url: impl ToString, body: &[u8]) -> Vec<u8> {
            let mut resp = self
                .post(url.to_string())
                .body(body.to_vec())
                .send()
                .unwrap();

            let mut body = vec![];
            resp.copy_to(&mut body).unwrap();
            body
        }
        fn sync_get_bytes(&self, url: impl ToString) -> Vec<u8> {
            let mut resp = self.get(url.to_string()).send().unwrap();
            let mut body = vec![];
            resp.copy_to(&mut body).unwrap();
            body
        }
    }
    /// Creates a new asynchronous HTTP client with default settings
    /// 
    /// # Returns
    /// reqwest::Client instance
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs;
    /// 
    /// #[tokio::main]
    /// async fn main() {
    ///     let client = httprs::async_client().await;
    /// }
    /// ```
    pub async fn async_client() -> reqwest::Client {
        let client_builder = reqwest::ClientBuilder::new();
        let client = client_builder.build().unwrap();
        client
    }

    /// Creates a new synchronous HTTP client with default settings
    /// 
    /// # Returns
    /// reqwest::blocking::Client instance
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs;
    /// 
    /// fn main() {
    ///     let client = httprs::sync_client();
    /// }
    /// ```
    pub fn sync_client() -> reqwest::blocking::Client {
        let client_builder = reqwest::blocking::ClientBuilder::new();
        let client = client_builder.build().unwrap();
        client
    }

    /// Creates a new asynchronous HTTP client with custom headers
    /// 
    /// # Arguments
    /// * `headers` - HeaderMap containing the custom headers
    /// 
    /// # Returns
    /// reqwest::Client instance with configured headers
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs::{async_client_with_headers, headers};
    /// 
    /// #[tokio::main]
    /// async fn main() {
    ///     let headers = headers![
    ///         "Content-Type" => "application/json",
    ///         "Authorization" => "Bearer token"
    ///     ];
    ///     let client = async_client_with_headers(headers).await;
    /// }
    /// ```
    pub async fn async_client_with_headers(headers: reqwest::header::HeaderMap) -> reqwest::Client {
        let client_builder = reqwest::ClientBuilder::new();
        let client = client_builder.default_headers(headers).build().unwrap();
        client
    }

    /// Creates a new asynchronous HTTP client builder for custom configuration
    /// 
    /// # Returns
    /// reqwest::ClientBuilder instance
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs;
    /// 
    /// #[tokio::main]
    /// async fn main() {
    ///     let client = httprs::async_client_builder()
    ///         .await
    ///         .danger_accept_invalid_certs(true)
    ///         .build()
    ///         .unwrap();
    /// }
    /// ```
    pub async fn async_client_builder() -> reqwest::ClientBuilder {
        reqwest::ClientBuilder::new()
    }

    /// Creates a new synchronous HTTP client with custom headers
    /// 
    /// # Arguments
    /// * `headers` - HeaderMap containing the custom headers
    /// 
    /// # Returns
    /// reqwest::blocking::Client instance with configured headers
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs::{sync_client_with_headers, headers};
    /// 
    /// fn main() {
    ///     let headers = headers![
    ///         "Content-Type" => "application/json",
    ///         "Authorization" => "Bearer token"
    ///     ];
    ///     let client = sync_client_with_headers(headers);
    /// }
    /// ```
    pub fn sync_client_with_headers(
        headers: reqwest::header::HeaderMap,
    ) -> reqwest::blocking::Client {
        let client_builder = reqwest::blocking::ClientBuilder::new();
        let client = client_builder.default_headers(headers).build().unwrap();
        client
    }

    /// Creates a new synchronous HTTP client builder for custom configuration
    /// 
    /// # Returns
    /// reqwest::blocking::ClientBuilder instance
    /// 
    /// # Examples
    /// 
    /// ```
    /// use doe::httprs;
    /// 
    /// fn main() {
    ///     let client = httprs::sync_client_builder()
    ///         .danger_accept_invalid_certs(true)
    ///         .build()
    ///         .unwrap();
    /// }
    /// ```
    pub fn sync_client_builder() -> reqwest::blocking::ClientBuilder {
        reqwest::blocking::ClientBuilder::new()
    }

    /// Re-export of reqwest::Method for convenience
    pub use reqwest::Method;
}





#[cfg(feature = "httprs")]
pub use httprs::*;
