use base64::Engine;
use base64::engine::general_purpose;
use chrono::Local;
use json::{object, JsonValue};
use log::{debug, error, info, warn};
use xmltree::Element;
use crate::{PayMode, PayNotify, RefundNotify, RefundStatus, TradeState, TradeType, Types};
use cipher::{BlockEncryptMut, KeyInit};
use cipher::block_padding::Pkcs7;
use des::Des;
use rand::{rng, Rng};


/// 建设银行
#[derive(Clone, Debug)]
pub struct Ccbc {
    /// 调试
    pub debug: bool,
    /// 微信小程序APPID
    pub appid: String,
    /// 微信公众号APPID
    pub appid_subscribe: String,
    /// 服务商号
    pub sp_mchid: String,
    /// 服务商账号ID
    pub sp_user_id: String,
    /// 服务商登录密码
    pub sp_pass: String,
    /// 通知地址
    /// 服务商柜台代码
    pub sp_posid: String,
    pub notify_url: String,
    /// 商户商柜台代码
    pub sub_posid: String,
    /// 分行代码
    pub branchid: String,
    /// 二级商户公钥
    pub public_key: String,
    pub client_ip: String,
    /// 重试次
    pub retry: usize,
}

impl Ccbc {
    pub fn http(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
        let mut mac = vec![];
        let mut path = vec![];
        let fields = ["MAC"];
        for (key, value) in body.entries() {
            if value.is_empty() && fields.contains(&key) {
                continue;
            }
            if key != "PUB" {
                path.push(format!("{key}={value}"));
            }
            mac.push(format!("{key}={value}"));
        }


        let mac_text = mac.join("&");
        let path = path.join("&");
        if self.debug {
            debug!("MERCHANTID=105000373721227&POSID=091864103&BRANCHID=530000000&ORDERID=96398&PAYMENT=0.01&CURCODE=01&TXCODE=530550&REMARK1=&REMARK2=&RETURNTYPE=3&TIMEOUT=&PUB=93bd9affe92c29dea12e5d79020111");
            debug!("{mac_text:#}");
        }
        body["MAC"] = br_crypto::md5::encrypt_hex(mac_text.as_bytes()).into();
        if self.debug {
            debug!("{body:#}");
        }
        body.remove("PUB");
        let mac = format!("{}&MAC={}", path, body["MAC"]);
        if self.debug {
            debug!("{mac:#}");
        }
        let urls = format!("{url}&{mac}");
        if self.debug {
            debug!("{urls:#}");
        }
        let mut http = br_reqwest::Client::new();

        let res = http.post(urls.as_str()).raw_json(body.clone()).set_retry(3).send()?;
        let res = res.body().to_string();
        match json::parse(&res) {
            Ok(e) => Ok(e),
            Err(e) => Err(e.to_string())
        }
    }
    pub fn http_alipay(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
        let mut mac = vec![];
        let mut path = vec![];
        let fields = ["MAC", "SUBJECT", "AREA_INFO"];
        for (key, value) in body.entries() {
            if value.is_empty() && fields.contains(&key) {
                continue;
            }
            if fields.contains(&key) {
                continue;
            }
            if key != "PUB" {
                path.push(format!("{key}={value}"));
            }
            mac.push(format!("{key}={value}"));
        }


        let mac_text = mac.join("&");
        let path = path.join("&");
        body["MAC"] = br_crypto::md5::encrypt_hex(mac_text.as_bytes()).into();
        body.remove("PUB");
        let mac = format!("{}&MAC={}", path, body["MAC"]);

        let urls = format!("{url}&{mac}");

        let mut http = br_reqwest::Client::new();
        let res = match http.post(urls.as_str()).raw_json(body.clone()).send() {
            Ok(e) => e,
            Err(e) => {
                if self.retry > 2 {
                    return Err(e.to_string());
                }
                self.retry += 1;
                warn!("建行接口重试: {}", self.retry);
                body.remove("MAC");
                let res = self.http_alipay(url, body.clone())?;
                return Ok(res);
            }
        };
        let res = res.body().to_string();
        match json::parse(&res) {
            Ok(e) => Ok(e),
            Err(_) => Err(res)
        }
    }
    fn escape_unicode(&mut self, s: &str) -> String {
        s.chars().map(|c| {
            if c.is_ascii() {
                c.to_string()
            } else {
                format!("%u{:04X}", c as u32)
            }
        }).collect::<String>()
    }
    fn _unescape_unicode(&mut self, s: &str) -> String {
        let mut output = String::new();
        let mut chars = s.chars().peekable();
        while let Some(c) = chars.next() {
            if c == '%' && chars.peek() == Some(&'u') {
                chars.next(); // consume 'u'
                let codepoint: String = chars.by_ref().take(4).collect();
                if let Ok(value) = u32::from_str_radix(&codepoint, 16) {
                    if let Some(ch) = std::char::from_u32(value) {
                        output.push(ch);
                    }
                }
            } else {
                output.push(c);
            }
        }
        output
    }
    pub fn http_q(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
        let mut path = vec![];
        let fields = ["MAC"];
        for (key, value) in body.entries() {
            if value.is_empty() && fields.contains(&key) {
                continue;
            }
            if key.contains("QUPWD") {
                path.push(format!("{key}="));
                continue;
            }
            path.push(format!("{key}={value}"));
        }

        let mac = path.join("&");
        body["MAC"] = br_crypto::md5::encrypt_hex(mac.as_bytes()).into();

        let mut map = vec![];
        for (key, value) in body.entries() {
            map.push((key, value.to_string()));
        }

        let mut http = br_reqwest::Client::new();

        http.header("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36");

        let res = match http.post(url).form_urlencoded(body.clone()).send() {
            Ok(d) => d,
            Err(e) => {
                if self.retry > 2 {
                    return Err(e.to_string());
                }
                self.retry += 1;
                warn!("建行查询接口重试: {}", self.retry);
                body.remove("MAC");
                let res = self.http_q(url, body.clone())?;
                return Ok(res);
            }
        };
        let res = res.body().to_string().trim().to_string();
        match Element::parse(res.as_bytes()) {
            Ok(e) => Ok(xml_element_to_json(&e)),
            Err(e) => Err(e.to_string())
        }
    }

    pub fn http_ccb_param(&mut self, url: &str, mut body: JsonValue) -> Result<JsonValue, String> {
        let mer_info = format!("MERCHANTID={}&POSID={}&BRANCHID={}", body["MERCHANTID"], body["POSID"], body["BRANCHID"]);
        let ccb_param = self.make_ccb_param(body.clone())?;
        let urls = format!("{url}?{mer_info}&ccbParam={ccb_param}");
        let mut http = br_reqwest::Client::new();
        let res = match http.post(urls.as_str()).raw_json(body.clone()).send() {
            Ok(e) => e,
            Err(e) => {
                if self.retry > 2 {
                    return Err(e.to_string());
                }
                self.retry += 1;
                warn!("建行接口重试: {}", self.retry);
                body.remove("MAC");
                let res = self.http_ccb_param(url, body.clone())?;
                return Ok(res);
            }
        };
        let res = res.body().to_string();
        match json::parse(&res) {
            Ok(e) => Ok(e),
            Err(_) => Err(res)
        }
    }

    pub fn make_ccb_param(&mut self, body: JsonValue) -> Result<String, String> {
        if self.public_key.is_empty() || self.public_key.len() < 30 {
            return Err(String::from("Public key is empty"));
        }
        let pubkey = self.public_key[self.public_key.len() - 30..].to_string();
        if pubkey.len() < 8 {
            return Err(String::from("Public key len 8"));
        }
        let pubkey = &pubkey[..8];
        let mut mac = vec![];
        let mut arr = vec![];
        let mut params = vec![];
        for (key, value) in body.entries() {
            arr.push(key);
            params.push(format!("{key}={value}"));
        }
        arr.sort();
        for key in arr.iter() {
            if body.has_key(key) && !body[key.to_string()].is_empty() {
                mac.push(format!("{key}={}", body[key.to_string()]));
            }
        }
        let ccb_param = mac.join("&");
        let ccb_param = format!("{}20120315201809041004", ccb_param);
        let ccb_param = br_crypto::md5::encrypt_hex(ccb_param.as_bytes());
        let params = params.join("&");
        let params = format!("{params}&SIGN={ccb_param}");
        let bytes = self.utf16_bytes(params.as_str());
        let encrypt = self.des_ecb_pkcs5_base64(&bytes, pubkey)?;
        let encrypt = encrypt.replace("+", ",");
        let url = br_crypto::encoding::urlencoding_encode(encrypt.as_str());
        Ok(url)
    }
    fn utf16_bytes(&mut self, s: &str) -> Vec<u8> {
        let mut out = Vec::with_capacity(2 + s.len() * 2);
        out.push(0xFE);
        out.push(0xFF);
        for u in s.encode_utf16() {
            out.push((u >> 8) as u8);
            out.push((u & 0xFF) as u8);
        }
        out
    }
    fn des_ecb_pkcs5_base64(&mut self, plain: &[u8], key_str: &str) -> Result<String, &'static str> {
        let kb = key_str.as_bytes();
        if kb.len() < 8 {
            return Err("DES key must be at least 8 bytes");
        }
        let mut key = [0u8; 8];
        key.copy_from_slice(&kb[..8]);

        let cipher = ecb::Encryptor::<Des>::new(&key.into());

        // 预留 1 块给 PKCS#5/7 填充
        let orig_len = plain.len();
        let mut buf = vec![0u8; orig_len + 8];
        buf[..orig_len].copy_from_slice(plain);

        let ct = cipher.encrypt_padded_mut::<Pkcs7>(&mut buf, orig_len).map_err(|_| "encrypt error")?;

        Ok(general_purpose::STANDARD.encode(ct))
    }
    fn https_cert(&mut self, text: &str) -> Result<JsonValue, String> {
        let text = br_crypto::encoding::urlencoding_encode(text);
        let mut http = br_reqwest::Client::new();
        http.header("Connection", "close");
        http.post("http://127.0.0.1:12345").form_urlencoded(object! {
            requestXml:text
        });
        let res = http.send()?;
        let ress = br_crypto::encoding::gb18030_to_utf8(res.stream())?;
        let ress = ress.replace(r#"encoding="GB18030""#, r#"encoding="UTF-8""#);
        match Element::parse(ress.trim().as_bytes()) {
            Ok(e) => {
                let json = xml_element_to_json(&e);
                Ok(json)
            }
            Err(e) => Err(e.to_string())
        }
    }

    fn get_xml_text(&mut self, name: &str) -> String {
        let res = match name {
            "5W1002.xml" => r#"<?xml version="1.0" encoding="GB2312" standalone="yes" ?>
<TX>
    <REQUEST_SN>{{REQUEST_SN}}</REQUEST_SN>
    <CUST_ID>{{CUST_ID}}</CUST_ID>
    <USER_ID>{{USER_ID}}</USER_ID>
    <PASSWORD>{{PASSWORD}}</PASSWORD>
    <TX_CODE>5W1002</TX_CODE>
    <LANGUAGE>CN</LANGUAGE>
    <TX_INFO>
        <START>{{START}}</START>
        <STARTHOUR>00</STARTHOUR>
        <STARTMIN>00</STARTMIN>
        <END>{{END}}</END>
        <ENDHOUR>00</ENDHOUR>
        <ENDMIN>00</ENDMIN>
        <KIND>{{KIND}}</KIND>
        <ORDER>{{ORDER}}</ORDER>
        <ACCOUNT></ACCOUNT>
        <DEXCEL>1</DEXCEL>
        <MONEY></MONEY>
        <NORDERBY>2</NORDERBY>
        <PAGE>1</PAGE>
        <POS_CODE>{{POS_CODE}}</POS_CODE>
        <STATUS>{{STATUS}}</STATUS>
        <Mrch_No>{{Mrch_No}}</Mrch_No>
        <TXN_TPCD></TXN_TPCD>
    </TX_INFO>
</TX>"#,
            "5W1003.xml" => r#"<?xml version="1.0" encoding="GB2312" standalone="yes" ?>
<TX>
    <REQUEST_SN>{{REQUEST_SN}}</REQUEST_SN>
    <CUST_ID>{{CUST_ID}}</CUST_ID>
    <USER_ID>{{USER_ID}}</USER_ID>
    <PASSWORD>{{PASSWORD}}</PASSWORD>
    <TX_CODE>5W1003</TX_CODE>
    <LANGUAGE>CN</LANGUAGE>
    <TX_INFO>
        <START>{{START}}</START>
        <STARTHOUR>00</STARTHOUR>
        <STARTMIN>00</STARTMIN>
        <END>{{END}}</END>
        <ENDHOUR>00</ENDHOUR>
        <ENDMIN>00</ENDMIN>
        <KIND>{{KIND}}</KIND>
        <ORDER>{{ORDER}}</ORDER>
        <ACCOUNT></ACCOUNT>
        <MONEY></MONEY>
        <NORDERBY>2</NORDERBY>
        <PAGE>1</PAGE>
        <POS_CODE>{{POS_CODE}}</POS_CODE>
        <STATUS>{{STATUS}}</STATUS>
        <Mrch_No>{{Mrch_No}}</Mrch_No>
        <TXN_TPCD></TXN_TPCD>
    </TX_INFO>
</TX>"#,
            "5W1024.xml" => r#"<?xml version="1.0" encoding="GB2312" standalone="yes" ?>
<TX>
    <REQUEST_SN>{{REQUEST_SN}}</REQUEST_SN>
    <CUST_ID>{{CUST_ID}}</CUST_ID>
    <USER_ID>{{USER_ID}}</USER_ID>
    <PASSWORD>{{PASSWORD}}</PASSWORD>
    <TX_CODE>5W1024</TX_CODE>
    <LANGUAGE>CN</LANGUAGE>
    <TX_INFO>
        <MONEY>{{MONEY}}</MONEY>
        <ORDER>{{ORDER}}</ORDER>
        <REFUND_CODE>{{REFUND_CODE}}</REFUND_CODE>
        <Mrch_No>{{Mrch_No}}</Mrch_No>
    </TX_INFO>
    <SIGN_INFO></SIGN_INFO>
    <SIGNCERT></SIGNCERT>
</TX>"#,
            _ => r#""#
        };

        let xml_text = res.as_bytes().to_vec();
        let mut text = unsafe { String::from_utf8_unchecked(xml_text) };
        let mut rng = rng();
        let num: String = (0..14).map(|_| rng.random_range(0..10).to_string()).collect();
        text = text.replace("{{REQUEST_SN}}", num.as_str());
        text = text.replace("{{CUST_ID}}", self.sp_mchid.as_str());
        text = text.replace("{{USER_ID}}", self.sp_user_id.as_str());
        text = text.replace("{{PASSWORD}}", self.sp_pass.as_str());
        text
    }
}
impl PayMode for Ccbc {
    fn check(&mut self) -> Result<bool, String> {
        todo!()
    }

    fn get_sub_mchid(&mut self, _sub_mchid: &str) -> Result<JsonValue, String> {
        todo!()
    }

    fn config(&mut self) -> JsonValue {
        todo!()
    }


    fn pay(&mut self, channel: &str, types: Types, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, sp_openid: &str) -> Result<JsonValue, String> {
        if self.public_key.is_empty() || self.public_key.len() < 30 {
            return Err(String::from("Public key is empty"));
        }
        let pubtext = self.public_key[self.public_key.len() - 30..].to_string();

        let url = match channel {
            "wechat" => "https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6",
            "alipay" => "https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain?CCB_IBSVersion=V6",
            _ => return Err(format!("Invalid channel: {channel}")),
        };
        let body = match channel {
            "wechat" => {
                let mut body = object! {
                    MERCHANTID:sub_mchid,
                    POSID:self.sub_posid.clone(),
                    BRANCHID:self.branchid.clone(),
                    ORDERID:out_trade_no,
                    PAYMENT:total_fee,
                    CURCODE:"01",
                    TXCODE:"530590",
                    REMARK1:"",
                    REMARK2:"",
                    TYPE:"1",
                    PUB:pubtext,
                    GATEWAY:"0",
                    CLIENTIP:self.client_ip.clone(),
                    REGINFO:"",
                    PROINFO: self.escape_unicode(description),
                    REFERER:"",
                    TRADE_TYPE:"",
                    SUB_APPID: "",
                    SUB_OPENID:sp_openid,
                    MAC:"",
                };
                body["TRADE_TYPE"] = match types {
                    Types::Jsapi => {
                        body["SUB_APPID"] = self.appid_subscribe.clone().into();
                        "JSAPI"
                    }
                    Types::MiniJsapi => {
                        body["SUB_APPID"] = self.appid.clone().into();
                        "MINIPRO"
                    }
                    _ => return Err(format!("Invalid types: {types:?}")),
                }.into();
                body
            }
            "alipay" => {
                let body = match types {
                    Types::MiniJsapi => object! {
                        MERCHANTID:sub_mchid,
                        POSID:self.sub_posid.clone(),
                        BRANCHID:self.branchid.clone(),
                        ORDERID:out_trade_no,
                        PAYMENT:total_fee,
                        CURCODE:"01",
                        TXCODE:"530591",
                        TRADE_TYPE:"JSAPI",
                        USERID:sp_openid,
                        PUB:pubtext,
                        MAC:""
                    },
                    Types::H5 => object! {
                        BRANCHID:self.branchid.clone(),
                        MERCHANTID:sub_mchid,
                        POSID:self.sub_posid.clone(),
                        TXCODE:"ZFBWAP",
                        ORDERID:out_trade_no,
                        AMOUNT:total_fee,
                        TIMEOUT:"",
                        REMARK1:"",
                        REMARK2:"",
                        PUB:pubtext,
                        MAC:"",
                        SUBJECT:description,
                        AREA_INFO:""
                    },
                    Types::Jsapi => object! {
                        MERCHANTID:sub_mchid,
                        POSID:self.sub_posid.clone(),
                        BRANCHID:self.branchid.clone(),
                        ORDERID:out_trade_no,
                        PAYMENT:total_fee,
                        CURCODE:"01",
                        TXCODE:"530550",
                        REMARK1:"",
                        REMARK2:"",
                        RETURNTYPE:"3",
                        TIMEOUT:"",
                        PUB:pubtext,
                        MAC:""
                    },
                    _ => return Err(format!("Invalid types: {types:?}")),
                };
                body
            }
            _ => return Err(format!("Invalid channel: {channel}")),
        };
        match (channel, types) {
            ("wechat", Types::Jsapi | Types::MiniJsapi) => {
                let res = self.http(url, body.clone())?;
                if res.has_key("PAYURL") {
                    let url = res["PAYURL"].to_string();
                    let mut http = br_reqwest::Client::new();

                    let re = match http.post(url.as_str()).send() {
                        Ok(e) => e,
                        Err(e) => {
                            return Err(e.to_string());
                        }
                    };
                    let re = re.body().to_string();
                    let res = match json::parse(&re) {
                        Ok(e) => e,
                        Err(_) => return Err(re)
                    };
                    if res.has_key("ERRCODE") && res["ERRCODE"] != "000000" {
                        return Err(format!("获取支付参数: 错误码: [{}] {} 失败", res["ERRCODE"], res["ERRMSG"]));
                    }
                    Ok(res)
                } else {
                    Err(res.to_string())
                }
            }
            ("alipay", Types::MiniJsapi) => {
                let res = self.http(url, body)?;
                if res.has_key("PAYURL") {
                    let url = res["PAYURL"].to_string();
                    let mut http = br_reqwest::Client::new();
                    let re = match http.post(url.as_str()).send() {
                        Ok(e) => e,
                        Err(e) => {
                            return Err(e.to_string());
                        }
                    };
                    let re = re.body().to_string();
                    let res = match json::parse(&re) {
                        Ok(e) => e,
                        Err(_) => return Err(re)
                    };
                    if res.has_key("ERRCODE") && res["ERRCODE"] != "000000" {
                        return Err(format!("获取支付参数: 错误码: [{}] {} 失败", res["ERRCODE"], res["ERRMSG"]));
                    }
                    Ok(res["jsapi"].clone())
                } else {
                    Err(res.to_string())
                }
            }
            ("alipay", Types::H5) => {
                let res = self.http_alipay(url, body)?;
                if res.has_key("ERRCODE") && res["ERRCODE"] != "000000" {
                    return Err(format!("获取支付参数: 错误码: [{}] {} 失败", res["ERRCODE"], res["ERRMSG"]));
                }
                Ok(res["form_data"].clone())
            }
            ("alipay", Types::Jsapi) => {
                let res = self.http(url, body)?;
                if res.has_key("PAYURL") {
                    let url = res["PAYURL"].to_string();
                    if self.debug {
                        debug!("{url:#}");
                    }
                    let mut http = br_reqwest::Client::new();

                    let re = match http.post(url.as_str()).send() {
                        Ok(e) => e,
                        Err(e) => {
                            return Err(e.to_string());
                        }
                    };
                    let re = re.body().to_string();
                    let res = match json::parse(&re) {
                        Ok(e) => e,
                        Err(_) => return Err(re)
                    };
                    if self.debug {
                        debug!("{res:#}");
                    }
                    if res.has_key("ERRCODE") && res["ERRCODE"] != "000000" {
                        return Err(format!("获取支付参数: 错误码: [{}] {} 失败", res["ERRCODE"], res["ERRMSG"]));
                    }
                    let r = br_crypto::encoding::urlencoding_decode(res["QRURL"].as_str().unwrap());
                    Ok(object! {
                        url:r.clone()
                    })
                } else {
                    Err(res.to_string())
                }
            }
            _ => {
                let res = self.http(url, body)?;
                Ok(res)
            }
        }
    }

    fn micropay(&mut self, channel: &str, auth_code: &str, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, _org_openid: &str, _ip: &str) -> Result<JsonValue, String> {
        let body = object! {
            MERCHANTID:sub_mchid,
            POSID:self.sub_posid.clone(),
            BRANCHID:self.branchid.clone(),
            MERFLAG:"1",
            TERMNO1:"",
            TERMNO2:"",
            ORDERID:out_trade_no,
            QRCODE:auth_code,
            AMOUNT:total_fee,
            TXCODE:"PAY100",
            PROINFO:description,
            REMARK1:"",
            REMARK2:"",
            SMERID:"",SMERNAME:"",SMERTYPEID:"",SMERTYPE:"",TRADECODE:"",TRADENAME:"",SMEPROTYPE:"",PRONAME:""
        };
        let url = "https://ibsbjstar.ccb.com.cn/CCBIS/B2CMainPlat_00_BEPAY";
        let res = self.http_ccb_param(url, body)?;

        match res["RESULT"].as_str().unwrap_or("N") {
            "Y" => {
                Ok(self.pay_micropay_query(out_trade_no, sub_mchid, channel)?)
            }
            "N" => {
                Err(res["ERRMSG"].to_string())
            }
            "U" => {
                Err(res.to_string())
            }
            "Q" => {
                let res = PayNotify {
                    trade_type: TradeType::MICROPAY,
                    out_trade_no: out_trade_no.to_string(),
                    sp_mchid: self.sp_mchid.clone(),
                    sub_mchid: sub_mchid.to_string(),
                    sp_appid: "".to_string(),
                    transaction_id: res["TRACEID"].to_string(),
                    success_time: 0,
                    sp_openid: "".to_string(),
                    sub_openid: "".to_string(),
                    total: total_fee,
                    payer_total: total_fee,
                    currency: "CNY".to_string(),
                    payer_currency: "CNY".to_string(),
                    trade_state: TradeState::NOTPAY,
                };
                Ok(res.json())
            }
            _ => {
                Err(res.to_string())
            }
        }
    }

    fn close(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
        let today = Local::now().date_naive();
        let date_str = today.format("%Y%m%d").to_string();
        let order_date = &out_trade_no[0..8];
        let kind = if date_str == order_date {
            0
        } else {
            1
        };

        let mut text = self.get_xml_text("5W1002.xml");

        text = text.replace("{{START}}", order_date);
        text = text.replace("{{END}}", order_date);
        text = text.replace("{{KIND}}", kind.to_string().as_str());
        text = text.replace("{{ORDER}}", out_trade_no);

        text = text.replace("{{POS_CODE}}", self.sp_posid.as_str());
        text = text.replace("{{STATUS}}", "1");
        text = text.replace("{{Mrch_No}}", sub_mchid);

        let res = self.https_cert(&text)?;
        if res.has_key("RETURN_CODE") && res["RETURN_CODE"].ne("000000") {
            if res["RETURN_MSG"].eq("流水记录不存在") {
                return Ok(true.into());
            }
            return Err(res["RETURN_MSG"].to_string());
        }
        let trade_state = match res["TX_INFO"]["LIST"]["ORDER_STATUS"].as_str().unwrap_or("") {
            "0" => TradeState::PAYERROR,
            "1" => TradeState::SUCCESS,
            "2" | "5" => TradeState::USERPAYING,
            "3" | "4" => TradeState::REFUND,
            _ => return Err("未知状态".to_string()),
        };
        match trade_state {
            TradeState::SUCCESS => Ok(false.into()),
            TradeState::REFUND => Ok(false.into()),
            TradeState::NOTPAY => Ok(true.into()),
            TradeState::CLOSED => Ok(true.into()),
            TradeState::REVOKED => Ok(true.into()),
            TradeState::USERPAYING => Ok(false.into()),
            TradeState::PAYERROR => Ok(true.into()),
            TradeState::None => Ok(true.into()),
        }
    }

    fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
        let today = Local::now().date_naive();
        let date_str = today.format("%Y%m%d").to_string();
        let order_date = &out_trade_no[0..8];
        let kind = if date_str == order_date {
            0
        } else {
            1
        };

        let mut text = self.get_xml_text("5W1002.xml");

        text = text.replace("{{START}}", order_date);
        text = text.replace("{{END}}", order_date);
        text = text.replace("{{KIND}}", kind.to_string().as_str());
        text = text.replace("{{ORDER}}", out_trade_no);

        text = text.replace("{{POS_CODE}}", self.sp_posid.as_str());
        text = text.replace("{{STATUS}}", "1");
        text = text.replace("{{Mrch_No}}", sub_mchid);

        let res = self.https_cert(&text)?;
        if res.has_key("RETURN_CODE") && res["RETURN_CODE"].ne("000000") {
            if res["RETURN_MSG"].eq("流水记录不存在") {
                let ttt = PayNotify {
                    trade_type: TradeType::None,
                    out_trade_no: out_trade_no.to_string(),
                    sp_mchid: self.sp_mchid.to_string(),
                    sub_mchid: sub_mchid.to_string(),
                    sp_appid: "".to_string(),
                    transaction_id: "".to_string(),
                    success_time: 0,
                    sp_openid: "".to_string(),
                    sub_openid: "".to_string(),
                    total: 0.0,
                    payer_total: 0.0,
                    currency: "CNY".to_string(),
                    payer_currency: "CNY".to_string(),
                    trade_state: TradeState::NOTPAY,
                };
                return Ok(ttt.json());
            }
            return Err(res["RETURN_MSG"].to_string());
        }
        let trade_state = match res["TX_INFO"]["LIST"]["ORDER_STATUS"].as_str().unwrap_or("") {
            "0" => TradeState::PAYERROR,
            "1" => TradeState::SUCCESS,
            "2" | "5" => TradeState::USERPAYING,
            "3" | "4" => TradeState::REFUND,
            _ => return Err("未知状态".to_string()),
        };
        let data = res["TX_INFO"]["LIST"].clone();
        let res = match trade_state {
            TradeState::SUCCESS => {
                PayNotify {
                    trade_type: TradeType::None,
                    out_trade_no: out_trade_no.to_string(),
                    sp_mchid: self.sp_mchid.to_string(),
                    sub_mchid: sub_mchid.to_string(),
                    sp_appid: "".to_string(),
                    transaction_id: data["OriOvrlsttnEV_Trck_No"].to_string(),
                    success_time: PayNotify::datetime_to_timestamp(data["TRAN_DATE"].as_str().unwrap_or(""), "%Y-%m-%d %H:%M:%S"),
                    sp_openid: "".to_string(),
                    sub_openid: "".to_string(),
                    total: data["Orig_Amt"].to_string().parse::<f64>().unwrap_or(0.0),
                    payer_total: data["Txn_ClrgAmt"].to_string().parse::<f64>().unwrap_or(0.0),
                    currency: "CNY".to_string(),
                    payer_currency: "CNY".to_string(),
                    trade_state,
                }
            }
            _ => PayNotify {
                trade_type: TradeType::None,
                out_trade_no: out_trade_no.to_string(),
                sp_mchid: self.sp_mchid.to_string(),
                sub_mchid: sub_mchid.to_string(),
                sp_appid: "".to_string(),
                transaction_id: res["ORDER"].to_string(),
                success_time: 0,
                sp_openid: "".to_string(),
                sub_openid: "".to_string(),
                total: 0.0,
                payer_total: 0.0,
                currency: "CNY".to_string(),
                payer_currency: "CNY".to_string(),
                trade_state,
            }
        };
        Ok(res.json())
    }

    fn pay_micropay_query(&mut self, out_trade_no: &str, sub_mchid: &str, channel: &str) -> Result<JsonValue, String> {
        let crcode_type = match channel {
            "wechat" => "2",
            "alipay" => "3",
            _ => "5"
        };

        let body = object! {
            MERCHANTID:sub_mchid,
            POSID:self.sub_posid.clone(),
            BRANCHID:self.branchid.clone(),
            TXCODE:"PAY102",
            MERFLAG:"1",
            TERMNO1:"",
            TERMNO2:"",
            ORDERID:out_trade_no,
            QRYTIME:"1",
            QRCODETYPE:crcode_type,
            QRCODE:"",
            REMARK1:"",
            REMARK2:"",
        };
        let url = "https://ibsbjstar.ccb.com.cn/CCBIS/B2CMainPlat_00_BEPAY";
        let res = self.http_ccb_param(url, body)?;

        match res["RESULT"].as_str().unwrap_or("N") {
            "Y" => {
                let now = Local::now();
                let formatted = now.format("%Y%m%d%H%M%S").to_string();
                let transaction_id = match channel {
                    "wechat" => res["WECHAT_NO"].to_string(),
                    "alipay" => res["ZFB_NO"].to_string(),
                    _ => "".to_string()
                };
                let res = PayNotify {
                    trade_type: TradeType::MICROPAY,
                    out_trade_no: out_trade_no.to_string(),
                    sp_mchid: self.sp_mchid.clone(),
                    sub_mchid: sub_mchid.to_string(),
                    sp_appid: "".to_string(),
                    transaction_id,
                    success_time: PayNotify::datetime_to_timestamp(formatted.as_str(), "%Y%m%d%H%M%S"),
                    sp_openid: "".to_string(),
                    sub_openid: "".to_string(),
                    total: res["AMOUNT"].to_string().parse::<f64>().unwrap_or(0.0),
                    payer_total: res["AMOUNT"].to_string().parse::<f64>().unwrap_or(0.0),
                    currency: "CNY".to_string(),
                    payer_currency: "CNY".to_string(),
                    trade_state: TradeState::SUCCESS,
                };
                Ok(res.json())
            }
            "N" => {
                let crcode_type = match channel {
                    "wechat" => res["WECHAT_STATE"].to_string(),
                    "alipay" => res["ZFB_STATE"].to_string(),
                    _ => "".to_string()
                };
                let res = PayNotify {
                    trade_type: TradeType::MICROPAY,
                    out_trade_no: out_trade_no.to_string(),
                    sp_mchid: self.sp_mchid.clone(),
                    sub_mchid: sub_mchid.to_string(),
                    sp_appid: "".to_string(),
                    transaction_id: "".to_string(),
                    success_time: 0,
                    sp_openid: "".to_string(),
                    sub_openid: "".to_string(),
                    total: 0.0,
                    currency: "CNY".to_string(),
                    payer_total: 0.0,
                    payer_currency: "CNY".to_string(),
                    trade_state: TradeState::from(crcode_type.as_str()),
                };
                Ok(res.json())
            }
            "U" => {
                Err(res.to_string())
            }
            "Q" => {
                let res = PayNotify {
                    trade_type: TradeType::MICROPAY,
                    out_trade_no: out_trade_no.to_string(),
                    sp_mchid: self.sp_mchid.clone(),
                    sub_mchid: sub_mchid.to_string(),
                    sp_appid: "".to_string(),
                    transaction_id: "".to_string(),
                    success_time: 0,
                    sp_openid: "".to_string(),
                    sub_openid: "".to_string(),
                    total: 0.0,
                    payer_total: 0.0,
                    currency: "CNY".to_string(),
                    payer_currency: "CNY".to_string(),
                    trade_state: TradeState::NOTPAY,
                };
                Ok(res.json())
            }
            _ => {
                Err(res.to_string())
            }
        }
    }

    fn pay_notify(&mut self, _nonce: &str, _ciphertext: &str, _associated_data: &str) -> Result<JsonValue, String> {
        Err("暂未开通".to_string())
    }

    fn refund(&mut self, sub_mchid: &str, out_trade_no: &str, _transaction_id: &str, out_refund_no: &str, amount: f64, _total: f64, _currency: &str) -> Result<JsonValue, String> {
        let mut text = self.get_xml_text("5W1024.xml");
        text = text.replace("{{MONEY}}", amount.to_string().as_str());
        text = text.replace("{{ORDER}}", out_trade_no);
        text = text.replace("{{REFUND_CODE}}", out_refund_no);
        text = text.replace("{{Mrch_No}}", sub_mchid);
        let res = self.https_cert(&text)?;
        if res.has_key("RETURN_CODE") && res["RETURN_CODE"].ne("000000") {
            return Err(res["RETURN_MSG"].to_string());
        }
        if res.has_key("RETURN_MSG") {
            error!("{:#}", res);
            return Err(res["RETURN_MSG"].to_string());
        }
        info!("{:#}", res);
        Ok(res)
    }

    fn micropay_refund(&mut self, _sub_mchid: &str, _out_trade_no: &str, _transaction_id: &str, _out_refund_no: &str, _amount: f64, _total: f64, _currency: &str, _refund_text: &str) -> Result<JsonValue, String> {
        Err("暂未开通".to_string())
    }

    fn refund_notify(&mut self, _nonce: &str, _ciphertext: &str, _associated_data: &str) -> Result<JsonValue, String> {
        Err("暂未开通".to_string())
    }

    fn refund_query(&mut self, trade_no: &str, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
        let today = Local::now().date_naive();
        let date_str = today.format("%Y%m%d").to_string();
        let order_date = &out_refund_no[0..8];
        let kind = if date_str == order_date {
            0
        } else {
            1
        };

        let mut text = self.get_xml_text("5W1003.xml");

        text = text.replace("{{START}}", order_date);
        text = text.replace("{{END}}", order_date);
        text = text.replace("{{KIND}}", kind.to_string().as_str());
        text = text.replace("{{ORDER}}", out_refund_no);

        text = text.replace("{{POS_CODE}}", self.sp_posid.as_str());
        text = text.replace("{{STATUS}}", "1");
        text = text.replace("{{Mrch_No}}", sub_mchid);

        let res = self.https_cert(&text)?;

        if res.has_key("RETURN_MSG") {
            if res["RETURN_MSG"].eq("流水记录不存在") {
                let res = RefundNotify {
                    out_trade_no: trade_no.to_string(),
                    refund_no: out_refund_no.to_string(),
                    sp_mchid: self.sp_mchid.to_string(),
                    sub_mchid: sub_mchid.to_string(),
                    transaction_id: "".to_string(),
                    refund_id: "".to_string(),
                    success_time: 0,
                    total: 0.0,
                    refund: 0.0,
                    payer_total: 0.0,
                    payer_refund: 0.0,
                    status: RefundStatus::None,
                };
                return Ok(res.json());
            }
            return Err(res["RETURN_MSG"].to_string());
        }
        Err("暂未开通".to_string())
        //
        //let today = Local::now().date_naive();
        //let date_str = today.format("%Y%m%d").to_string();
        //let body = object! {
        //    MERCHANTID:sub_mchid,
        //    BRANCHID:self.branchid.clone(),
        //    POSID:self.sub_posid.clone(),
        //    ORDERDATE:date_str,
        //    BEGORDERTIME:"00:00:00",
        //    ENDORDERTIME:"23:59:59",
        //    ORDERID:out_refund_no,
        //    QUPWD:self.sub_pass.clone(),
        //    TXCODE:"410408",
        //    TYPE:"1",
        //    KIND:"0",
        //    STATUS:"1",
        //    SEL_TYPE:"3",
        //    PAGE:"1",
        //    OPERATOR:"",
        //    CHANNEL:"",
        //    MAC:""
        //};
        //let res = self.http_q("https://ibsbjstar.ccb.com.cn/CCBIS/ccbMain", body)?;
        //if res["RETURN_CODE"] != "000000" {
        //    if res["RETURN_MSG"].eq("流水记录不存在") {
        //        let res = PayNotify {
        //            trade_type: TradeType::None,
        //            out_trade_no: "".to_string(),
        //            sp_mchid: "".to_string(),
        //            sub_mchid: "".to_string(),
        //            sp_appid: "".to_string(),
        //            transaction_id: "".to_string(),
        //            success_time: 0,
        //            sp_openid: "".to_string(),
        //            sub_openid: "".to_string(),
        //            total: 0.0,
        //            payer_total: 0.0,
        //            currency: "".to_string(),
        //            payer_currency: "".to_string(),
        //            trade_state: TradeState::NOTPAY,
        //        };
        //        return Ok(res.json());
        //    }
        //    return Err(res["RETURN_MSG"].to_string());
        //}
        //let data = res["QUERYORDER"].clone();
        //let res = RefundNotify {
        //    out_trade_no: trade_no.to_string(),
        //    refund_no: out_refund_no.to_string(),
        //    sp_mchid: "".to_string(),
        //    sub_mchid: sub_mchid.to_string(),
        //    transaction_id: data["ORDERID"].to_string(),
        //    refund_id: data["refund_id"].to_string(),
        //    success_time: PayNotify::datetime_to_timestamp(data["ORDERDATE"].as_str().unwrap_or(""), "%Y%m%d%H%M%S"),
        //    total: data["AMOUNT"].as_f64().unwrap_or(0.0),
        //    payer_total: data["amount"]["total"].to_string().parse::<f64>().unwrap(),
        //    refund: data["amount"]["refund"].to_string().parse::<f64>().unwrap(),
        //    payer_refund: data["amount"]["refund"].to_string().parse::<f64>().unwrap(),
        //    status: RefundStatus::from(data["STATUS"].as_str().unwrap()),
        //};
        //
        //Ok(res.json())
    }

    fn incoming(&mut self, _business_code: &str, _contact_info: JsonValue, _subject_info: JsonValue, _business_info: JsonValue, _settlement_info: JsonValue, _bank_account_info: JsonValue) -> Result<JsonValue, String> {
        todo!()
    }
}

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()),
    }
}