///all ctf utils functions, activate feature by `cargo add doe -F ctf`
#[allow(warnings)]
#[cfg(feature = "ctf")]
pub mod ctf {
    ///parse_html
    ///```rust
    ///parse_html(html,"span", None).dprintln();//find all span
    ///parse_html(html,"h2", Some("id")).dprintln(); //find all h2 and get id value
    ///```
    ///
    pub fn parse_html<T: ToString>(html: T, selectors: T, attr: Option<T>) -> Vec<String> {
        use scraper::{Html, Selector};
        let html = html.to_string();
        let selectors = selectors.to_string();
        let document = Html::parse_document(&html);
        let img_selector = Selector::parse(&selectors).expect("Failed to parse selector");
        let mut res = Vec::new();
        if let Some(attr) = attr {
            let attr = attr.to_string();
            for element in document.select(&img_selector) {
                if let Some(src) = element.value().attr(&attr) {
                    res.push(src.to_string());
                }
            }
        } else {
            for element in document.select(&img_selector) {
                res.push(element.html());
            }
        }
        res
    }

    pub fn find_png_urls<T: ToString>(text: T) -> Vec<String> {
        use regex::Regex;
        let text = text.to_string();
        let re = Regex::new(r#"https?://[^\s\"']+\.(?:png|PNG)"#).expect("Failed to compile regex");
        re.find_iter(&text)
            .map(|m| m.as_str().to_string())
            .collect()
    }

    pub fn find_urls<T: ToString>(text: T) -> Vec<String> {
        use regex::Regex;
        let text = text.to_string();
        let re = Regex::new(r#"https?://[^\s"']+"#).expect("Failed to compile regex");
        re.find_iter(&text)
            .map(|m| m.as_str().to_string())
            .collect()
    }

    pub fn hex_encode<T: AsRef<[u8]>>(data: T) -> String {
        hex::encode(data)
    }

    pub fn hex_decode<T: AsRef<[u8]>>(data: T) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
        Ok(hex::decode(data).unwrap())
    }

    ///gerate number between start..=end
    ///```rust
    ///use doe::{Print, ctf::random};
    ///random(1.5, 10.54).println();
    /// ```
    pub fn random<T: std::cmp::PartialOrd + rand::distributions::uniform::SampleUniform>(
        start: T,
        end: T,
    ) -> T {
        use rand::Rng;
        let mut rng = rand::thread_rng();
        rng.gen_range(start..=end)
    }

    pub fn base64_encode<T: AsRef<[u8]>>(s: T) -> String {
        use base64::{engine::general_purpose, Engine as _};
        general_purpose::STANDARD_NO_PAD.encode(s)
    }
    pub fn base64_decode<T: AsRef<[u8]>>(s: T) -> Vec<u8> {
        use base64::{engine::general_purpose, Engine as _};
        general_purpose::STANDARD_NO_PAD.decode(s).unwrap()
    }

    pub fn url_encode(url: &str) -> String {
        use percent_encoding::{percent_decode_str, utf8_percent_encode};
        let encoded = utf8_percent_encode(url, percent_encoding::NON_ALPHANUMERIC);
        encoded.to_string()
    }
    pub fn url_decode(url: &str) -> String {
        use percent_encoding::{percent_decode_str, utf8_percent_encode};
        let decoded = percent_decode_str(url).decode_utf8().unwrap();
        decoded.to_string()
    }
    ///unicode_to_chinese
    /// ```rust
    ///   assert_eq!(chinese_to_unicode("学习").unwrap(),"\\u5b66\\u4e60");
    ///   assert_eq!(unicode_to_chinese("\\u5b66\\u4e60").unwrap(),"学习");
    /// ```
    ///
    pub fn unicode_to_chinese(unicode_str: impl ToString) -> Option<String> {
        use unicode_segmentation::UnicodeSegmentation;
        let converted = unicode_str
            .to_string()
            .split("\\u")
            .filter(|&segment| !segment.is_empty())
            .map(|segment| u32::from_str_radix(segment, 16))
            .collect::<Result<Vec<u32>, _>>()
            .ok()
            .map(|code_points| {
                code_points
                    .iter()
                    .map(|&code_point| std::char::from_u32(code_point).unwrap_or('\u{FFFD}'))
                    .collect::<String>()
            });
        converted
    }
    ///chinese_to_unicode
    /// ```rust
    ///   assert_eq!(chinese_to_unicode("学习").unwrap(),"\\u5b66\\u4e60");
    ///   assert_eq!(unicode_to_chinese("\\u5b66\\u4e60").unwrap(),"学习");
    /// ```
    ///
    pub fn chinese_to_unicode(chinese_str: &str) -> Option<String> {
        let converted = chinese_str
            .chars()
            .map(|ch| ch.escape_unicode().to_string())
            .collect::<Vec<String>>()
            .join("");
        Some(converted.replace("{", "").replace("}", ""))
    }

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

    use crate::DynError;

    #[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
            }
        };
    }
    pub trait ToHeader {
        fn to_headers(&self) -> HeaderMap;
    }
    impl<T: ToString> ToHeader for T {
        fn to_headers(&self) -> HeaderMap {
            let head_str = self.to_string();
            let head_vec = head_str
                .split("\n")
                .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();
                    vec![header_name, header_val.to_string()]
                })
                .collect::<Vec<Vec<_>>>();

            let mut headers = crate::ctf::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 =
                    crate::ctf::HeaderName::from_lowercase(key.to_lowercase().as_bytes()).unwrap();
                headers.insert(header_name, val.parse().unwrap());
            }
            headers
        }
    }
    pub trait ToJsonValue {
        fn to_json_value(&self) -> serde_json::Value;
    }
    impl ToJsonValue for &[u8] {
        fn to_json_value(&self) -> serde_json::Value {
            serde_json::from_slice(&self).unwrap()
        }
    }
    impl ToJsonValue for Vec<u8> {
        fn to_json_value(&self) -> serde_json::Value {
            serde_json::from_slice(self).unwrap()
        }
    }

    pub trait ClientSend {
        fn post_bytes(&self, url: impl ToString, body: &[u8]) -> Option<Vec<u8>>;
        fn get_bytes(&self, url: impl ToString) -> Option<Vec<u8>>;
    }
    impl ClientSend for reqwest::blocking::Client {
        fn post_bytes(&self, url: impl ToString, body: &[u8]) -> Option<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();
            Some(body)
        }
        fn get_bytes(&self, url: impl ToString) -> Option<Vec<u8>> {
            let mut resp = self.get(url.to_string()).send().unwrap();
            let mut body = vec![];
            resp.copy_to(&mut body).unwrap();
            Some(body)
        }
    }
    pub fn client() -> reqwest::blocking::Client {
        let client_builder = reqwest::blocking::ClientBuilder::new();
        let client = client_builder.build().unwrap();
        client
    }

    pub fn 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
    }
}
#[cfg(feature = "ctf")]
pub use ctf::*;
