package com.ningdatech.pmapi.meeting.task;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ningdatech.basic.util.StrPool;
import com.ningdatech.pmapi.common.util.StrUtils;
import com.ningdatech.pmapi.meeting.entity.domain.ExpertInviteRule;
import com.ningdatech.pmapi.meeting.entity.domain.MeetingExpert;
import com.ningdatech.pmapi.meeting.entity.dto.RandomInviteRuleDTO;
import com.ningdatech.pmapi.meeting.entity.dto.YxtCallBackDTO;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum;
import com.ningdatech.pmapi.meeting.entity.enumeration.ExpertInviteTypeEnum;
import com.ningdatech.pmapi.meeting.service.IExpertInviteRuleService;
import com.ningdatech.pmapi.meeting.service.IMeetingExpertService;
import com.ningdatech.yxt.entity.SysMsgRecordDetail;
import com.ningdatech.yxt.service.ISysMsgRecordDetailService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static com.ningdatech.pmapi.meeting.entity.enumeration.ExpertAttendStatusEnum.*;

/**
 * <p>
 * ExpertCallResultRewriteTask
 * <br>
 * 专家电话结果回填
 * </p>
 *
 * @author WendyYang
 * @since 14:15 2022/8/18
 */
@Slf4j
@Component
@AllArgsConstructor
public class ExpertCallResultRewriteTask {

    private final RandomInviteProperties randomInviteProperties;

    private final ThreadPoolTaskScheduler scheduler;
    private final IMeetingExpertService meetingExpertService;
    private final IExpertInviteRuleService inviteRuleService;
    private final ISysMsgRecordDetailService msgRecordDetailService;
    private final static int MINUTES_CALL_RESULT_FEEDBACK = 15;

    private static final String AGREE_KEY = "1";

    @PostConstruct
    public void initTask() {
        if (!randomInviteProperties.getEnable()) {
            log.warn("随机邀请已关闭……");
            return;
        }
        Instant startTime = Instant.now().plus(randomInviteProperties.getResultRewriteFixedRate(), ChronoUnit.MINUTES);
        // 处理电话结果回填
        Duration fixedRate = Duration.ofMinutes(randomInviteProperties.getResultRewriteFixedRate());
        scheduler.scheduleAtFixedRate(this::rewritePhoneCallResult, startTime, fixedRate);
    }


    public void rewritePhoneCallResult() {
        log.info("开始执行电话结果回填任务：{}", Thread.currentThread().getName());
        // 查询所有邀请的专家信息 状态为通话中的
        LambdaQueryWrapper<MeetingExpert> meQuery = Wrappers.lambdaQuery(MeetingExpert.class)
                .eq(MeetingExpert::getStatus, NOTICING.getCode())
                .eq(MeetingExpert::getInviteType, ExpertInviteTypeEnum.RANDOM.getCode());
        List<MeetingExpert> experts = meetingExpertService.list(meQuery);
        if (experts.isEmpty()) {
            log.info("暂无电话结果回填任务执行");
            return;
        }
        // 所有随机邀请的规则ID
        Map<Long, String> submitKeys = new HashMap<>(experts.size());
        Set<Long> randomRuleIds = experts.stream().peek(w -> submitKeys.put(w.getId(), w.getSubmitKey()))
                .map(MeetingExpert::getRuleId).collect(Collectors.toSet());
        // 查询随机邀请回调等待时间
        Map<Long, Integer> callbackMinutes = new HashMap<>(randomRuleIds.size());
        if (!randomRuleIds.isEmpty()) {
            List<ExpertInviteRule> inviteRules = inviteRuleService.listByIds(randomRuleIds);
            inviteRules.forEach(w -> {
                RandomInviteRuleDTO rule = JSONObject.parseObject(w.getInviteRule(), RandomInviteRuleDTO.class);
                callbackMinutes.put(w.getId(), rule.getWaitForCallbackMinutes());
            });
        }
        LambdaQueryWrapper<SysMsgRecordDetail> msgRecordDetailQuery = Wrappers.lambdaQuery(SysMsgRecordDetail.class)
                .in(SysMsgRecordDetail::getSubmitKey, submitKeys.values());
        List<SysMsgRecordDetail> recordDetailList = msgRecordDetailService.list(msgRecordDetailQuery);
        if (recordDetailList.isEmpty()) {
            return;
        }
        Map<String, SysMsgRecordDetail> recordDetailMap = recordDetailList.stream()
                .collect(Collectors.toMap(w -> w.getSubmitKey() + StrPool.UNDERSCORE + w.getReceiveNumber(), w -> w));
        List<MeetingExpert> updates = new ArrayList<>(), agrees = new ArrayList<>();
        for (MeetingExpert expert : experts) {
            String key = expert.getSubmitKey() + StrPool.UNDERSCORE + expert.getMobile();
            SysMsgRecordDetail msgRecordDetail = recordDetailMap.get(key);
            if (msgRecordDetail == null) {
                // 极端情况下获取不到submitKey异常情况
                continue;
            }
            Integer minutes = ObjectUtil.defaultIfNull(callbackMinutes.get(expert.getRuleId()), MINUTES_CALL_RESULT_FEEDBACK);
            Optional<Integer> status = getStatusByMsgRecordDetail(msgRecordDetail, minutes, expert.getCreateOn());
            if (status.isPresent()) {
                MeetingExpert update = new MeetingExpert();
                update.setUpdateBy(0L);
                update.setUpdateOn(LocalDateTime.now());
                update.setId(expert.getId());
                update.setStatus(status.get());
                if (AGREED.eq(update.getStatus())) {
                    // 发送专家确认参加的短信通知
                    // sendAgreeNotice(expert);
                    agrees.add(expert);
                }
                updates.add(update);
            }
        }
        meetingExpertService.updateBatchById(updates);
    }

    private static Optional<Integer> getStatusByMsgRecordDetail(SysMsgRecordDetail mrd, int minutes, LocalDateTime createOn) {
        LocalDateTime limitTime = LocalDateTime.now().minusMinutes(minutes);
        String callBackJson = mrd.getCallBackJson();
        boolean waiting = limitTime.isBefore(createOn);
        boolean hasCallBack = StrUtils.isNotBlank(callBackJson);
        if (!hasCallBack && waiting) {
            return Optional.empty();
        }
        ExpertAttendStatusEnum status;
        if (hasCallBack) {
            try {
                YxtCallBackDTO callback = JSONObject.parseObject(callBackJson, YxtCallBackDTO.class);
                LocalDateTime dialBeginTime = callback.getDialBeginTime();
                if (dialBeginTime == null) {
                    return Optional.empty();
                }
                Integer resultCode = callback.getResultCode();
                if (resultCode != null && resultCode == 0) {
                    String pressKey = callback.getPressKey();
                    if (pressKey != null) {
                        pressKey = pressKey.replaceAll("\\*", "").trim();
                    }
                    if (StrUtils.isBlank(pressKey) && waiting) {
                        return Optional.empty();
                    }
                    status = AGREE_KEY.equals(pressKey) ? AGREED : REFUSED;
                } else {
                    if (waiting) {
                        return Optional.empty();
                    }
                    status = REFUSED;
                }
            } catch (Exception e) {
                log.error("获取电话回调结果异常{}", mrd, e);
                status = UNANSWERED;
            }
        } else {
            // 超时未回复设置为拒绝参加
            status = REFUSED;
        }
        return Optional.of(status.getCode());
    }

}
