Documentation
use chrono::DateTime;
use crate::wechat::Wechat;
use json::{object, JsonValue};

pub mod alipay;
pub mod wechat;

#[derive(Clone)]
pub enum Pay {
    Wechat(Wechat),
    Alipay,
}

impl Pay {
    pub fn new(data: JsonValue) -> Self {
        match data["mode"].as_str().unwrap_or("") {
            "wechat" => Pay::Wechat(Wechat {
                appid: data["appid"].to_string(),
                mchid: data["mchid"].to_string(),
                serial_no: data["serial_no"].to_string(),
                cert_path: data["cert_path"].to_string(),
                secret: data["secret"].to_string(),
                apikey: data["apikey"].to_string(),
            }),
            _ => Pay::Alipay,
        }
    }
}
impl PayMode for Pay {
    fn login(&self, code: &str) -> Result<JsonValue, String> {
        match self {
            Self::Wechat(e) => e.login(code),
            Self::Alipay => Err("Alipay alipay".to_string()),
        }
    }

    /// jsapi 支付
    fn jsapi(
        &mut self,
        sub_mchid: &str,
        out_trade_no: &str,
        description: &str,
        total_fee: f64,
        notify_url: &str,
        sp_openid: &str,
    ) -> Result<JsonValue, String> {
        match self {
            Self::Wechat(e) => e.jsapi(
                sub_mchid,
                out_trade_no,
                description,
                total_fee,
                notify_url,
                sp_openid,
            ),
            Self::Alipay => Err("Alipay alipay".to_string()),
        }
    }

    fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
        match self {
            Self::Wechat(e) => e.pay_query(
                out_trade_no,
                sub_mchid,
            ),
            Self::Alipay => Err("Alipay alipay".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> {
        match self {
            Self::Wechat(e) => e.refund(
                sub_mchid,
                out_trade_no,
                transaction_id,
                out_refund_no,
                amount,
                total,
                currency,
            ),
            Self::Alipay => Err("Alipay alipay".to_string()),
        }
    }

    fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
        match self {
            Self::Wechat(e) => e.pay_notify(
                nonce,
                ciphertext,
                associated_data,
            ),
            Self::Alipay => Err("Alipay alipay".to_string()),
        }
    }

    fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String> {
        match self {
            Self::Wechat(e) => e.refund_notify(
                nonce,
                ciphertext,
                associated_data,
            ),
            Self::Alipay => Err("Alipay alipay".to_string()),
        }
    }

    fn refund_query(&mut self, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String> {
        match self {
            Self::Wechat(e) => e.refund_query(
                out_refund_no,
                sub_mchid,
            ),
            Self::Alipay => Err("Alipay alipay".to_string()),
        }
    }
}

pub trait PayMode {
    fn login(&self, code: &str) -> Result<JsonValue, String>;
    /// JSAPI 支付
    /// * out_trade_no 商户订单号
    /// * sub_mchid 子商户号
    /// * description 商品名称
    /// * total_fee 支付金额
    /// * notify_url 通知地址
    /// * sp_openid 支付客户ID
    fn jsapi(&mut self, sub_mchid: &str, out_trade_no: &str, description: &str, total_fee: f64, notify_url: &str, sp_openid: &str) -> Result<JsonValue, String>;
    /// 订单查询
    /// * out_trade_no 商户订单号
    /// * sub_mchid 子商户号
    fn pay_query(&mut self, out_trade_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
    /// 支付成功通知
    fn pay_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;

    #[allow(clippy::too_many_arguments)]
    /// 订单退款
    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>;
    /// 退款通知
    fn refund_notify(&mut self, nonce: &str, ciphertext: &str, associated_data: &str) -> Result<JsonValue, String>;
    /// 退款查询
    fn refund_query(&mut self, out_refund_no: &str, sub_mchid: &str) -> Result<JsonValue, String>;
}

/// 支付回调数据
#[derive(Debug)]
pub struct PayNotify {
    /// 支付方式
    trade_type: TradeType,
    /// 商户订单号
    out_trade_no: String,
    /// 服务商商户号
    sp_mchid: String,
    /// 子商户号
    sub_mchid: String,
    /// 应用APPID
    sp_appid: String,
    /// 微信内部订单号
    transaction_id: String,
    /// 成功时间
    success_time: i64,
    /// 服务商用户ID
    sp_openid: String,
    /// 子商户用户ID
    sub_openid: String,
    /// 支付金额
    total: usize,
    /// 实际支付金额
    payer_total: usize,
    /// 币种
    currency: String,
    /// 实际支付币种
    payer_currency: String,
    /// 状态
    trade_state: TradeState,
}
impl PayNotify {
    pub fn json(&self) -> JsonValue {
        object! {
            "trade_type" => self.trade_type.to_string(),
            "out_trade_no" => self.out_trade_no.to_string(),
            "sp_mchid" => self.sp_mchid.to_string(),
            "sub_mchid" => self.sub_mchid.to_string(),
            "sp_appid" => self.sp_appid.to_string(),
            "transaction_id" => self.transaction_id.to_string(),
            "success_time" => self.success_time,
            "sp_openid" => self.sp_openid.to_string(),
            "sub_openid" => self.sub_openid.to_string(),
            "total" => self.total,
            "payer_total" => self.payer_total,
            "currency" => self.currency.clone(),
            "payer_currency" => self.payer_currency.clone(),
            "trade_state" => self.trade_state.to_string(),
        }
    }
    pub fn success_time(datetime: &str) -> i64 {
        if datetime.is_empty() {
            return 0;
        }
        let datetime = DateTime::parse_from_rfc3339(datetime).unwrap();
        datetime.timestamp()
    }
}
#[derive(Debug)]
pub enum TradeType {
    /// 小程序与JSAPI
    JSAPI,
    None,
}
impl TradeType {
    fn from(code: &str) -> TradeType {
        match code {
            "JSAPI" => TradeType::JSAPI,
            _ => TradeType::None
        }
    }
    fn to_string(&self) -> &'static str {
        match self {
            TradeType::JSAPI => "JSAPI",
            TradeType::None => ""
        }
    }
}
#[derive(Debug)]
pub enum TradeState {
    /// 支付成功
    SUCCESS,
    None,
}
impl TradeState {
    fn from(code: &str) -> TradeState {
        match code {
            "SUCCESS" => TradeState::SUCCESS,
            _ => TradeState::None
        }
    }
    fn to_string(&self) -> &'static str {
        match self {
            TradeState::SUCCESS => "支付成功",
            TradeState::None => "未知"
        }
    }
}

#[derive(Debug)]
pub enum RefundStatus {
    /// 退款成功
    SUCCESS,
    None,
}
impl RefundStatus {
    fn from(code: &str) -> RefundStatus {
        match code {
            "SUCCESS" => RefundStatus::SUCCESS,
            _ => RefundStatus::None
        }
    }
    fn to_string(&self) -> &'static str {
        match self {
            RefundStatus::SUCCESS => "已退款",
            RefundStatus::None => "退款中"
        }
    }
}
/// 退款回调数据
#[derive(Debug)]
pub struct RefundNotify {
    /// 商户订单号
    out_trade_no: String,
    refund_no: String,
    /// 服务商商户号
    sp_mchid: String,
    /// 子商户号
    sub_mchid: String,
    /// 微信内部订单号
    transaction_id: String,
    /// 退款订单号
    refund_id: String,
    /// 成功时间
    success_time: i64,
    /// 支付金额
    total: f64,
    /// 退款金额
    refund: f64,
    /// 实际支付金额
    payer_total: f64,
    /// 实际退款金额
    payer_refund: f64,
    /// 状态
    refund_status: RefundStatus,
}

impl RefundNotify {
    pub fn json(&self) -> JsonValue {
        object! {
            "out_trade_no" => self.out_trade_no.clone(),
            "sp_mchid" => self.sp_mchid.clone(),
            "sub_mchid" => self.sub_mchid.clone(),
            "transaction_id" => self.transaction_id.clone(),
            "success_time" => self.success_time,
            "total" => self.total,
            "refund" => self.refund,
            "payer_total" => self.payer_total,
            "payer_refund" => self.payer_refund,
            "refund_status" => self.refund_status.to_string(),
            "refund_no"=>self.refund_no.clone(),
            "refund_id"=>self.refund_id.clone(),
        }
    }
}