#![allow(dead_code)]
use std::borrow::Cow;

use crate::{
    CodeMessage, IntoResponse, JsonResponseWrap, QueryValue, Request, Result,
    auth::{AccessKeySecret, Acs3HmacSha256},
};
use serde::Deserialize;

#[derive(Clone, Copy)]
pub enum Endpoint {
    CnShanghaiFinance1,
    NaSouth1,
}

impl From<Endpoint> for &'static str {
    fn from(value: Endpoint) -> Self {
        match value {
            Endpoint::CnShanghaiFinance1 => "ecs.cn-shanghai-finance-1.aliyuncs.com",
            Endpoint::NaSouth1 => "ecs.na-south-1.aliyuncs.com",
        }
    }
}

mod sealed {
    /// prevent Request type used with Connection of other mod.
    pub trait Bound {}
}

#[derive(Clone)]
pub struct Connection(crate::common::Connection<Acs3HmacSha256>);

impl Connection {
    pub fn new(endpoint: Endpoint, app_key_secret: AccessKeySecret) -> Self {
        Self(crate::common::Connection::new(
            Acs3HmacSha256(app_key_secret),
            "2020-01-01",
            endpoint.into(),
        ))
    }

    fn call<R: Request + sealed::Bound>(
        &self,
        req: R,
    ) -> impl Future<Output = Result<<R::ResponseWrap as IntoResponse>::Response>> + Send {
        self.0.call(req)
    }

    pub fn send_sms(&self, req: SendSms) -> impl Future<Output = Result<SendSmsResponse>> + Send {
        self.call(req)
    }
}

#[derive(derive_setters::Setters, Debug)]
#[setters(generate = false)]
pub struct SendSms {
    phone_numbers: String,
    sign_name: String,
    template_code: String,
    #[setters(generate = true, strip_option)]
    template_param: Option<String>,
    #[setters(generate = true, strip_option)]
    sms_up_extend_code: Option<String>,
    #[setters(generate = true, strip_option)]
    out_id: Option<String>,
}

impl sealed::Bound for SendSms {}

impl SendSms {
    pub fn new(
        phone_numbers: impl Into<String>,
        sign_name: impl Into<String>,
        template_code: impl Into<String>,
    ) -> Self {
        SendSms {
            phone_numbers: phone_numbers.into(),
            sign_name: sign_name.into(),
            template_code: template_code.into(),
            template_param: None,
            sms_up_extend_code: None,
            out_id: None,
        }
    }
}

impl Request for SendSms {
    const METHOD: http::Method = http::Method::GET;

    const ACTION: &'static str = "SendSms";

    type Body = ();

    type ResponseWrap = JsonResponseWrap<SendSmsResponse>;

    fn to_query_params(&self) -> Vec<(Cow<'static, str>, QueryValue<'_>)> {
        let mut params = Vec::with_capacity(6);
        params.push(("PhoneNumbers".into(), (&self.phone_numbers).into()));
        params.push(("SignName".into(), (&self.sign_name).into()));
        params.push(("TemplateCode".into(), (&self.template_code).into()));
        if let Some(f) = &self.out_id {
            params.push(("OutId".into(), f.into()));
        }
        if let Some(f) = &self.sms_up_extend_code {
            params.push(("SmsUpExtendCode".into(), f.into()));
        }
        if let Some(f) = &self.template_param {
            params.push(("TemplateParam".into(), f.into()));
        }
        params
    }

    fn to_body(self) -> Self::Body {
        todo!();
    }
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct SendSmsResponse {
    #[serde(flatten)]
    code_message: CodeMessage,
    biz_id: String,
    request_id: String,
}

impl crate::ToCodeMessage for SendSmsResponse {
    fn to_code_message(&self) -> &crate::CodeMessage {
        &self.code_message
    }
}
