pub async fn init_route() -> axum::Router {
    axum::Router::new()
        .route("/pay", axum::routing::post(pay::route))
        .route("/search", axum::routing::post(search::route))
        .route("/balance", axum::routing::post(balance::route))
        .route("/callback", axum::routing::post(callback::route))
}

#[derive(sqlx::FromRow, Debug)]
struct MobileHomeInfo {
    operator_code: Option<String>,
}

/// 盛大下单
pub(crate) mod pay {
    use super::MobileHomeInfo;
    use anyhow::Context;
    use axum::Extension;
    use sqlx::{AnyPool, Arguments};
    use std::sync::Arc;
    use tracing::Instrument;

    use crate::{backend, extract::SdRespExtract, packageid_map, sd_model};

    #[tracing::instrument(name="HandlerPay", ret(Debug), fields(privateKey=config.sd_config.private_key, Request=?data), skip_all )]
    pub(crate) async fn route(
        Extension(session): Extension<reqwest::Client>,
        Extension(config): Extension<Arc<crate::config::Config>>,
        Extension(pool): Extension<AnyPool>,
        data: crate::extract::SdReqExtract<sd_model::PayReqBody>,
    ) -> SdRespExtract<sd_model::PayRespBody> {
        let crate::extract::SdReqExtract { head, mut data } = data;
        if let Some(s) = packageid_map::MAP.get(data.product_no.as_str()) {
            if s.starts_with("YD") || s.starts_with("LT") || s.starts_with("DX") {
                data.product_no = s.to_string();
            } else {
                let mut conn = match pool.acquire().await {
                    Ok(o) => o,
                    Err(e) => {
                        let data = sd_model::PubRespBody::error(
                            "99",
                            format!("请求后端服务器错误: {:#?}", e),
                        );
                        return SdRespExtract {
                            data,
                            request_id: head.request_id,
                            config,
                        };
                    }
                };
                let mut arg = sqlx::any::AnyArguments::default();
                arg.add(&data.game_user_id[..7]);
                let r: Result<MobileHomeInfo, sqlx::Error> = sqlx::query_as_with(
                    "SELECT operator_code FROM mobile_home_info WHERE mobile_no=?",
                    arg,
                )
                .fetch_one(&mut conn)
                .await
                .map_err(|err| {
                    tracing::error!(Error = ?err, "Query Error");
                    err
                });

                if let Ok(MobileHomeInfo {
                    operator_code: Some(operator_code),
                }) = r
                {
                    tracing::debug!(
                        phone = data.game_user_id,
                        arg = data.game_user_id,
                        "#################"
                    );
                    data.product_no = format!("{}HF{}", operator_code, s);
                };
            }
        }
        let backend_req = data.into_backend_pay_req(&head, &config);
        // 请求服务器
        let backend_resp = match session
            .post(config.fscg_config.pay_url.clone())
            .json(&backend_req)
            .send()
            .instrument(tracing::info_span!("BackendClient", BackendRequest=?backend_req,url=%config.fscg_config.pay_url))
            .await
            .context("链接")
        {
            Ok(o) => o,
            Err(e) => {
                let response = sd_model::PubRespBody::error("99", format!("请求后端服务器错误: {:#?}", e));
                tracing::error!(
                    url=%config.fscg_config.pay_url,
                    BackendRequest=?backend_req,
                    Response=?response,
                    "{}", response.result_desc
                );

                return SdRespExtract{ data: response, request_id: head.request_id, config };
            }
        };
        // Http 状态检测
        let status = backend_resp.status();
        if !status.is_success() {
            let response =
                sd_model::PubRespBody::error("99", format!("Backend Http Status: {}", status));
            let data = serde_json::to_string(&response).unwrap();
            tracing::error!(
                BackendStatus = %status,
                Response = data,
                "Backend Http Status Error"
            );
            return SdRespExtract {
                data: response,
                request_id: head.request_id,
                config,
            };
        }

        // 读取数据
        let backend_data = match backend_resp
            .text()
            .instrument(tracing::info_span!("BackendRead"))
            .await
        {
            Ok(o) => o,
            Err(e) => {
                let response =
                    sd_model::PubRespBody::error("99", format!("读取Response: {:#?}", e));
                // let data = serde_json::to_string(&response).unwrap();
                tracing::error!(Response = ?response, "{}", response.result_desc);
                return SdRespExtract {
                    data: response,
                    request_id: head.request_id,
                    config,
                };
            }
        };

        // 解析后端数据
        let resp = match serde_json::from_str::<backend::Body<backend::PayRespBody>>(&backend_data)
        {
            Ok(o) => o,
            Err(e) => {
                let response = sd_model::PubRespBody::error("99", format!("解析JSON错误: {:?}", e));
                tracing::error!(
                    BackendResponse = backend_data,
                    Response = ?response,
                    "{}",
                    response.result_desc
                );
                return SdRespExtract {
                    data: response,
                    request_id: head.request_id,
                    config,
                };
            }
        };

        let result = sd_model::PayRespBody::from_backend_pay_resp(&config, resp, &data);
        SdRespExtract {
            data: result,
            request_id: head.request_id,
            config,
        }
    }
}

/// 盛大订单查询
pub(crate) mod search {
    use std::sync::Arc;

    use anyhow::Context;
    use axum::Extension;
    use tracing::Instrument;

    use crate::{backend, sd_model};

    #[tracing::instrument(name="HandlerSearch", ret(Debug), fields(privateKey=config.sd_config.private_key, Request=?data), skip_all )]
    pub async fn route(
        Extension(session): Extension<reqwest::Client>,
        Extension(config): Extension<Arc<crate::config::Config>>,
        data: crate::extract::SdReqExtract<sd_model::SearchReqBody>,
    ) -> crate::extract::SdRespExtract<sd_model::SearchRespBody> {
        let crate::extract::SdReqExtract { head, data } = data;
        let backend_req = data.into_backend_search_req(&head, &config);

        let backend_resp = match session
            .post(config.fscg_config.search_url.clone())
            .json(&backend_req)
            .send()
            .instrument(tracing::info_span!("BackendClient", BackendRequest=?backend_req,url=%config.fscg_config.search_url))
            .await
            .context("链接")
        {
            Ok(o) => o,
            Err(e) => {
                let response = sd_model::SearchRespBody::error("99", format!("请求后端服务器错误: {:#?}", e));
                tracing::error!(
                    url=%config.fscg_config.pay_url,
                    BackendRequest=?backend_req,
                    Response=?response,
                    "{}", response.result_desc
                );
                return crate::extract::SdRespExtract{ data: response, request_id: head.request_id, config };
            }
        };

        let status = backend_resp.status();
        if !status.is_success() {
            let response =
                sd_model::SearchRespBody::error("99", format!("Backend Http Status: {}", status));
            tracing::error!(
                BackendStatus = %status,
                Response = ?response,
                "Backend Http Status Error"
            );
            return crate::extract::SdRespExtract {
                data: response,
                request_id: head.request_id,
                config,
            };
        }

        let backend_data = match backend_resp
            .text()
            .instrument(tracing::info_span!("BackendRead"))
            .await
        {
            Ok(o) => o,
            Err(e) => {
                let response =
                    sd_model::SearchRespBody::error("99", format!("读取Response: {:#?}", e));
                tracing::error!(Response = ?response, "{}", response.result_desc);
                return crate::extract::SdRespExtract {
                    data: response,
                    request_id: head.request_id,
                    config,
                };
            }
        };

        let resp =
            match serde_json::from_str::<backend::Body<backend::SearchRespBody>>(&backend_data) {
                Ok(o) => o,
                Err(e) => {
                    let response =
                        sd_model::SearchRespBody::error("99", format!("解析JSON错误: {:?}", e));
                    tracing::error!(
                        BackendResponse = backend_data,
                        Response = ?response,
                        "{}",
                        response.result_desc
                    );
                    return crate::extract::SdRespExtract {
                        data: response,
                        request_id: head.request_id,
                        config,
                    };
                }
            };

        let response = sd_model::SearchRespBody::from_backend_search_resp(&config, resp);

        crate::extract::SdRespExtract {
            data: response,
            request_id: head.request_id,
            config,
        }
    }
}
/// 盛大余额查询
pub(crate) mod balance {
    use std::sync::Arc;

    use anyhow::Context;
    use axum::Extension;
    use tracing::Instrument;

    use crate::{backend, sd_model};

    #[tracing::instrument(name="HandlerBalance", ret(Debug), fields(privateKey=config.sd_config.private_key, Request=?data), skip_all )]
    pub async fn route(
        Extension(session): Extension<reqwest::Client>,
        Extension(config): Extension<Arc<crate::config::Config>>,
        data: crate::extract::SdReqExtract<sd_model::BalanceReqBody>,
    ) -> crate::extract::SdRespExtract<sd_model::BalanceRespBody> {
        let crate::extract::SdReqExtract { head, data } = data;

        let backend_req = data.into_backend_account_req(&config, &head);

        let backend_resp = match session
            .post(config.fscg_config.balance_url.clone())
            .json(&backend_req)
            .send()
            .instrument(tracing::info_span!("BackendClient", BackendRequest=?backend_req,url=%config.fscg_config.balance_url))
            .await
            .context("链接")
        {
            Ok(o) => o,
            Err(e) => {
                let response = sd_model::BalanceRespBody::error("99", format!("请求后端服务器错误: {:#?}", e));
                tracing::error!(
                    url=%config.fscg_config.pay_url,
                    BackendRequest=?backend_req,
                    Response=?response,
                    "{}", response.result_desc
                );
                return crate::extract::SdRespExtract{ data: response, request_id: head.request_id, config };
            }
        };

        let status = backend_resp.status();
        if !status.is_success() {
            let response =
                sd_model::BalanceRespBody::error("99", format!("Backend Http Status: {}", status));
            tracing::error!(
                BackendStatus = %status,
                Response = ?response,
                "Backend Http Status Error"
            );
            return crate::extract::SdRespExtract {
                data: response,
                request_id: head.request_id,
                config,
            };
        }

        let backend_data = match backend_resp
            .text()
            .instrument(tracing::info_span!("BackendRead"))
            .await
        {
            Ok(o) => o,
            Err(e) => {
                let response =
                    sd_model::BalanceRespBody::error("99", format!("读取Response: {:#?}", e));
                tracing::error!(Response = ?response, "{}", response.result_desc);
                return crate::extract::SdRespExtract {
                    data: response,
                    request_id: head.request_id,
                    config,
                };
            }
        };

        let resp =
            match serde_json::from_str::<backend::Body<backend::AccountRespBody>>(&backend_data) {
                Ok(o) => o,
                Err(e) => {
                    let response =
                        sd_model::BalanceRespBody::error("99", format!("解析JSON错误: {:?}", e));
                    tracing::error!(
                        BackendResponse = backend_data,
                        Response = ?response,
                        "{}",
                        response.result_desc
                    );
                    return crate::extract::SdRespExtract {
                        data: response,
                        request_id: head.request_id,
                        config,
                    };
                }
            };
        let response = sd_model::BalanceRespBody::from_backend_account_resp(&config, resp);
        crate::extract::SdRespExtract {
            data: response,
            request_id: head.request_id,
            config,
        }
    }
}

/// FSCG 回调
pub(crate) mod callback {
    use std::{fmt::Display, sync::Arc};

    use anyhow::Context;
    use axum::{Extension, Json};
    use tracing::Instrument;

    use crate::{backend, config::Config, sd_model, Verify};

    #[tracing::instrument(name="HandlerCallback", ret(Debug), fields(Request=?req), skip_all )]

    pub async fn route(
        Extension(session): Extension<reqwest::Client>,
        Extension(config): Extension<Arc<crate::config::Config>>,
        Json(req): Json<backend::Body<backend::CallbackReqBody>>,
    ) -> Json<backend::Body<backend::CallbackRespBody>> {
        if let Err(e) = req.verify(&config) {
            return Json(err_resp(e, "07", "鉴权信息错误", req, &config));
        }

        let callback_req = match to_sd_callback_req(
            &config,
            req.header.seqno.clone(),
            req.msgbody.content.as_ref().unwrap(),
        ) {
            Ok(o) => o,
            Err(e) => {
                return Json(err_resp(e, "99", "转换错误", req, &config));
            }
        };

        let callback_resp= match session.post(config.sd_config.callback_url.clone())
                .json(&callback_req)
                .send()
                .instrument(tracing::info_span!("CallbackClient",CallbackRequest=?callback_req,url=%config.sd_config.callback_url))
                .await
                .context("连接") {
            Ok(o) => {o},
            Err(e) => {
                return Json(err_resp(e, "99", "请求错误", req, &config));
            },
        };

        let callback_data = match callback_resp
            .text()
            .instrument(tracing::info_span!("BackendRead"))
            .await
            .context("读取信息")
        {
            Ok(o) => o,
            Err(e) => {
                return Json(err_resp(e, "99", "请求错误", req, &config));
            }
        };
        tracing::info!(callback_data, "盛大返回");
        match serde_json::from_str::<sd_model::PubRespHead>(&callback_data)
            .context("serde_json::from_str::<sd_model::PubRespHead>")
            .and_then(|o| {
                if o.code != "0000" {
                    Err(anyhow::Error::msg(format!(
                        "PubRespHead: {}",
                        callback_data
                    )))
                } else {
                    let r = config.sd_config.get_sk().context("获取私钥")?;
                    let data = hex::decode(&o.data).context("回调HEX读取")?;
                    let s = r
                        .decrypt(data, crate::gmssl::sm2::PassOrder::C1C3C2)
                        .context("回调解密")?;
                    serde_json::from_slice(&s).context("解析JSON CallbackRespBody")
                }
            }) {
            Ok(o) => Json(to_backend_callback_resp(o, req)),
            Err(e) => Json(err_resp(e, "99", "请求错误", req, &config)),
        }
    }

    fn to_backend_callback_resp(
        result: sd_model::CallbackRespBody,
        req: backend::Body<backend::CallbackReqBody>,
    ) -> backend::Body<backend::CallbackRespBody> {
        let rcode = if result.result_code == "0000" {
            "00"
        } else {
            &result.result_code
        };

        backend::Body {
            header: req.header,
            msgbody: backend::CallbackRespBody {
                content: None,
                resp: Some(backend::RespResp {
                    rcode: rcode.to_string(),
                    rmsg: result.result_desc.into(),
                }),
            },
        }
    }

    fn to_sd_callback_req(
        config: &Config,
        request_id: String,
        req: &backend::CallbackReqContent,
    ) -> anyhow::Result<sd_model::PubReqHead> {
        let mut result = sd_model::PubReqHead {
            app_id: config.sd_config.app_id.clone(),
            method: "sd.tovc.callback".to_string(),
            request_id,
            ..Default::default()
        };

        let state = if req.code == "00" {
            "1"
        } else if req.code == "99" {
            "9"
        } else {
            "0"
        };

        let data = sd_model::CallbackReqBody {
            state: state.to_string(),
            message: req.status.clone(),
            order_id: req.orderid.clone(),
            sp_order_id: req.extorder.clone(),
            operator_order: req.attr1.clone(),
            ..Default::default()
        };
        if let Err(e) = result.encrypt_sign(config, data) {
            return Err(anyhow::Error::msg(format!("转换加密错误 {:#?}", e)));
        }

        Ok(result)
    }

    #[allow(unused, clippy::let_and_return)]
    fn err_resp(
        e: anyhow::Error,
        code: impl ToString,
        msg: impl Display,
        req: backend::Body<backend::CallbackReqBody>,
        config: &Config,
    ) -> backend::Body<backend::CallbackRespBody> {
        let mut result = backend::Body {
            header: req.header,
            msgbody: backend::CallbackRespBody {
                content: None,
                resp: Some(backend::RespResp {
                    rcode: code.to_string(),
                    rmsg: Some(format!("{} {:#?}", msg, e)),
                }),
            },
        };
        // result.sign(config);
        result
    }
}

#[cfg(test)]
mod tests {
    use sqlx::Arguments;

    use crate::{config, sd_model::PubReqHead};

    use super::*;

    #[test]
    fn feature() {
        let a = PubReqHead {
             app_id: "cf55124007014dd18674eeec09c5356f1679972467894".to_string(),
              method: "sd.tovc.callback".to_string(),
               sign_type: "md5".to_string(),
                sign: "127453E347ABE79D6F4D323EC203F0F0".to_string(),
                 timestamp: "2023-04-19 15:33:01".to_string(),
                  version: "1.0".to_string(),
                   request_id: "CP1681888200006".to_string(), 
                   biz_content: "04640cc6329b075b65440e42d6f285115fdab30637cc324e61fbdaced9819ff03afc441948436a3fac9b290579298b5015bccf7dc415aba93feab17f5e51d246d532cf0ed466d919eae552a4e0112a951fa83acca9390e65c1d51d4b4681fcf2dcf8394504bb65952e75892473dfb389b5bd0e76fa8364cb6ed83cf749f5d175a6c2557e3579bed85115ea7cbe534ae672665b7a6e147fa3cd8f37a313e6571e76c2c449ca4f5931bbed7ac615428457923e887c9a59a5a24b45c41af2b583c29c9629753cd8f47fb03b9fd900ab0b8b92ec37c4a938bbb4fb9e59b904d0e2b920f9c29bf8d16165febec130c17b5c57947d6b98cab5c8536dc52744fe59dd2f94ba4cb050c3a7af40a22d1894c2976a3512f3f5b144825885d663".to_string() };
        println!("{:?}", serde_json::to_string(&a))
    }

    #[tokio::test]
    async fn test_name() {
        let config = crate::load_config();
        let pool = sqlx::any::AnyPoolOptions::new()
            .connect(&config.date_source)
            .await
            .unwrap();
        let mut conn = pool.acquire().await.unwrap();
        let s = "13524192990";
        let mut arg = sqlx::any::AnyArguments::default();
        arg.add(&s[..7]);
        let r: Result<MobileHomeInfo, sqlx::Error> = sqlx::query_as_with(
            "SELECT operator_code FROM mobile_home_info WHERE mobile_no=?",
            arg,
        )
        .fetch_one(&mut conn)
        .await;
        if let Ok(MobileHomeInfo {
            operator_code: Some(operator_code),
        }) = r
        {
            println!("{:?}", operator_code);
        };
        // println!("{:?}", r.ok())
    }
}
