use json::{JsonValue, object};
use std::{str};
use std::collections::HashMap;
use std::fmt::Debug;
use std::str::FromStr;
use log::debug;
use reqwest::{Method as OtherMethod};
use reqwest::blocking::multipart::{Form};
use serde_json::Value;


#[derive(Debug)]
pub struct Http {
    url: String,
    method: Method,
    header: HashMap<String, String>,
    debug: bool,
    read_timeout: u64,
    write_timeout: u64,
    query: String,

    content_type: ContentType,
    param_form_data: Option<Form>,
    param_form_urlencoded: HashMap<String, String>,
    param_raw_json: Value,
    param_raw_text: String,
    param_raw_stream: Vec<u8>,
    status: u16,
    response_content_type: ContentType,
    response_header: HashMap<String, String>,

}

impl Default for Http {
    fn default() -> Self {
        Self::new()
    }
}

impl Http {
    pub fn new() -> Http {
        Self {
            url: "".to_string(),
            method: Method::new(),
            header: Default::default(),
            debug: false,
            read_timeout: 5,
            write_timeout: 5,
            query: "".to_string(),
            content_type: ContentType::None,
            param_form_data: None,
            param_form_urlencoded: Default::default(),
            param_raw_json: Default::default(),
            param_raw_text: "".to_string(),
            param_raw_stream: vec![],
            status: 0,
            response_content_type: ContentType::None,
            response_header: Default::default(),
        }
    }
    pub fn set_read_timeout(&mut self, secs: u64) -> &mut Self {
        self.read_timeout = secs;
        self
    }
    pub fn set_write_timeout(&mut self, secs: u64) -> &mut Self {
        self.write_timeout = secs;
        self
    }
    /// 调试
    pub fn debug(&mut self, open: bool) -> &mut Self {
        self.debug = open;
        self
    }
    /// GET请求
    pub fn get(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::GET;
        self
    }
    /// POST请求
    pub fn post(&mut self, url: &str) -> &mut Http {
        self.url = url.to_string();
        self.method = Method::POST;
        self
    }
    pub fn options(&mut self, url: &str) -> &mut Http {
        self.url = url.to_string();
        self.method = Method::OPTIONS;
        self
    }
    pub fn put(&mut self, url: &str) -> &mut Http {
        self.url = url.to_string();
        self.method = Method::PUT;
        self
    }
    pub fn patch(&mut self, url: &str) -> &mut Http {
        self.url = url.to_string();
        self.method = Method::PATCH;
        self
    }
    pub fn head(&mut self, url: &str) -> &mut Http {
        self.url = url.to_string();
        self.method = Method::HEAD;
        self
    }
    pub fn delete(&mut self, url: &str) -> &mut Http {
        self.url = url.to_string();
        self.method = Method::DELETE;
        self
    }
    pub fn header(&mut self, key: &str, value: &str) -> &mut Http {
        self.header.insert(key.to_string(), value.to_string());
        self
    }
    /// bearer 登陆头
    pub fn bearer_auth(&mut self, token: &str) -> &mut Http {
        self.header.insert("Authorization".to_string(), format!("Bearer {}", token));
        self
    }
    pub fn cookie(&mut self, key: &str, value: &str) -> &mut Http {
        self.header.insert("Cookie".to_string(), format!("{}={}", key, value));
        self
    }
    /// query 请求地址参数
    pub fn query(&mut self, mut data: JsonValue) -> &mut Self {
        self.content_type = ContentType::Plain;
        let mut t = vec![];
        for (key, value) in data.entries_mut() {
            t.push(format!("{}={}", key, value))
        };
        self.query = t.join("&");
        self.url = format!("{}?{}", self.url, self.query);
        self
    }
    /// form-data 数据类型
    pub fn form_data(&mut self, data: Vec<FormData>) -> &mut Self
    {
        self.content_type = ContentType::FormData;

        let mut form = Some(Form::default());
        for item in data.iter() {
            match item {
                FormData::Text(key, value) => {
                    form = Some(form.take().unwrap().text(key.clone(), value.to_string().clone()));
                }
                FormData::File(key, path) => {
                    form = Some(form.take().unwrap().file(key.clone(), path.to_string().clone()).unwrap_or_default());
                }
                FormData::None => {}
            }
        }
        self.param_form_data = form;
        self
    }
    /// form-urlencoded 数据类型
    pub fn form_urlencoded(&mut self, data: JsonValue) -> &mut Self {
        self.content_type = ContentType::FormUrlencoded;
        let mut params = HashMap::new();
        for (key, value) in data.entries() {
            params.insert(key.to_string(), value.to_string());
        }
        self.param_form_urlencoded = params;
        self
    }
    /// json 数据类型
    pub fn raw_json(&mut self, data: JsonValue) -> &mut Self {
        self.content_type = ContentType::Json;
        if !data.is_empty() {
            self.param_raw_json = Value::from_str(&data.dump()).unwrap();
        }
        self
    }
    pub fn raw_xml(&mut self, data: &str) -> &mut Self {
        self.header("Content-Type", ContentType::Xml.str().as_str());
        self.content_type = ContentType::Xml;
        self.param_raw_text = data.to_string();
        self
    }
    pub fn raw_html(&mut self, data: &str) -> &mut Self {
        self.header("Content-Type", ContentType::Html.str().as_str());
        self.content_type = ContentType::Html;
        self.param_raw_text = data.to_string();
        self
    }
    pub fn raw_text(&mut self, data: &str) -> &mut Self {
        self.header("Content-Type", ContentType::Plain.str().as_str());
        self.content_type = ContentType::Plain;
        self.param_raw_text = data.to_string();
        self
    }
    pub fn raw_stream(&mut self, data: Vec<u8>) -> &mut Self {
        self.header("Content-Type", ContentType::Stream.str().as_str());
        self.content_type = ContentType::Stream;
        self.header("Content-Length", data.len().to_string().leak());
        self.param_raw_stream = data;
        self
    }

    /// 发送回执
    fn send(&mut self, mode: &str) -> Result<Vec<u8>, String> {
        let client = reqwest::blocking::Client::new();
        let mut request = match self.method {
            Method::GET => {
                client.get(self.url.as_str())
            }
            Method::POST => {
                client.post(self.url.as_str())
            }
            Method::OPTIONS => {
                client.request(OtherMethod::OPTIONS, self.url.as_str())
            }
            Method::PATCH => {
                client.patch(self.url.as_str())
            }
            Method::HEAD => {
                client.head(self.url.as_str())
            }
            Method::DELETE => {
                client.delete(self.url.as_str())
            }
            Method::PUT => {
                client.put(self.url.as_str())
            }
            Method::TRACE => {
                client.request(OtherMethod::TRACE, self.url.as_str())
            }
            Method::NONE => {
                client.request(OtherMethod::CONNECT, self.url.as_str())
            }
        };

        match self.content_type {
            ContentType::FormData => {
                request = request.multipart(self.param_form_data.take().unwrap());
            }
            ContentType::FormUrlencoded => {
                request = request.form(&self.param_form_urlencoded);
            }
            ContentType::Json => {
                request = request.json(&self.param_raw_json);
            }
            ContentType::Xml | ContentType::Plain | ContentType::Html => {
                let body = self.param_raw_text.as_mut_str();
                request = request.body(body.as_bytes().to_vec());
            }
            ContentType::Stream => {
                request = request.body(self.param_raw_stream.clone());
            }
            ContentType::None => {}
        }


        for (key, value) in self.header.iter() {
            request = request.header(key.clone(), value.clone());
        }

        if self.debug {
            debug!("发送数据: {:#?}",request);
        }
        match request.send() {
            Ok(e) => {
                if self.debug {
                    debug!("返回数据: {:#?}",e);
                }
                self.status = e.status().as_u16();
                self.response_header = HashMap::new();
                for (key, value) in e.headers().clone() {
                    if key.clone().is_some() {
                        self.response_header.insert(key.unwrap().to_string(), value.to_str().unwrap().trim_start_matches("\"").trim_end_matches("\"").to_string());
                    }
                }
                match self.response_header.get("content-type") {
                    Some(header) => {
                        let value = header.clone();
                        self.response_content_type = ContentType::from(&value);
                        match value {
                            s if s.contains("application/json") => {
                                let json = e.json::<Value>();
                                match json {
                                    Ok(e) => {
                                        Ok(e.to_string().as_bytes().to_vec())
                                    }
                                    Err(e) => Err(e.to_string())
                                }
                            }
                            _ => {
                                let text = e.text();
                                match text {
                                    Ok(e) => {
                                        Ok(e.as_bytes().to_vec())
                                    }
                                    Err(e) => Err(e.to_string())
                                }
                            }
                        }
                    }
                    None => {
                        match mode {
                            "json" => {
                                let r = e.json::<Value>();
                                match r {
                                    Ok(e) => Ok(e.to_string().as_bytes().to_vec()),
                                    Err(_e) => Ok("".to_string().as_bytes().to_vec())
                                }
                            }
                            "text" => {
                                let r = e.text();
                                match r {
                                    Ok(e) => Ok(e.as_bytes().to_vec()),
                                    Err(e) => Err(e.to_string())
                                }
                            }
                            "cookie" => {
                                let cookies = e.cookies().collect::<Vec<_>>();
                                let mut list = HashMap::new();
                                for cookie in cookies.iter() {
                                    list.insert(cookie.name().to_string(), cookie.value().to_string());
                                }
                                Ok(JsonValue::from(list).dump().as_bytes().to_vec())
                            }
                            "stream" => {
                                let r = e.bytes().unwrap().to_vec();
                                Ok(r)
                            }
                            "info" => {
                                let r = e.text();
                                match r {
                                    Ok(e) => Ok(e.as_bytes().to_vec()),
                                    Err(e) => Err(e.to_string())
                                }
                            }
                            &_ => {
                                Ok(vec![])
                            }
                        }
                    }
                }
            }
            Err(e) => {
                Err(e.to_string())
            }
        }
    }
    pub fn cookies(&mut self) -> Result<JsonValue, String> {
        match self.send("cookie") {
            Ok(e) => {
                let res = unsafe { String::from_utf8_unchecked(e) };
                Ok(json::parse(&res).unwrap_or(object! {}))
            }
            Err(e) => Err(e)
        }
    }
    /// 返回 text
    pub fn text(&mut self) -> Result<String, String> {
        match self.send("text") {
            Ok(e) => {
                let res = unsafe { String::from_utf8_unchecked(e) };
                Ok(res)
            }
            Err(e) => Err(e)
        }
    }
    /// 返回 JSON
    pub fn json(&mut self) -> Result<JsonValue, String> {
        match self.send("json") {
            Ok(e) => {
                let res = unsafe { String::from_utf8_unchecked(e) };
                Ok(json::parse(&res).unwrap_or(object! {}))
            }
            Err(e) => Err(e)
        }
    }
    /// 返回 info
    pub fn info(&mut self) -> Result<(u16, String, String, HashMap<String, String>), String> {
        match self.send("info") {
            Ok(e) => {
                let res = unsafe { String::from_utf8_unchecked(e) };
                Ok((self.status, self.response_content_type.str(), res, self.response_header.clone()))
            }
            Err(e) => Err(e)
        }
    }

    pub fn stream(&mut self) -> Result<Vec<u8>, String> {
        match self.send("stream") {
            Ok(e) => Ok(e),
            Err(e) => Err(e)
        }
    }
}

#[derive(Clone, Debug)]
pub enum Method {
    GET,
    POST,
    OPTIONS,
    PATCH,
    HEAD,
    DELETE,
    TRACE,
    PUT,
    NONE,
}

impl Method {
    fn new() -> Self {
        Method::GET
    }
}
#[derive(Clone, Debug)]
pub enum Version {
    Http09,
    Http10,
    Http11,
    H2,
    H3,
    None,
}

impl Version {
    pub fn str(&mut self) -> String {
        match self {
            Version::Http09 => "HTTP/0.9",
            Version::Http10 => "HTTP/1.0",
            Version::Http11 => "HTTP/1.1",
            Version::H2 => "HTTP/2.0",
            Version::H3 => "HTTP/3.0",
            Version::None => ""
        }.to_string()
    }
    pub fn from(name: &str) -> Version {
        match name {
            "HTTP/0.9" => Self::Http09,
            "HTTP/1.0" => Self::Http10,
            "HTTP/1.1" => Self::Http11,
            "HTTP/2.0" => Self::H2,
            "HTTP/3.0" => Self::H3,
            _ => Self::None
        }
    }
    pub fn set_version(name: &str) -> Version {
        match name {
            "0.9" => Self::Http09,
            "1.0" => Self::Http10,
            "1.1" => Self::Http11,
            "2.0" => Self::H2,
            "3.0" => Self::H3,
            _ => Self::None
        }
    }
}


/// 请求
#[derive(Clone, Debug)]
pub enum Body {
    Json(JsonValue),
    Text(String),
    Xml(String),
    FormData(Vec<u8>),
    XWwwFormUrlencoded(String),
    None,
}

impl Default for Body {
    fn default() -> Self {
        Self::new()
    }
}

impl Body {
    pub fn new() -> Body {
        Self::None
    }
    pub fn bytes(&mut self) -> Vec<u8> {
        match self {
            Body::Json(e) => e.to_string().as_bytes().to_vec(),
            Body::Text(e) => e.clone().as_bytes().to_vec(),
            Body::Xml(e) => e.clone().as_bytes().to_vec(),
            Body::FormData(e) => e.to_vec().clone(),
            Body::XWwwFormUrlencoded(e) => e.clone().as_bytes().to_vec(),
            Body::None => "".as_bytes().to_vec()
        }
    }
}

#[derive(Clone, Debug)]
pub enum ContentType {
    FormData,
    FormUrlencoded,
    Json,
    Xml,
    Plain,
    Html,
    Stream,
    None,
}
impl ContentType {
    pub fn str(&mut self) -> String {
        match self {
            ContentType::Xml => "application/xml",
            ContentType::Plain => "text/plain",
            ContentType::Html => "text/html",
            ContentType::Stream => "application/octet-stream",
            _ => "",
        }.to_string()
    }
    pub fn from(name: &str) -> Self {
        match name {
            "application/xml" => ContentType::Xml,
            "text/plain" => ContentType::Plain,
            "text/html" => ContentType::Html,
            "application/octet-stream" => ContentType::Stream,
            _ => ContentType::None
        }
    }
}
#[derive(Clone, Debug)]
pub enum FormData {
    Text(String, String),
    File(String, String),
    None,
}