package org.springblade.modules.resource.template;

import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson.JSONObject;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.core.sms.SmsTemplate;
import org.springblade.core.sms.model.SmsCode;
import org.springblade.core.sms.model.SmsData;
import org.springblade.core.sms.model.SmsResponse;
import org.springblade.core.sms.props.SmsProperties;
import org.springblade.core.tool.utils.StringUtil;
import org.springframework.http.HttpStatus;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.*;

/**
 * Author pangqijun
 * Date 2023/5/15
 * Description
 */
public class HuaweiSmsTemplate implements SmsTemplate {

    //无需修改,用于格式化鉴权头域,给"X-WSSE"参数赋值
    private static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
    //无需修改,用于格式化鉴权头域,给"Authorization"参数赋值
    private static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";

    private final String apiUrl;
    private final String sender;
    private final SmsProperties smsProperties;
    private final BladeRedis bladeRedis;

    public HuaweiSmsTemplate(String apiUrl, String sender, SmsProperties smsProperties, BladeRedis bladeRedis) {
        this.apiUrl = apiUrl;
        this.sender = sender;
        this.smsProperties = smsProperties;
        this.bladeRedis = bladeRedis;
    }

    @Override
    public SmsResponse sendMessage(SmsData smsData, Collection<String> phones) {
        try {
            Map<String, String> body = this.buildRequestBody(toTel(phones), smsData.getParams().get(smsData.getKey()));
            String wsseHeader = this.buildWsseHeader(smsProperties.getAccessKey(), smsProperties.getSecretKey());
            String result = HttpRequest.post(apiUrl + "/sms/batchSendSms/v1")
                    .header("X-WSSE", wsseHeader)
                    .header("Authorization", AUTH_HEADER_VALUE)
                    .formStr(body)
                    .execute().body();
            JSONObject jsonObject = JSONObject.parseObject(result);
            String code = jsonObject.getString("code");
            if (!"000000".equals(code)) {
                throw new ServiceException("发送失败_" + code);
            }
            return new SmsResponse(true, 200, "success");
        } catch (Exception e) {
            e.printStackTrace();
            return new SmsResponse(Boolean.FALSE, HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }

    @Override
    public SmsCode sendValidate(SmsData smsData, String phone) {
        SmsCode smsCode = new SmsCode();
        boolean temp = this.sendSingle(smsData, phone);
        if (temp && StringUtil.isNotBlank(smsData.getKey())) {
            String id = StringUtil.randomUUID();
            String value = (String)smsData.getParams().get(smsData.getKey());
            this.bladeRedis.setEx(this.cacheKey(phone, id), value, Duration.ofMinutes(30L));
            smsCode.setId(id).setValue(value);
        } else {
            smsCode.setSuccess(Boolean.FALSE);
        }

        return smsCode;
    }

    @Override
    public boolean validateMessage(SmsCode smsCode) {
        String id = smsCode.getId();
        String value = smsCode.getValue();
        String phone = smsCode.getPhone();
        String cache = (String)this.bladeRedis.get(this.cacheKey(phone, id));
        if (StringUtil.isNotBlank(value) && StringUtil.equalsIgnoreCase(cache, value)) {
            this.bladeRedis.del(this.cacheKey(phone, id));
            return true;
        } else {
            return false;
        }
    }

    private Map<String, String> buildRequestBody(String receiver, String code) {
        Map<String, String> map = new HashMap<>();
        map.put("from", sender);
        map.put("to", receiver);
        map.put("templateId", smsProperties.getTemplateId());
        map.put("signature", smsProperties.getSignName());
        map.put("templateParas", "[\"" + code + "\"]");
        return map;
    }

    private String buildWsseHeader(String appKey, String appSecret) {
        if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
            throw new ServiceException("buildWsseHeader(): appKey or appSecret is null.");
        }
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        String time = sdf.format(new Date()); //Created
        String nonce = UUID.randomUUID().toString().replace("-", ""); //Nonce

        MessageDigest md;
        byte[] passwordDigest = null;

        try {
            md = MessageDigest.getInstance("SHA-256");
            md.update((nonce + time + appSecret).getBytes());
            passwordDigest = md.digest();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest);
        return String.format(WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
    }

    private String toTel(Collection<String> phones) {
        List<String> temp = new ArrayList<>();
        for (String phone : phones) {
            temp.add("+86" + phone);
        }
        return StringUtil.join(temp);
    }
}
