package com.njcn.msgpush.module.push.client.sender.impl.sms;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.njcn.msgpush.module.push.client.sender.SendResult;
import com.njcn.msgpush.module.push.client.sender.Sender;
import com.njcn.msgpush.module.push.client.sender.SmsSender;
import com.njcn.msgpush.module.push.client.setting.impl.TelecomSmsSetting;
import com.njcn.msgpush.module.push.dal.dataobject.message.MessageRecordDO;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import static com.alibaba.fastjson.JSON.toJSON;

@Data
@Slf4j
public class TelecomSmsSender implements SmsSender {

    private static final String CONTENT_TYPE = "application/json;charset=utf-8";

    private final TelecomSmsSetting telecomSmsSetting;
    private final Sender sender;

    @Data
    private static class TelecomSmsSendResponse {
        private String status;
        private Double balance;
        private List<TelecomSmsSendDetailRes> list;
    }

    @Data
    private static class TelecomSmsSendDetailRes {
        private String mid;
        private String mobile;
        private Integer result;
    }

    @Data
    private static class TelecomSmsSelectResponse {
        private String status;
        private Double balance;
        private List<TelecomSmsSelectDetailRes> list;
    }

    @Data
    private static class TelecomSmsSelectDetailRes {
        private String apmid;
        private String apSubmitTime;
        private String mobile;
        private Integer status;
        private String stat;
        private String deliverTime;
    }

    public TelecomSmsSender(TelecomSmsSetting telecomSmsSetting, Sender sender) {
        this.telecomSmsSetting = telecomSmsSetting;
        this.sender = sender;
    }

    @Override
    public SendResult sendSms(MessageRecordDO message) {
        Future<SendResult> future = this.sender.MSG_PUSH_THREAD_POOL_EXECUTOR.submit(() -> {
            Map<String, Object> request = new HashMap<>();
            boolean isTemplateSend = StrUtil.isNotBlank(message.getTemplateCode());
            request.put("account", telecomSmsSetting.getAccount());
            request.put("password", telecomSmsSetting.getPassword());
            request.put("extno", telecomSmsSetting.getExtno());
            if (isTemplateSend) {
                request.put("action", "templatep2p");
                Map<String, Object> templateJsonMap = new HashMap<>();
                templateJsonMap.put("templateID", message.getTemplateCode());
                JSONObject jsonObject = JSON.parseObject(message.getTemplateParams());
                Map<String, String> variable = new HashMap<>();
                variable.put("mobile", message.getReceiver());
                jsonObject.forEach((key, value) -> variable.put(key, value.toString()));
                templateJsonMap.put("variable", "[" + toJSON(variable) + "]");
                request.put("templateJson", toJSON(templateJsonMap));
            } else {
                request.put("action", "send");
                request.put("mobile", message.getReceiver());
                request.put("content", message.getContent());
            }

            HttpHeaders headers = new HttpHeaders();
            headers.set("Content-Type", CONTENT_TYPE);

            try {
                LocalDateTime now = LocalDateTime.now();
                long start = now.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
                ResponseEntity<String> response = this.sender.restTemplateUtil.post(
                        telecomSmsSetting.getApiUrl(),
                        request,
                        headers,
                        String.class
                );
                LocalDateTime end = LocalDateTime.now();
                int costTime = (int) (end.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - start);
                message.setSendTime(now);
                message.setCostTime(costTime);
                TelecomSmsSendResponse sendResponse = JSON.parseObject(response.getBody(), TelecomSmsSendResponse.class);
                TelecomSmsSendDetailRes detailRes = sendResponse == null || sendResponse.getList() == null || sendResponse.getList().isEmpty()
                        ? null
                        : sendResponse.getList().get(0);

                if (response.getStatusCode() == HttpStatus.OK && detailRes != null && detailRes.getResult() != null && detailRes.getResult() == 0) {
                    message.setThirdPartyId(detailRes.getMid());
                    // 电信短信同步返回成功同样只代表已受理，最终状态以后续回执为准。
                    this.getDownInfo(detailRes.getMid(), message);
                    return SendResult.accepted(now, costTime, detailRes.getMid());
                }

                String providerRawCode = detailRes == null || detailRes.getResult() == null ? null : String.valueOf(detailRes.getResult());
                // 电信原始结果码在这里统一映射为平台错误码和中文错误信息。
                return this.sender.buildFailureResult(
                        message,
                        providerRawCode,
                        null,
                        "THIRD_PARTY_CALL_FAILED",
                        "第三方服务调用失败",
                        false
                );
            } catch (Exception e) {
                log.error("电信短信发送失败", e);
                return this.sender.buildCallFailedResult("第三方服务调用失败");
            }
        });

        try {
            return future.get(3, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("电信短信发送超时或执行异常", e);
            return this.sender.buildTimeoutResult();
        }
    }

    @Override
    public List<SendResult> sendBatchSms(List<MessageRecordDO> messageList) {
        List<SendResult> results = new ArrayList<>();
        for (MessageRecordDO message : messageList) {
            results.add(this.sendSms(message));
        }
        return results;
    }

    @Override
    public void queryTemplate(String templateIdentifier) {
        Map<String, Object> request = new HashMap<>();
        request.put("action", "templateSelect");
        request.put("account", telecomSmsSetting.getAccount());
        request.put("password", telecomSmsSetting.getPassword());
        request.put("templateJson", StrUtil.isBlank(templateIdentifier) ? 0 : templateIdentifier);

        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", CONTENT_TYPE);
        this.sender.restTemplateUtil.post(
                telecomSmsSetting.getApiUrl(),
                request,
                headers,
                String.class
        );
    }

    private void getDownInfo(String mid, MessageRecordDO message) {
        // 回执查询延后执行，给第三方落库和状态变更留出时间。
        this.sender.MSG_CALLBACK_THREAD_POOL_SCHEDULER.schedule(() -> {
            Map<String, Object> request = new HashMap<>();
            request.put("action", "select");
            request.put("account", telecomSmsSetting.getAccount());
            request.put("password", telecomSmsSetting.getPassword());
            request.put("date", message.getSendTime().format(DateTimeFormatter.ofPattern("yyyyMMdd")));
            request.put("condition", "APMID");
            request.put("valueList", mid);

            HttpHeaders headers = new HttpHeaders();
            headers.set("Content-Type", CONTENT_TYPE);
            try {
                ResponseEntity<String> response = this.sender.restTemplateUtil.post(
                        telecomSmsSetting.getApiUrl(),
                        request,
                        headers,
                        String.class
                );

                TelecomSmsSelectResponse selectResponse = JSON.parseObject(response.getBody(), TelecomSmsSelectResponse.class);
                if (selectResponse == null || selectResponse.getList() == null || selectResponse.getList().isEmpty()) {
                    return;
                }
                TelecomSmsSelectDetailRes detailRes = selectResponse.getList().get(0);
                if (detailRes.getStatus() == 5) {
                    SendResult failedResult = this.sender.buildFailureResult(
                            message,
                            detailRes.getStat(),
                            null,
                            "THIRD_PARTY_CALLBACK_FAILED",
                            "短信回执返回失败",
                            false
                    );
                    this.sender.applyCallbackResult(message, failedResult);
                } else if (detailRes.getStatus() == 4) {
                    // 回执确认成功后，复用统一成功落库逻辑。
                    this.sender.applyCallbackResult(message, SendResult.success(message.getSendTime(), message.getCostTime(), message.getThirdPartyId()));
                }
            } catch (Exception e) {
                log.error("电信短信回执查询失败", e);
            }
        }, 20, TimeUnit.SECONDS);
    }
}
