use std::sync::Arc;

use anyhow::Context;
use axum::{
    body::HttpBody,
    extract::{FromRequest, FromRequestParts},
    response::IntoResponse,
    BoxError, Json,
};
use hyper::Request;
use serde::{de::DeserializeOwned, Serialize};

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

/// 盛大请求 验证,解密
#[derive(Debug)]
pub struct SdReqExtract<T> {
    pub head: sd_model::PubReqHead,
    pub data: T,
}

#[axum::async_trait]

impl<T, S, B> FromRequest<S, B> for SdReqExtract<T>
where
    T: DeserializeOwned,
    T: sd_model::TryFromPubHeadConfig + crate::Verify,
    B: HttpBody + Send + 'static,
    B::Data: Send,
    B::Error: Into<BoxError>,
    S: Send + Sync,
{
    type Rejection = sd_model::PubRespHead;

    #[tracing::instrument(skip_all, name = "SdReqExtract")]
    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
        let (mut parts, body) = req.into_parts();

        let mut err_result = sd_model::PubRespHead {
            code: "999".to_string(),
            msg: "解析Request头异常".to_string(),
            ..Default::default()
        };

        let config = axum::Extension::<Arc<Config>>::from_request_parts(&mut parts, state).await;
        let config = match config {
            Ok(o) => o,
            Err(e) => {
                let msg = format!("获取配置错误: {}", e.body_text());
                tracing::error!("{}", msg);
                err_result.msg = msg;
                return Err(err_result);
            }
        };
        let req = Request::from_parts(parts, body);

        let Json(head) = match Json::<sd_model::PubReqHead>::from_request(req, state).await {
            Ok(o) => o,
            Err(e) => {
                let msg = format!("Json 转换错误: {}", e.body_text());
                tracing::error!("{}", msg);
                err_result.msg = msg;
                return Err(err_result);
            }
        };
        tracing::info!(Request=?head,"盛大请求");
        use crate::Verify;
        if let Err(e) = head.verify(&config) {
            let e = e.context("Proxy verify PubReqHead Error");
            let msg = format!("{:#?}", e);
            tracing::error!("{}", msg);
            err_result.msg = msg;
            err_result.sign(&config);
            return Err(err_result);
        }

        let data = match T::try_from_pub_req_head_config(&head, &config) {
            Ok(o) => o,
            Err(e) => {
                let msg = format!("类型转换错误: {:#?}", e);
                tracing::error!("{}", msg);
                err_result.msg = msg;
                err_result.sign(&config);

                return Err(err_result);
            }
        };
        if let Err(e) = data.verify(&config) {
            let msg = format!("Proxy Verify Body Error: {:#?}", e);
            tracing::error!("{}", msg);
            err_result.msg = msg;
            err_result.sign(&config);

            return Err(err_result);
        }

        Ok(Self { head, data })
    }
}

/// 盛大返回, 加签,加密
#[derive(Debug)]
pub struct SdRespExtract<T> {
    pub data: T,
    pub request_id: String,
    pub config: Arc<Config>,
}

impl<T> IntoResponse for SdRespExtract<T>
where
    T: Serialize + crate::Sign,
{
    fn into_response(mut self) -> axum::response::Response {
        let mut result = sd_model::PubRespHead {
            request_id: self.request_id,
            ..Default::default()
        };
        self.data.sign(&self.config);
        let data = match serde_json::to_string(&self.data)
            .context("serde_json::to_string")
            .and_then(|data| {
                self.config
                    .sd_config
                    .get_pk()
                    .and_then(|o| o.encrypt(data, crate::gmssl::sm2::PassOrder::C1C3C2))
            }) {
            Ok(o) => hex::encode(o),
            Err(e) => {
                result.code = "999".to_string();
                result.msg = format!("{:#?}", e);
                return Json(result).into_response();
            }
        };

        result.data = data;
        result.sign(&self.config);
        Json(result).into_response()
    }
}
