use crate::{config::Config, Sign};
use md5::{Digest, Md5};

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct PubMsgBody<C, R> {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub content: Option<C>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resp: Option<R>,
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct Header {
    pub version: String,
    pub timestamp: String,
    pub seqno: String,
    pub appid: String,
    pub secertkey: String,
}

impl crate::Verify for Header {
    fn verify(&self, config: &crate::config::Config) -> anyhow::Result<()> {
        let r = self.sign_var(config);
        match hex::decode(&self.secertkey) {
            Ok(o) => {
                if o == r {
                    Ok(())
                } else {
                    Err(anyhow::Error::msg("sign 不匹配"))
                }
            }
            Err(e) => Err(anyhow::Error::msg(format!("sign 解码异常: {e:#?}"))),
        }
    }
}

impl Sign for Header {
    fn sign(&mut self, config: &crate::config::Config) {
        let r = self.sign_var(config);
        self.secertkey = hex::encode_upper(r);
    }
}

impl Header {
    pub fn sign_var(&self, config: &crate::config::Config) -> Vec<u8> {
        let str = format!(
            "{}{}{}{}",
            self.timestamp, self.seqno, self.appid, config.fscg_config.app_secret
        );

        let mut aa = Md5::new();
        aa.update(str);

        aa.finalize().to_vec()
    }
}

impl Default for Header {
    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][subsecond digits:3]");
        let offset = time::UtcOffset::from_hms(8, 0, 0).unwrap();
        let now = time::OffsetDateTime::now_utc().to_offset(offset);
        let timestamp = now.format(time_format).unwrap();

        Self {
            version: "V1.1".to_string(),
            timestamp,
            seqno: now.unix_timestamp_nanos().to_string(),
            appid: Default::default(),
            secertkey: Default::default(),
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct PayReqContent {
    pub sign: String,
    pub user: String,
    pub packageid: String,
    pub ordertype: isize,
    pub extorder: String,
    pub r#type: Option<String>,
    pub note: Option<String>,
}

impl Default for PayReqContent {
    fn default() -> Self {
        Self {
            sign: Default::default(),
            user: Default::default(),
            packageid: Default::default(),
            ordertype: 3,
            extorder: Default::default(),
            r#type: None,
            note: None,
        }
    }
}

impl Sign for PayReqContent {
    fn sign(&mut self, config: &crate::config::Config) {
        let r = self.sign_var(config);
        self.sign = hex::encode_upper(r)
    }
}

impl PayReqContent {
    pub fn sign_var(&self, config: &crate::config::Config) -> Vec<u8> {
        let str = format!(
            "{}{}{}{}{}",
            config.fscg_config.app_secret, self.user, self.packageid, self.ordertype, self.extorder
        );
        let mut aa = Md5::new();
        aa.update(str);

        aa.finalize().to_vec()
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct PayRespContent {
    pub orderid: String,
    pub extorder: String,
}

impl crate::Verify for PayRespContent {}

pub type PayReqBody = PubMsgBody<PayReqContent, ()>;

impl Sign for PayReqBody {
    fn sign(&mut self, config: &crate::config::Config) {
        self.content.sign(config)
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct RespResp {
    pub rcode: String,
    pub rmsg: Option<String>,
}
impl crate::Sign for RespResp {
    fn sign(&mut self, _: &Config) {}
}
pub type PubRespBody<T> = PubMsgBody<T, RespResp>;

pub type PayRespBody = PubRespBody<PayRespContent>;

impl<T> crate::Verify for PubRespBody<T>
where
    T: crate::Verify,
{
    fn verify(&self, config: &crate::config::Config) -> anyhow::Result<()> {
        self.content
            .as_ref()
            .map(move |o| o.verify(config))
            .unwrap_or(Ok(()))
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct SearchReqContent {
    pub sign: String,
    pub orderid: Option<String>,
    pub extorderid: Option<String>,
}

pub type SearchReqBody = PubMsgBody<SearchReqContent, ()>;

impl crate::Sign for SearchReqBody {
    fn sign(&mut self, config: &crate::config::Config) {
        self.content.sign(config)
    }
}

impl Default for SearchReqContent {
    fn default() -> Self {
        Self {
            sign: "".to_string(),
            orderid: None,
            extorderid: None,
        }
    }
}

impl SearchReqContent {
    pub fn sign_var(&self, config: &crate::config::Config) -> Vec<u8> {
        let str = format!(
            "{}{}{}",
            config.fscg_config.app_secret,
            self.orderid.as_deref().unwrap_or(""),
            self.extorderid.as_deref().unwrap_or(""),
        );
        let mut aa = Md5::new();
        aa.update(str);

        aa.finalize().to_vec()
    }
}

impl crate::Sign for SearchReqContent {
    fn sign(&mut self, config: &crate::config::Config) {
        let r = self.sign_var(config);
        self.sign = hex::encode_upper(r)
    }
}

impl crate::Verify for SearchReqContent {
    fn verify(&self, config: &crate::config::Config) -> anyhow::Result<()> {
        let r = self.sign_var(config);
        match hex::decode(&self.sign) {
            Ok(o) => {
                if o == r {
                    Ok(())
                } else {
                    Err(anyhow::Error::msg("sign 不匹配"))
                }
            }
            Err(e) => Err(anyhow::Error::msg(format!("sign 解码异常: {e:#?}"))),
        }
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct SearchRespContent {
    pub orderid: String,
    pub ordertype: Option<isize>,
    pub user: String,
    pub reqdate: String,
    pub packageid: String,
    pub status: String,
    pub extorder: String,
    pub msg: String,
    pub price: String,
    pub checktime: String,
    pub attr1: Option<String>,
    pub attr2: Option<String>,
    pub attr3: Option<String>,
}

impl crate::Verify for SearchRespContent {}

pub type SearchRespBody = PubRespBody<SearchRespContent>;

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct Body<T> {
    pub header: Header,
    pub msgbody: T,
}

impl<T> Sign for Body<T>
where
    T: Sign,
{
    fn sign(&mut self, config: &crate::config::Config) {
        self.header.sign(config);
        self.msgbody.sign(config);
    }
}
impl<T> crate::Verify for Body<T>
where
    T: crate::Verify,
{
    fn verify(&self, config: &crate::config::Config) -> anyhow::Result<()> {
        self.header
            .verify(config)
            .and_then(|_| self.msgbody.verify(config))
    }
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct AccountReqContent {
    sign: String,
    timestamp: String,
}

impl AccountReqContent {
    pub fn sign_var(&self, config: &crate::config::Config) -> Vec<u8> {
        let str = format!("{}{}", config.fscg_config.app_secret, self.timestamp);
        let mut aa = Md5::new();
        aa.update(str);
        aa.finalize().to_vec()
    }
}
impl crate::Sign for AccountReqContent {
    fn sign(&mut self, config: &crate::config::Config) {
        let r = self.sign_var(config);
        self.sign = hex::encode_upper(r)
    }
}

pub type AccountReqBody = PubMsgBody<AccountReqContent, ()>;

impl crate::Sign for AccountReqBody {
    fn sign(&mut self, config: &Config) {
        self.content.sign(config)
    }
}

impl AccountReqBody {
    pub fn new() -> Self {
        let offset = time::UtcOffset::from_hms(8, 0, 0).unwrap();
        let now = time::OffsetDateTime::now_utc().to_offset(offset);
        let content = AccountReqContent {
            sign: "".to_string(),
            timestamp: (now.unix_timestamp() * 1000).to_string(),
        };
        Self {
            content: Some(content),
            resp: None,
        }
    }
}

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

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct AccountRespContent {
    pub balance: String,
    pub status: String,
}

pub type AccountRespBody = PubMsgBody<AccountRespContent, RespResp>;

impl crate::Verify for AccountRespBody {}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
#[serde(rename_all = "UPPERCASE")]
pub struct CallbackReqContent {
    pub orderid: String,
    pub extorder: String,
    pub status: String,
    pub code: String,
    pub price: String,
    pub checktime: String,
    pub attr1: Option<String>,
    pub attr2: Option<String>,
    pub attr3: Option<String>,
}

pub type CallbackReqBody = PubMsgBody<CallbackReqContent, ()>;
pub type CallbackRespBody = PubMsgBody<(), RespResp>;
impl crate::Sign for CallbackRespBody {
    fn sign(&mut self, _: &Config) {}
}
impl crate::Verify for CallbackReqBody {}
