//! 盛大数据模型

use crate::{
    backend::{self},
    config::Config,
    gmssl, Sign, Verify,
};
use anyhow::Context;
use axum::{response::IntoResponse, Json};
use md5::{Digest, Md5};
use serde::de::DeserializeOwned;
#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PubReqHead {
    pub app_id: String,

    pub method: String,

    pub sign_type: String,

    pub sign: String,

    pub timestamp: String, // yyyy-MM-dd HH:mm:ss

    pub version: String,

    pub request_id: String,

    pub biz_content: String,
}

pub trait TryFromPubHeadConfig: Sized {
    fn try_from_pub_req_head_config(head: &PubReqHead, config: &Config) -> anyhow::Result<Self>;
}

impl<T> TryFromPubHeadConfig for T
where
    T: Sized + DeserializeOwned + crate::Verify,
{
    fn try_from_pub_req_head_config(head: &PubReqHead, config: &Config) -> anyhow::Result<Self> {
        let req: T = head.decopy(config)?;
        req.verify(config).context("验证出错")?;
        Ok(req)
    }
}

impl crate::Verify for PubReqHead {
    #[tracing::instrument(skip_all, fields(PubReqHead=?self))]
    fn verify(&self, config: &Config) -> anyhow::Result<()> {
        if self.sign_type != "md5"
            || self.app_id != config.sd_config.app_id
            || self.method != "sd.tovc.recharge"
        {
            return Err(anyhow::Error::msg("PubReqHead Verify Value Error"));
        }

        match hex::decode(&self.sign) {
            Ok(o) => {
                let new_sign = self.sign_var(config);
                if o == new_sign {
                    Ok(())
                } else {
                    Err(anyhow::Error::msg("sign 不匹配"))
                }
            }
            Err(e) => {
                tracing::error!("verify: {self:?} error: {e}");
                Err(anyhow::Error::msg(format!(
                    "PubReqHead Verify Value sign hex::decode Error: {e:#?}"
                )))
            }
        }
    }
}

impl Default for PubReqHead {
    fn default() -> Self {
        let time_format = time::macros::format_description!(
        "[year]-[month padding:zero]-[day padding:zero] [hour padding:zero]:[minute padding:zero]:[second padding:zero]"
    );
        let offset = time::UtcOffset::from_hms(8, 0, 0).unwrap();
        let now = time::OffsetDateTime::now_utc().to_offset(offset);

        Self {
            app_id: "".to_string(),
            method: "".to_string(),
            sign_type: "md5".to_string(),
            sign: "".to_string(),
            timestamp: now.format(time_format).unwrap(),
            version: "1.0".to_string(),
            request_id: "".to_string(),
            biz_content: "".to_string(),
        }
    }
}

macro_rules! str_per {
    ( $($p:ident).*) => {
        (str_per!(@internal $($p).*), $($p).*)
    };
    (@internal $p:ident ) => {
        {
            let s = stringify!($p);
            let mut str = String::new();
            let mut capitalize = false;
            for i in s.chars(){
                if i == '_'{
                    capitalize = true;
                }else if capitalize {
                    str.push(i.to_ascii_uppercase());
                    capitalize = false;
                }else{
                    str.push(i)
                }
            }
            str
        }

    };
    (@internal $_p:ident.$($p:ident).*) => {
        str_per!(@internal $($p).*)
    };
}

macro_rules! bt_map {
    ( $( ($k:expr, $v:expr) ),* $(,)?) => {
        {
            let mut map = std::collections::BTreeMap::new();
            $(
                map.insert($k, $v)
            )*
            map
        }
    };
    ($($k:expr),* $(,)?) => {
        {
            let mut map = std::collections::BTreeMap::new();
            $(
                let entry: (_,_) = $k;
                map.insert(entry.0, entry.1);
            )*
            map
        }
    }
}

impl PubReqHead {
    pub fn encrypt_sign<T>(&mut self, config: &Config, data: T) -> anyhow::Result<()>
    where
        T: serde::Serialize,
    {
        let data = serde_json::to_string(&data)?;
        let key = config
            .sd_config
            .get_pk()
            .context("PayReqHead::encrypt_sign")?;
        let r = key.encrypt(data, gmssl::sm2::PassOrder::C1C3C2)?;
        let data = hex::encode(r);
        self.biz_content = data;
        self.sign(config);
        Ok(())
    }

    pub fn decopy<T>(&self, config: &Config) -> anyhow::Result<T>
    where
        T: for<'a> serde::Deserialize<'a>,
    {
        let key = config.sd_config.get_sk().context("PayReqHead::decopy")?;
        let data = hex::decode(&self.biz_content).context("hex::decode Error")?;
        let s = key
            .decrypt(data, gmssl::sm2::PassOrder::C1C3C2)
            .context("解密 biz_content")?;
        serde_json::from_slice(&s).context("反序列化 biz_content")
    }

    fn sign(&mut self, config: &Config) {
        self.sign = hex::encode_upper(self.sign_var(config))
    }

    pub fn sign_var(&self, config: &Config) -> Vec<u8> {
        let mut aa = Md5::new();
        let s = self.format_str(config);
        aa.update(s);
        let r = aa.finalize().to_vec();
        tracing::debug!("{self:?} Sing:{}", hex::encode(&r));
        r
    }

    pub fn format_str(&self, config: &Config) -> String {
        let Self {
            app_id,
            method,
            sign_type,
            sign,
            timestamp,
            version,
            request_id,
            biz_content,
        } = self;
        let map = bt_map!(
            str_per!(app_id),
            str_per!(method),
            str_per!(sign_type),
            str_per!(sign),
            str_per!(timestamp),
            str_per!(version),
            str_per!(request_id),
            str_per!(biz_content)
        );

        let items: Vec<_> = map
            .into_iter()
            .map(|(k, v)| (k.trim().to_owned(), v.trim().to_owned()))
            .filter(|(k, v)| !v.is_empty() && k != "sign")
            .map(|(k, v)| format!("{}={}", k, v))
            .collect();
        let mut result = items.join("&");
        result.push('&');
        result.push_str(&format!("key={}", config.sd_config.sin_key));
        result
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PayReqBody {
    pub game_user_id: String,

    pub server_type: String,

    pub product_no: String,

    pub sp_order_id: String,

    pub source: String,
}

impl crate::Verify for PayReqBody {
    fn verify(&self, _: &Config) -> anyhow::Result<()> {
        if self.server_type != "tel" {
            return Err(anyhow::Error::msg("仅支持话费"));
        }
        Ok(())
    }
}

impl PayReqBody {
    pub fn into_backend_pay_req(
        &self,
        head: &PubReqHead,
        config: &Config,
    ) -> backend::Body<backend::PayReqBody> {
        let timestamp = head.timestamp.trim().replace(
            |c: char| c.is_whitespace() || c == '-' || c == '_' || c == ':',
            "",
        );
        let mut body = backend::Body {
            header: backend::Header {
                timestamp,
                seqno: head.request_id.clone(),
                appid: config.fscg_config.app_id.clone(),
                ..Default::default()
            },
            msgbody: backend::PayReqBody {
                content: Some(backend::PayReqContent {
                    user: self.game_user_id.clone(),
                    packageid: self.product_no.clone(),
                    extorder: self.sp_order_id.clone(),
                    note: Some(self.source.clone()),
                    ..Default::default()
                }),
                resp: None,
            },
        };

        body.sign(config);
        body
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PayRespBodyData {
    state: String,

    order_cash: String,

    order_id: String,

    sp_order_id: String,

    game_user_id: String,

    product_name: String,
}

pub type PayRespBody = PubRespBody<PayRespBodyData>;

impl PayRespBody {
    pub fn from_backend_pay_resp(
        config: &Config,
        body: backend::Body<backend::PayRespBody>,
        req: &PayReqBody,
    ) -> Self {
        if let Err(e) = body.verify(config) {
            // return Self::error("99", "服务响应验证错误");
            return Self::error("99", format!("服务响应验证错误: {:#?}", e));
        }

        let resp = match body.msgbody.resp {
            Some(resp) => resp,
            None => {
                return Self::error("99", "服务没有响应字段: resp");
            }
        };

        let content = match body.msgbody.content {
            Some(content) => content,
            None => {
                return Self::error(
                    resp.rcode,
                    resp.rmsg.unwrap_or("服务没有响应字段: content".to_string()),
                );
            }
        };

        let state = if resp.rcode == "00" {
            "0".to_string()
        } else {
            "9".to_string()
        };

        Self::success(PayRespBodyData {
            state,
            order_cash: "".to_string(),
            order_id: content.orderid,
            sp_order_id: content.extorder,
            game_user_id: req.game_user_id.clone(),
            product_name: req.product_no.clone(),
        })
    }
}

impl crate::Sign for PayRespBody {
    fn sign(&mut self, _: &Config) {}
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct BalanceRespBody {
    pub result_code: String,
    pub result_desc: String,
    pub balance: Option<String>,
}

impl BalanceRespBody {
    pub fn from_backend_account_resp(
        config: &Config,
        body: backend::Body<backend::AccountRespBody>,
    ) -> Self {
        if let Err(e) = body.verify(config) {
            return Self::error("99", format!("服务响应验证错误: {:#?}", e));
        }
        let resp = match body.msgbody.resp {
            Some(resp) => resp,
            None => {
                return Self::error("99", "服务没有响应字段: resp");
            }
        };
        let content = match body.msgbody.content {
            Some(content) => content,
            None => {
                return Self::error(
                    resp.rcode,
                    resp.rmsg.unwrap_or("服务没有响应字段: content".to_string()),
                );
            }
        };

        Self::success(content.balance)
    }

    pub fn success(data: impl Into<Option<String>>) -> Self {
        Self {
            result_code: "0000".to_string(),
            result_desc: "success".to_string(),
            balance: data.into(),
        }
    }

    pub fn error(code: impl ToString, desc: impl ToString) -> Self {
        Self {
            result_code: code.to_string(),
            result_desc: desc.to_string(),
            balance: None,
        }
    }
}

impl crate::Sign for BalanceRespBody {
    fn sign(&mut self, _: &Config) {}
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct BalanceReqBody {
    source: String,
}

impl BalanceReqBody {
    pub fn into_backend_account_req(
        &self,
        config: &Config,
        head: &PubReqHead,
    ) -> backend::Body<backend::AccountReqBody> {
        let backend_req = backend::AccountReqBody::new();
        let mut body = backend::Body {
            header: backend::Header {
                seqno: head.request_id.clone(),
                appid: config.fscg_config.app_id.clone(),
                ..Default::default()
            },
            msgbody: backend_req,
        };
        body.sign(config);

        body
    }
}

impl crate::Verify for BalanceReqBody {
    fn verify(&self, _: &Config) -> anyhow::Result<()> {
        if self.source == "SD" {
            Ok(())
        } else {
            Err(anyhow::Error::msg("BalanceReqBody Verify False"))
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SearchReqBody {
    sp_order_id: String,
}

impl SearchReqBody {
    pub fn into_backend_search_req(
        &self,
        head: &PubReqHead,
        config: &Config,
    ) -> backend::Body<backend::SearchReqBody> {
        let mut body = backend::Body {
            header: backend::Header {
                seqno: head.request_id.clone(),
                appid: config.fscg_config.app_id.clone(),
                ..Default::default()
            },
            msgbody: backend::SearchReqBody {
                content: Some(backend::SearchReqContent {
                    // orderid: Some(req.sp_order_id.clone()),
                    extorderid: Some(self.sp_order_id.clone()),
                    ..Default::default()
                }),
                resp: None,
            },
        };
        body.sign(config);

        body
    }
}

impl crate::Verify for SearchReqBody {}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct SearchRespData {
    state: String,
    message: String,
    success_time: Option<String>,
    order_id: String,
    sp_order_id: String,
    game_user_id: String,
    operator_order: Option<String>,
    card_no: Option<String>,        // 卡密
    card_pwd: Option<String>,       // 卡密
    effective_time: Option<String>, // 卡密
}

pub type SearchRespBody = PubRespBody<SearchRespData>;

impl SearchRespBody {
    pub fn from_backend_search_resp(
        config: &Config,
        body: backend::Body<backend::SearchRespBody>,
    ) -> Self {
        if let Err(e) = body.verify(config) {
            Self::error("99", format!("服务响应验证错误: {:#?}", e));
        }
        let resp = match body.msgbody.resp {
            Some(resp) => resp,
            None => {
                return Self::error("99", "服务没有响应字段: resp".to_string());
            }
        };
        if resp.rcode != "00" {
            let rcode = if resp.rcode=="06"{
                "0011"
            }else{
                &resp.rcode
            };
            return Self::error(rcode, format!("{:?}", resp.rmsg));
        };
        let content = match body.msgbody.content {
            Some(content) => content,
            None => {
                return Self::error(
                    resp.rcode,
                    resp.rmsg.unwrap_or("服务没有响应字段: content".to_string()),
                );
            }
        };
        let state = if content.status == "6" {
            "1"
        } else if content.status == "4" {
            "9"
        } else {
            "0"
        };

        let time_str = if content.checktime.is_empty() {
            None
        } else {
            let time_format = time::macros::format_description!("[year]-[month padding:zero]-[day padding:zero] [hour padding:zero]:[minute padding:zero]:[second padding:zero]");
            let backend_time = time::macros::format_description!("[year][month padding:zero][day padding:zero][hour padding:zero][minute padding:zero][second padding:zero][subsecond digits:1+]");
            let time = time::PrimitiveDateTime::parse(&content.checktime, backend_time).unwrap();
            let time_str = time.format(time_format).unwrap();
            Some(time_str)
        };

        let data = SearchRespData {
            state: state.to_string(),
            message: content.msg,
            success_time: time_str,
            order_id: content.orderid,
            sp_order_id: content.extorder,
            game_user_id: content.user,
            operator_order: content.attr1,
            card_no: None,
            card_pwd: None,
            effective_time: None,
        };

        Self::success(data)
    }
}

impl crate::Sign for SearchRespBody {
    fn sign(&mut self, _: &Config) {}
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PubRespBody<T> {
    pub result_code: String,
    pub result_desc: String,
    pub data: Option<T>,
}

impl<T> PubRespBody<T> {
    pub fn success(data: T) -> Self {
        Self {
            result_code: "0000".to_string(),
            result_desc: "success".to_string(),
            data: Some(data),
        }
    }

    pub fn error(code: impl ToString, desc: impl ToString) -> Self {
        Self {
            result_code: code.to_string(),
            result_desc: desc.to_string(),
            data: None,
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct PubRespHead {
    #[serde(default = "String::new")]
    pub code: String,
    #[serde(default = "String::new")]
    pub msg: String,
    #[serde(default = "String::new")]
    pub data: String,
    #[serde(default = "String::new")]
    pub sign: String,
    #[serde(default = "String::new")]
    pub request_id: String,
}

impl IntoResponse for PubRespHead {
    fn into_response(self) -> axum::response::Response {
        Json(self).into_response()
    }
}

impl PubRespHead {
    pub fn new(config: &Config, data: impl ToString, request_id: impl ToString) -> Self {
        let mut result = Self {
            data: data.to_string(),
            request_id: request_id.to_string(),
            ..Default::default()
        };
        result.sign(config);

        result
    }

    pub fn sign(&mut self, config: &Config) {
        self.sign = hex::encode_upper(self.sign_var(config))
    }

    pub fn sign_var(&self, config: &Config) -> Vec<u8> {
        let mut aa = Md5::new();
        let s = self.format_str(config);
        aa.update(&s);
        let r = aa.finalize().to_vec();
        tracing::debug!(sign = hex::encode(&r), data = s, "签名");
        r
    }

    pub fn format_str(&self, config: &Config) -> String {
        let Self {
            code,
            msg,
            data,
            sign,
            request_id,
        } = self;
        let map = bt_map!(
            str_per!(code),
            str_per!(msg),
            str_per!(data),
            str_per!(sign),
            str_per!(request_id),
        );

        let items: Vec<_> = map
            .into_iter()
            .map(|(k, v)| (k.trim().to_owned(), v.trim().to_owned()))
            .filter(|(k, v)| !v.is_empty() && k != "sign")
            .map(|(k, v)| format!("{}={}", k, v))
            .collect();
        let mut result = items.join("&");
        result.push('&');
        result.push_str(&format!("key={}", config.sd_config.sin_key));
        result
    }
}

impl Default for PubRespHead {
    fn default() -> Self {
        Self {
            code: "0000".to_string(),
            msg: "success".to_string(),
            data: "".to_string(),
            sign: "".to_string(),
            request_id: "".to_string(),
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CallbackReqBody {
    pub state: String,
    pub message: String,
    pub order_id: String,
    pub sp_order_id: String,
    pub game_user_id: String,
    pub operator_order: Option<String>,
    pub card_no: Option<String>,
    pub card_pwd: Option<String>,
    pub effective_time: Option<String>,
}

impl Default for CallbackReqBody {
    fn default() -> Self {
        Self {
            state: "".to_string(),
            message: "".to_string(),
            order_id: "".to_string(),
            sp_order_id: "".to_string(),
            game_user_id: "".to_string(),
            operator_order: None,
            card_no: None,
            card_pwd: None,
            effective_time: None,
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct CallbackRespBody {
    pub result_code: String,
    pub result_desc: String,
}

#[cfg(test)]
#[allow(unused)]
mod tests {
    use std::time::Duration;

    use crate::{
        backend::{Body, CallbackReqContent, Header, PubMsgBody},
        config::SdConfig,
    };

    use super::*;

    #[tokio::test]
    async fn test_callback() {
        let body = Body {
            header: Header {
                version: "V1.0".to_string(),
                timestamp: "20230419151000".to_string(),
                seqno: "CP1681888200006".to_string(),
                appid: "jtceshi".to_string(),
                secertkey: "aebcc37778fd948b0972013e4d9ac4eb".to_string(),
            },
            msgbody: PubMsgBody {
                content: Some(CallbackReqContent {
                    orderid: "1681888064405103338".to_string(),
                    extorder: "CESHIAAEF13447172744".to_string(),
                    status: "失败".to_string(),
                    code: "99".to_string(),
                    price: "1000000".to_string(),
                    checktime: "20230418170659000".to_string(),
                    attr1: None,
                    attr2: None,
                    attr3: None,
                }),
                resp: None::<()>,
            },
        };

        let req = reqwest::Client::builder()
            // .user_agent(APP_USER_AGENT)
            .deflate(true)
            .gzip(true)
            .brotli(true)
            .timeout(Duration::from_millis(10000))
            .no_proxy()
            // .trust_dns(true)
            .build()
            .unwrap();
        let resp: serde_json::Value = req
            .post("http://192.144.212.211:13657/callback")
            .json(&body)
            .send()
            .await
            .unwrap()
            .json()
            .await
            .unwrap();
        println!("resp: {}", resp);
    }

    #[test]
    fn test_aaaa() {
        let s = "04f97e835ec530a95df4c999ab0184996b68464bf308239ebb692174812fb4c54349fc217ac5ab13ea8313e458e2cf37fa35345afc4290f8d25dc7c8bb37da1a0ff87a7752c8d03998067b3bf9fec1d7e397aba05fb016b3b48e269e9c87c22b6eef61508de4572c7dacd980530dac913f507ad9aa2a2a8c62edae35c21bf3aaa446168989a1350914499322ebb5b37038e432ed8f37e25083e35087b4fc1dbff3488c1e7cd7810712725a1fd78365a73892bf6b45352ca775b7b2e66a334ed93986728d2c2c78af2bbfaeb590d55a494163371c0c5df7";
        let head = PubReqHead {
            biz_content: s.to_string(),
            ..Default::default()
        };
        let cfg = Config::default();
        let r = head.decopy::<serde_json::Value>(&cfg);
        println!("{:#?}", r)
    }

    #[test]
    fn test_sign() {
        // 2C5A4C48A9BD07B79C250049A17C21C2
        // 2C5A4C48A9BD07B79C250049A17C21C2
        let mut a = PubReqHead {
            app_id: "asdaw".to_string(),
            method: "asdaw".to_string(),
            sign_type: "asdaw".to_string(),
            sign: "asdaw".to_string(),
            timestamp: "2023-03-23 16:25:00".to_string(),
            version: "asdaw".to_string(),
            request_id: "asdaw".to_string(),
            biz_content: "".to_string(),
        };
        let c = Default::default();
        a.sign(&c);
        println!("{a:?}");
    }

    #[test]
    fn feature() {
        let r = PayReqBody {
            game_user_id: "13856023633".to_string(),
            server_type: "tel".to_string(),
            product_no: "LD_5477".to_string(),
            sp_order_id: "QHGHFBGF0235641202".to_string(),
            source: "SD".to_string(),
        };

        let r = serde_json::to_string(&r).unwrap();
        println!("{r}")
    }

    #[test]
    fn test_try_into_fscg_req() {
        // 470EF517335D444BD003098D6F6ABCE0
        // 470EF517335D444BD003098D6F6ABCE0
        let now = std::time::Instant::now();
        let mut a = PubReqHead {
            app_id: "asdaw".to_string(),
            method: "asdaw".to_string(),
            sign_type: "asdaw".to_string(),
            sign: "asdaw".to_string(),
            timestamp: "2023-03-23 16:25:00".to_string(),
            version: "asdaw".to_string(),
            request_id: "asdaw".to_string(),
            biz_content: r#"{"gameUserId":"13856023633","serverType":"tel","productNo":"LD_5477","spOrderId":"QHGHFBGF0235641202","source":"SD"}"#.to_string(),
        };
        let c: Config = Default::default();
        // let r = c.sd_config.;
        let key = c.sd_config.get_sk().unwrap();
        let r = key
            .encrypt(a.biz_content, gmssl::sm2::PassOrder::C1C2C3)
            .unwrap();
        a.biz_content = hex::encode(r);
        a.sign(&c);

        println!("{a:?}");

        let new = std::time::Instant::now();
        println!("use Time{:?}", new - now);
    }
}
