use json::{array, object, JsonValue};
use log::{debug, error, info};
use std::collections::HashMap;
use std::fmt::Debug;
use std::{env, fs, str};
use std::io::Write;
use std::path::Path;
use std::process::Command;
use std::str::Lines;
use xmltree::Element;

pub struct Client {
    pub url: String,
    debug: bool,
    pub method: Method,
    pub header: HashMap<String, String>,
    body_data: JsonValue,
    pub content_type: ContentType,
    proxy: ProxyInfo,
    cert_p12: bool,
    cert_p12_filename: String,
    cert_p12_password: String,
    args: Vec<String>,
    /// 请求超时
    request_timeout: usize,
    /// 响应超时
    response_timeout: usize,
    /// 重试次数
    retry:usize
}

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

impl Client {
    pub fn new() -> Self {
        Self {
            url: "".to_string(),
            debug: false,
            method: Method::NONE,
            header: Default::default(),
            body_data: object! {},
            content_type: ContentType::Text,
            proxy: ProxyInfo::None,
            cert_p12: false,
            cert_p12_filename: "".to_string(),
            cert_p12_password: "".to_string(),
            args: vec![],
            request_timeout: 0,
            response_timeout: 0,
            retry: 0,
        }
    }
    pub fn debug(&mut self) -> &mut Self {
        self.debug = true;
        self
    }
    pub fn post(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::POST;
        self
    }
    pub fn get(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::GET;
        self
    }
    pub fn put(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::PUT;
        self
    }
    pub fn patch(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::PATCH;
        self
    }
    pub fn delete(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::DELETE;
        self
    }
    pub fn head(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::HEAD;
        self
    }
    pub fn options(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::OPTIONS;
        self
    }
    pub fn trace(&mut self, url: &str) -> &mut Self {
        self.url = url.to_string();
        self.method = Method::TRACE;
        self
    }
    pub fn header(&mut self, key: &str, value: &str) -> &mut Self {
        self.header.insert(key.to_string(), value.to_string());
        self
    }

    pub fn query(&mut self, params: JsonValue) -> &mut Self {
        let mut txt = vec![];
        for (key, value) in params.entries() {
            txt.push(format!("{key}={value}"));
        }
        if self.url.contains('?') {
            if !txt.is_empty() {
                self.url = format!("{}&{}", self.url, txt.join("&"));
            }
        } else if !txt.is_empty() {
            self.url = format!("{}?{}", self.url, txt.join("&"));
        }
        self
    }
    /// 设置连接超时
    pub fn set_request_timeout(&mut self, s: usize) -> &mut Self {
        self.request_timeout = s;
        self
    }

    pub fn set_response_timeout(&mut self, s: usize) -> &mut Self {
        self.response_timeout = s;
        self
    }

    pub fn set_retry(&mut self, count: usize) -> &mut Self {
        self.retry = count;
        self
    }

    /// JSON请求
    pub fn raw_json(&mut self, data: JsonValue) -> &mut Self {
        self.header("Content-Type", ContentType::Json.str().as_str());
        self.content_type = ContentType::Json;
        self.body_data = data;
        self
    }
    pub fn raw_xml(&mut self, data: JsonValue) -> &mut Self {
        self.header("Content-Type", ContentType::Xml.str().as_str());
        self.content_type = ContentType::Xml;
        self.body_data = data;
        self
    }
    pub fn raw_stream(&mut self, filename: &str) -> &mut Self {
        self.content_type = ContentType::Stream;
        self.body_data = filename.into();
        self
    }
    pub fn raw_binary_str(&mut self, text: &str) -> &mut Self {
        self.content_type = ContentType::BinaryStr;
        self.body_data = text.into();
        self
    }
    pub fn raw_stream_urlencode(&mut self, text: &str) -> &mut Self {
        self.content_type = ContentType::StreamUrlencode;
        self.body_data = text.into();
        self
    }
    /// FormData请求
    pub fn form_data(&mut self, data: JsonValue) -> &mut Self {
        self.header("Content-Type", ContentType::FormData.str().as_str());
        self.content_type = ContentType::FormData;
        self.body_data = data;
        self
    }
    /// FormData请求
    pub fn form_urlencoded(&mut self, data: JsonValue) -> &mut Self {
        self.header("Content-Type", ContentType::FormUrlencoded.str().as_str());
        self.content_type = ContentType::FormUrlencoded;
        self.body_data = data;
        self
    }
    /// 开启旧的ssl
    pub fn set_cert_p12(&mut self, filename: &str, password: &str) -> &mut Self {
        self.cert_p12_filename = filename.to_string();
        self.cert_p12_password = password.to_string();
        self.cert_p12 = true;
        self
    }
    /// 指定代理
    // curl -x socks5://proxy.example.com:1080 http://example.com
    pub fn add_proxy(&mut self, proxy_addr: String) {
        self.proxy = ProxyInfo::Addr(proxy_addr);
    }

    /// 指定代理, 该代理需要用户名和密码认证
    // curl -x http://proxy-server:port -U username:password http://example.com
    pub fn add_proxy_with_account(&mut self, proxy_addr: String, user: String, password: String) {
        self.proxy = ProxyInfo::AddrWithAccount(proxy_addr, user, password);
    }

    pub fn send(&mut self) -> Result<Response, String> {
        let mut output = Command::new("curl");

        if self.cert_p12 {
            self.args.extend(["--cert-type".to_string(), "P12".to_string(), "--cert".to_string(), self.cert_p12_filename.to_string(), "--pass".to_string(), self.cert_p12_password.to_string()]);
        }

        if self.debug {
            self.args.extend(["--trace-ascii".to_string(), "br-reqwest.log".to_string()]);
        }

        if self.request_timeout > 0 {
            self.args.extend(["--connect-timeout".to_string(), self.request_timeout.to_string()]);
        }
        if self.response_timeout > 0 {
            self.args.extend(["--max-time".to_string(), self.response_timeout.to_string()]);
        }

        if self.retry > 0 {
            self.args.extend(["--retry".to_string(), self.retry.to_string()]);
        }

        match &self.proxy {
            ProxyInfo::None => {
                self.args.extend(["--noproxy".to_string(), "*".to_string()]);
            }
            ProxyInfo::Addr(proxy) => {
                self.args.extend(["-x".to_string(), proxy.to_string()]);
            }
            ProxyInfo::AddrWithAccount(proxy, user, pwd) => {
                self.args.extend(["-x".to_string(), proxy.to_string(), "-U".to_string(), format!("{user}:{pwd}")]);
            }
        }

        match self.method {
            Method::HEAD => {
                self.args.push("-I".to_string());
            }
            _ => {
                self.args.extend(["-i".to_string(), "-X".to_string(), self.method.to_str().to_uppercase()]);
            }
        }
        self.args.push(self.url.to_string());

        if !self.header.is_empty() {
            for (key, value) in self.header.iter() {
                self.args.extend(["-H".to_string(), format!("'{key}: {value}'")]);
            }
        }

        match self.content_type {
            ContentType::FormData => {
                for (_, value) in self.body_data.entries() {
                    self.args.push("-F".to_string());

                    if value[2].is_empty() {
                        self.args.push(format!("{}={}", value[0], value[1]));
                    } else {
                        self.args.push(format!("{}={};{}", value[0], value[1], value[2]));
                    }
                }
            }
            ContentType::FormUrlencoded => {
                self.args.push("-d".to_string());
                let mut d = vec![];
                for (key, value) in self.body_data.entries() {
                    d.push(format!("{key}={value}"))
                }
                self.args.push(d.join("&").to_string());
            }
            ContentType::Json | ContentType::Xml => {
                self.args.extend(["-d".to_string(), format!("{}", self.body_data)]);
            }
            ContentType::Javascript => {}
            ContentType::Text => {}
            ContentType::Html => {}
            ContentType::Other(_) => {}
            ContentType::Stream => {
                // 文件路径
                self.args.push("--data-binary".to_string());
                self.args.push(format!("@{}", self.body_data));
            }
            ContentType::BinaryStr => {
                self.args.push("--data-binary".to_string());
                self.args.push(format!("{}", self.body_data));
            }
            ContentType::StreamUrlencode => {
                self.args.push("--data-urlencode".to_string());
                self.args.push(format!("{}", self.body_data));
            }
        }
        output.args(self.args.as_slice());
        if self.debug {
            info!("curl {}",self.args.join(" "));
        }
        let req = match output.output() {
            Ok(e) => e,
            Err(e) => {
                return Err(e.to_string());
            }
        };

        if req.status.success() {
            let body = req.stdout.clone();
            if self.debug {
                let text = String::from_utf8_lossy(&req.stdout);
                info!("响应内容:\n{text}");
            }
            Ok(Response::new(self.debug, body)?)
        } else {
            let err = String::from_utf8_lossy(&req.stderr).to_string();
            let txt = match err.find("ms:") {
                None => err,
                Some(e) => {
                    err[e + 3..].trim().to_string()
                }
            };
            Err(txt)
        }
    }
}

enum ProxyInfo {
    None,
    Addr(String),
    AddrWithAccount(String, String, String),
}


#[derive(Debug)]
pub struct Response {
    debug: bool,
    status: i32,
    method: Method,
    headers: JsonValue,
    cookies: JsonValue,
    /// 请求体
    body: Body,
}
impl Response {
    const CRLF2: [u8; 4] = [13, 10, 13, 10];

    fn new(debug: bool, body: Vec<u8>) -> Result<Self, String> {
        let mut that = Self {
            debug,
            status: 0,
            method: Method::NONE,
            headers: object! {},
            cookies: object! {},
            body: Default::default(),
        };

        let (header, body) = match body.windows(Self::CRLF2.len()).position(|window| window == Self::CRLF2) {
            None => (vec![], vec![]),
            Some(index) => {
                let header = body[..index].to_vec();
                let body = Vec::from(&body[index + Self::CRLF2.len()..]);
                (header, body)
            }
        };

        let text = String::from_utf8_lossy(header.as_slice());
        let lines = text.lines();

        that.get_request_line(lines.clone().next().expect("get next line in response err"))?;
        // Header处理
        that.get_header(lines.clone())?;
        that.body.stream = body.clone();
        that.body.set_content(body);
        Ok(that)
    }

    /// 获取请求行信息
    fn get_request_line(&mut self, line: &str) -> Result<(), String> {
        let lines = line.split_whitespace().collect::<Vec<&str>>();
        if lines.len() < 2 {
            return Err("请求行错误".to_string());
        }
        self.method = Method::from(lines[0]);
        self.status = lines[1].parse::<i32>().unwrap();
        Ok(())
    }

    /// Header处理
    fn get_header(&mut self, data: Lines) -> Result<(), String> {
        let mut header = object! {};
        let mut cookie = object! {};
        let mut body = Body::default();

        for text in data {
            let (key, value) = match text.trim().find(":") {
                None => continue,
                Some(e) => {
                    let key = text[..e].trim().to_lowercase().clone();
                    let value = text[e + 1..].trim().to_string();
                    (key, value)
                }
            };
            match key.as_str() {
                "content-type" => match value {
                    _ if value.contains("multipart/form-data") => {
                        let boundarys = value.split("boundary=").collect::<Vec<&str>>();
                        body.boundary = boundarys[1..].join("");
                        body.content_type = ContentType::from("multipart/form-data");
                        let _ = header.insert(key.as_str(), "multipart/form-data");
                    }
                    _ => {
                        let value = match value.find(";") {
                            None => value,
                            Some(e) => value[..e].trim().to_string(),
                        };
                        body.content_type = ContentType::from(value.as_str());
                        let _ = header.insert(key.as_str(), body.content_type.str());
                    }
                },
                "content-length" => {
                    body.content_length = value.to_string().parse::<usize>().unwrap_or(0);
                }
                "cookie" => {
                    let _ = value.split(";").collect::<Vec<&str>>().iter().map(|&x| {
                        match x.find("=") {
                            None => {}
                            Some(index) => {
                                let key = x[..index].trim().to_string();
                                let val = x[index + 1..].trim().to_string();
                                let _ = cookie.insert(key.as_str(), val);
                            }
                        };
                        ""
                    }).collect::<Vec<&str>>();
                }
                _ => {
                    if self.debug {
                        debug!("header: {key} = {value}");
                    }
                    let _ = header.insert(key.as_str(), value);
                }
            };
        }
        self.headers = header.clone();
        self.cookies = cookie.clone();
        self.body = body.clone();
        Ok(())
    }
    pub fn status(&self) -> i32 {
        self.status
    }
    pub fn headers(&self) -> JsonValue {
        self.headers.clone()
    }
    pub fn cookies(&self) -> JsonValue {
        self.cookies.clone()
    }
    pub fn content_type(&self) -> String {
        self.body.content_type.str().clone()
    }
    pub fn json(&self) -> Result<JsonValue, String> {
        if self.body.content.is_empty() {
            Ok(object! {})
        } else {
            match json::parse(self.body.content.to_string().as_str()) {
                Ok(e) => Ok(e),
                Err(e) => Err(e.to_string())
            }
        }
    }
    pub fn xml(&self) -> Result<JsonValue, String> {
        if self.body.content.is_empty() {
            Ok(object! {})
        } else {
            let json = match Element::parse(self.body.content.to_string().as_bytes()) {
                Ok(e) => xml_element_to_json(&e),
                Err(e) => {
                    if self.debug {
                        error!("{:?} {}", e.to_string(),self.body.content.clone());
                    }
                    self.body.content.clone()
                }
            };
            Ok(json)
        }
    }
    pub fn body(&self) -> JsonValue {
        self.body.content.clone()
    }
    pub fn stream(&self) -> Vec<u8> {
        self.body.stream.clone()
    }
}
fn xml_element_to_json(elem: &Element) -> JsonValue {
    let mut obj = object! {};

    for child in &elem.children {
        if let xmltree::XMLNode::Element(e) = child {
            obj[e.name.clone()] = xml_element_to_json(e);
        }
    }

    match elem.get_text() {
        None => obj,
        Some(text) => JsonValue::from(text.to_string()),
    }
}

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

impl Method {
    pub fn to_str(&self) -> String {
        match self {
            Method::GET => "GET",
            Method::POST => "POST",
            Method::OPTIONS => "OPTIONS",
            Method::PATCH => "PATCH",
            Method::HEAD => "HEAD",
            Method::DELETE => "DELETE",
            Method::TRACE => "TRACE",
            Method::PUT => "PUT",
            Method::NONE => "NONE",
        }.to_string()
    }
    pub fn from(name: &str) -> Self {
        match name.to_lowercase().as_str() {
            "post" => Self::POST,
            "get" => Self::GET,
            "head" => Self::HEAD,
            "put" => Self::PUT,
            "delete" => Self::DELETE,
            "options" => Self::OPTIONS,
            "patch" => Self::PATCH,
            "trace" => Self::TRACE,
            _ => Self::NONE,
        }
    }
}
#[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 FormData {
    /// KEY
    /// value
    /// 文本类型
    Text(String, JsonValue, String),
    /// 文件名
    /// 文件路径
    /// 文件类型
    File(String, String, String),
    None,
}


#[derive(Debug, Clone)]
pub struct Body {
    pub content_type: ContentType,
    pub boundary: String,
    pub content_length: usize,
    pub content: JsonValue,
    pub stream: Vec<u8>,
}
impl Body {
    /// 解码
    pub fn decode(input: &str) -> Result<String, String> {
        let mut decoded = String::new();
        let bytes = input.as_bytes();
        let mut i = 0;

        while i < bytes.len() {
            if bytes[i] == b'%' {
                if i + 2 >= bytes.len() {
                    return Err("Incomplete percent-encoding".into());
                }
                let hex = &input[i + 1..i + 3];
                match u8::from_str_radix(hex, 16) {
                    Ok(byte) => decoded.push(byte as char),
                    Err(_) => return Err(format!("Invalid percent-encoding: %{hex}")),
                }
                i += 3;
            } else if bytes[i] == b'+' {
                decoded.push(' ');
                i += 1;
            } else {
                decoded.push(bytes[i] as char);
                i += 1;
            }
        }

        Ok(decoded)
    }
    pub fn set_content(&mut self, data: Vec<u8>) {
        match self.content_type.clone() {
            ContentType::FormData | ContentType::Stream => {
                let mut fields = object! {};
                let boundary_marker = format!("--{}", self.boundary);
                let text = unsafe { String::from_utf8_unchecked(data) };
                let parts = text.split(&boundary_marker).collect::<Vec<&str>>();
                for part in parts {
                    let part = part.trim();
                    if part.is_empty() || part == "--" {
                        continue; // 跳过无效部分
                    }

                    let mut headers_and_body = part.splitn(2, "\r\n\r\n");
                    if let (Some(headers), Some(body)) = (headers_and_body.next(), headers_and_body.next())
                    {
                        // 解析头部，查找 Content-Disposition
                        let headers = headers.split("\r\n");

                        let mut field_name = "";
                        let mut filename = "";
                        let mut content_type = ContentType::Text;

                        for header in headers {
                            if header.to_lowercase().starts_with("content-disposition:") {
                                match header.find("filename=\"") {
                                    None => {}
                                    Some(filename_start) => {
                                        let filename_len = filename_start + 10;
                                        let filename_end = header[filename_len..].find('"').unwrap() + filename_len;
                                        filename = &header[filename_len..filename_end];
                                    }
                                }

                                match header.find("name=\"") {
                                    None => {}
                                    Some(name_start) => {
                                        let name_start = name_start + 6;
                                        let name_end = header[name_start..].find('"').unwrap() + name_start;
                                        field_name = &header[name_start..name_end];
                                    }
                                }
                            }
                            if header.to_lowercase().starts_with("content-type:") {
                                content_type = ContentType::from(
                                    header.to_lowercase().trim_start_matches("content-type:").trim(),
                                );
                            }
                        }

                        if filename.is_empty() {
                            fields[field_name.to_string()] = JsonValue::from(body);
                        } else {
                            // 获取系统临时目录
                            let mut temp_dir = env::temp_dir();
                            // 构造临时文件的完整路径
                            temp_dir.push(filename);
                            // 打开（创建）临时文件
                            let mut temp_file = match fs::File::create(&temp_dir) {
                                Ok(e) => e,
                                Err(_) => continue,
                            };
                            if temp_file.write(body.as_bytes()).is_ok() {
                                if fields[field_name.to_string()].is_empty() {
                                    fields[field_name.to_string()] = array![]
                                }

                                let extension = Path::new(filename).extension() // 可能返回 None
                                                                   .and_then(|ext| ext.to_str()); // 转换为 &str

                                let suffix = extension.unwrap_or("txt");

                                fields[field_name.to_string()].push(object! {
                                        //id:sha_256(body.as_bytes().to_vec()),
                                        name:filename,
                                        suffix:suffix,
                                        size:body.len(),
                                        "type":content_type.str(),
                                        file:temp_dir.to_str()
                                    }).unwrap();
                            };
                        }
                    }
                }
                self.content = fields;
            }
            ContentType::FormUrlencoded => {
                let text = unsafe { String::from_utf8_unchecked(data) };
                let params = text.split("&").collect::<Vec<&str>>();
                let mut list = object! {};
                for param in params.iter() {
                    let t = param.split("=").collect::<Vec<&str>>().iter().map(|&x| Body::decode(x).unwrap_or(x.to_string())).collect::<Vec<String>>();
                    list[t[0].to_string()] = t[1].clone().into();
                }
                self.content = list;
            }
            ContentType::Json => {
                let text = unsafe { String::from_utf8_unchecked(data) };
                self.content = json::parse(text.as_str()).unwrap_or(object! {});
            }
            ContentType::Xml => {
                let text = unsafe { String::from_utf8_unchecked(data) };
                self.content = text.into();
            }
            ContentType::Html | ContentType::Text | ContentType::Javascript => {
                let text = unsafe { String::from_utf8_unchecked(data) };
                self.content = text.into();
            }
            ContentType::Other(_) => self.content = data.into(),
            ContentType::BinaryStr => todo!(),
            ContentType::StreamUrlencode => todo!()
        }
    }
}

impl Default for Body {
    fn default() -> Self {
        Self {
            content_type: ContentType::Other("text/plain".to_string()),
            boundary: "".to_string(),
            content_length: 0,
            content: object! {},
            stream: vec![],
        }
    }
}

/// 内容类型
#[derive(Debug, Clone)]
pub enum ContentType {
    FormData,
    FormUrlencoded,
    Json,
    Xml,
    Javascript,
    Text,
    Html,
    Stream,
    BinaryStr,
    StreamUrlencode,
    Other(String),
}
impl ContentType {
    pub fn from(name: &str) -> Self {
        match name {
            "multipart/form-data" => Self::FormData,
            "application/x-www-form-urlencoded" => Self::FormUrlencoded,
            "application/json" => Self::Json,
            "application/xml" | "text/xml" => Self::Xml,
            "application/javascript" => Self::Javascript,
            "text/html" => Self::Html,
            "text/plain" => Self::Text,
            "application/octet-stream" => Self::Stream,
            _ => Self::Other(name.to_string()),
        }
    }
    pub fn str(&self) -> String {
        match self {
            Self::FormData => "multipart/form-data",
            Self::FormUrlencoded => "application/x-www-form-urlencoded",
            Self::Json => "application/json",
            Self::Xml => "application/xml",
            Self::Javascript => "application/javascript",
            Self::Text => "text/plain",
            Self::Html => "text/html",
            Self::Other(name) => name,
            Self::Stream => "application/octet-stream",
            Self::BinaryStr => "application/octet-stream",
            ContentType::StreamUrlencode => "application/x-www-form-urlencoded",
        }.to_string()
    }
}