package cn.iocoder.byzs.module.system.framework.sms.core.client.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.iocoder.byzs.framework.common.core.KeyValue;
import cn.iocoder.byzs.framework.common.util.http.HttpUtils;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.TreeMap;
import cn.iocoder.byzs.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
import cn.iocoder.byzs.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.byzs.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.byzs.module.system.framework.sms.core.property.SmsChannelProperties;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class SubmailSmsClient extends AbstractSmsClient {

    private static final String API_URL_XSEND = "https://api.mysubmail.com/message/xsend";
    private static final String API_URL_QUERY = "https://api.mysubmail.com/message/query";
    private static final String API_URL_TEMPLATE_GET = "https://api-v4.mysubmail.com/sms/template";
    private static final String TIMESTAMP_URL = "https://api-v4.mysubmail.com/service/timestamp";
    private static final String SIGN_TYPE_MD5 = "md5";
    private static final String NORMAL = "normal";

    public SubmailSmsClient(SmsChannelProperties properties) {
        super(properties);
        Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
        Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
    }

    @Override
    public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
                                   List<KeyValue<String, Object>> templateParams) throws Throwable {
        StringBuilder postData = new StringBuilder();
        postData.append("appid=").append(URLEncoder.encode(properties.getApiKey(), StandardCharsets.UTF_8));
        postData.append("&signature=").append(URLEncoder.encode(properties.getApiSecret(), StandardCharsets.UTF_8));
        postData.append("&project=").append(URLEncoder.encode(apiTemplateId, StandardCharsets.UTF_8));
        postData.append("&to=").append(URLEncoder.encode(mobile, StandardCharsets.UTF_8));

        if (ObjectUtil.isNotEmpty(properties.getSignature())) {
            postData.append("&sms_signature=").append(URLEncoder.encode(properties.getSignature(), StandardCharsets.UTF_8));
        }

        if (ObjectUtil.isNotEmpty(templateParams)) {
            Map<String, Object> vars = new LinkedHashMap<>();
            templateParams.forEach(param -> vars.put(param.getKey(), param.getValue()));
            postData.append("&vars=").append(URLEncoder.encode(JSONUtil.toJsonStr(vars), StandardCharsets.UTF_8));
        }

        java.util.Map<String, String> headers = new LinkedHashMap<>();
        headers.put("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
        headers.put("Accept", "application/json");

        String responseBody = HttpUtils.post(API_URL_XSEND, headers, postData.toString());
        JSONObject response = JSONUtil.parseObj(responseBody);

        String status = response.getStr("status");
        if ("success".equals(status)) {
            return new SmsSendRespDTO()
                    .setSuccess(true)
                    .setSerialNo(response.getStr("send_id"));
        }
        return new SmsSendRespDTO()
                .setSuccess(false)
                .setApiCode(response.getStr("code"))
                .setApiMsg(response.getStr("msg"));
    }

    @Override
    public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable {
        List<SmsReceiveRespDTO> results = new ArrayList<>();
        JSONObject status = JSONUtil.parseObj(text);

        String statusStr = status.getStr("status");
        if ("success".equals(statusStr)) {
            JSONObject data = status.getJSONObject("data");
            if (ObjectUtil.isNotEmpty(data)) {
                SmsReceiveRespDTO result = new SmsReceiveRespDTO()
                        .setSuccess("success".equals(data.getStr("status")))
                        .setErrorMsg(data.getStr("status"))
                        .setMobile(data.getStr("phone"))
                        .setSerialNo(data.getStr("send_id"));
                results.add(result);
            }
        }
        return results;
    }

    @Override
    public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
        TreeMap<String, String> requestData = new TreeMap<>();
        requestData.put("appid", properties.getApiKey());
        requestData.put("template_id", apiTemplateId);

        requestData = buildSignature(properties.getApiKey(), properties.getApiSecret(), NORMAL, requestData);

        StringBuilder urlBuilder = new StringBuilder(API_URL_TEMPLATE_GET);
        urlBuilder.append("?");
        for (Map.Entry<String, String> entry : requestData.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            urlBuilder.append(key).append("=").append(URLEncoder.encode(value, StandardCharsets.UTF_8)).append("&");
        }
        String url = urlBuilder.substring(0, urlBuilder.length() - 1);

        java.util.Map<String, String> headers = new LinkedHashMap<>();
        headers.put("Accept", "application/json");

        String responseBody;
        try {
            responseBody = HttpUtils.get(url, headers);
        } catch (Exception e) {
            log.warn("[getSmsTemplate][模板编号({}) 请求异常: {}]", apiTemplateId, e.getMessage());
            return null;
        }

        if (ObjectUtil.isEmpty(responseBody)) {
            log.warn("[getSmsTemplate][模板编号({}) 响应为空]", apiTemplateId);
            return null;
        }

        String trimmedBody = responseBody.trim();
        if (!trimmedBody.startsWith("{")) {
            log.warn("[getSmsTemplate][模板编号({}) 响应不是有效的 JSON，响应内容: {}", apiTemplateId, trimmedBody);
            return null;
        }

        JSONObject response;
        try {
            response = JSONUtil.parseObj(responseBody);
        } catch (Exception e) {
            log.warn("[getSmsTemplate][模板编号({}) 响应 JSON 解析失败，错误: {}，响应内容: {}", apiTemplateId, e.getMessage(), responseBody);
            return null;
        }

        if (response.containsKey("status") && "success".equals(response.getStr("status"))) {
            JSONObject data = response.getJSONObject("template");
            if (ObjectUtil.isNotEmpty(data)) {
                return new SmsTemplateRespDTO()
                        .setId(data.getStr("template_id"))
                        .setContent(data.getStr("sms_content"))
                        .setAuditStatus(Integer.valueOf(data.getStr("template_status")));
            }
        } else {
            log.warn("[getSmsTemplate][模板编号({}) 查询失败，响应: {}", apiTemplateId, responseBody);
        }
        return null;
    }

    private String getTimestamp() {
        try {
            String responseBody = HttpUtils.get(TIMESTAMP_URL, new LinkedHashMap<>());
            if (ObjectUtil.isNotEmpty(responseBody)) {
                JSONObject json = JSONUtil.parseObj(responseBody);
                return json.getStr("timestamp");
            }
        } catch (Exception e) {
            log.warn("[getTimestamp][获取时间戳异常: {}]", e.getMessage());
        }
        return null;
    }

    private TreeMap<String, String> buildSignature(String appid, String appkey, String signType, TreeMap<String, String> requestData) {
        String timestamp = getTimestamp();
        if (StrUtil.isEmpty(timestamp)) {
            log.warn("[buildSignature][获取时间戳失败]");
            return requestData;
        }
        requestData.put("timestamp", timestamp);
        requestData.put("sign_type", signType);

        StringBuilder signStr = new StringBuilder();
        signStr.append(appid).append(appkey);
        for (Map.Entry<String, String> entry : requestData.entrySet()) {
            signStr.append(entry.getKey()).append("=").append(entry.getValue());
        }
        signStr.append(appid).append(appkey);

        String signature;
        if (SIGN_TYPE_MD5.equals(signType)) {
            signature = DigestUtil.md5Hex(signStr.toString(), StandardCharsets.UTF_8);
        } else {
            signature = appkey;
        }
        requestData.put("signature", signature);
        return requestData;
    }
}