package cn.iocoder.yudao.module.system.service.notify;

import cn.hutool.core.date.DateUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.corp.CorpMsgUtil;
import cn.iocoder.yudao.framework.common.util.string.StringUtil;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskApproveMethodEnum;
import cn.iocoder.yudao.module.bpm.enums.message.BpmMessageEnum;
import cn.iocoder.yudao.module.system.api.notify.dto.NotifySendSingleToUserReqDTO;
import cn.iocoder.yudao.module.system.api.wechat.CorpWechatApi;
import cn.iocoder.yudao.module.system.api.wechat.dto.CorpSendMessage;
import cn.iocoder.yudao.module.system.api.wechat.dto.CorpWechatTextcard;
import cn.iocoder.yudao.module.system.api.wechat.dto.WechatMessageSaveReq;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyMessageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.notify.NotifyTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
import cn.iocoder.yudao.module.system.enums.notice.NotieBusinessNameEnum;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.corp.CorpMsgUtil.BTNTXT_DETAIL_NOTICE;
import static cn.iocoder.yudao.framework.common.util.corp.CorpMsgUtil.TITLE_CANCEL;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 站内信发送 Service 实现类
 *
 * @author xrcoder
 */
@Service
@Validated
@Slf4j
public class NotifySendServiceImpl implements NotifySendService {

    @Resource
    private NotifyTemplateService notifyTemplateService;

    @Resource
    private NotifyMessageService notifyMessageService;

    @Resource
    private NotifyMessageMapper notifyMessageMapper;

    @Resource
    private CorpWechatApi corpWechatApi;

    @Override
    public Long sendSingleNotifyToAdmin(Long userId, String templateCode, Map<String, Object> templateParams) {
        return sendSingleNotify(userId, UserTypeEnum.ADMIN.getValue(), templateCode, templateParams);
    }

    @Override
    @TenantIgnore
    public Long sendSingleNotifyToAdmin(Long userId, String pkMessage, String pkMessage2, String gotoUrl,
                                        String templateCode, Map<String, Object> templateParams, Integer businessType,
                                        Integer notifyType, Integer priority, Integer completedStatus, Long tenantId) {
        Integer userType = UserTypeEnum.ADMIN.getValue();
        // 校验模版
        NotifyTemplateDO template = validateNotifyTemplate(templateCode);
        if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
            log.info("[sendSingleNotify][模版({})已经关闭，无法给用户({}/{})发送]", templateCode, userId, userType);
            return null;
        }
        // 校验参数
        validateTemplateParams(template, templateParams);

        // 发送站内信
        String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), templateParams);
        NotifyMessageDO message = new NotifyMessageDO().setUserId(userId).setUserType(userType)
                .setTemplateId(template.getId()).setPkMessage(pkMessage).setPkMessage2(pkMessage2)
                .setGotoUrl(gotoUrl).setTemplateCode(template.getCode())
                .setTemplateType(template.getType()).setTemplateNickname(template.getNickname())
                .setTemplateContent(content).setTemplateParams(templateParams).setReadStatus(false)
                .setBusinessType(businessType).setNotifyType(notifyType).setPriority(priority).setCompletedStatus(completedStatus)
                .setTenantId(tenantId);
        notifyMessageMapper.insert(message);
        return message.getId();
    }

    @Override
    @TenantIgnore
    public void updateMessageStatus(String processInstanceId, String taskId, Long userId, Integer completedStatus) {
        LambdaUpdateWrapper<NotifyMessageDO> lambdaQueryWrapper = new LambdaUpdateWrapper<>();
        lambdaQueryWrapper.set(NotifyMessageDO::getCompletedStatus, completedStatus)
                .set(NotifyMessageDO::getCompletedTime, new Date())
                .set(NotifyMessageDO::getCompletedUser, userId)
                .eq(NotifyMessageDO::getPkMessage, processInstanceId);
        notifyMessageMapper.update(lambdaQueryWrapper);
    }

    @Override
    @TenantIgnore
    public void updateSelfMessageStatus(String processInstanceId, String taskId, Long userId, Long assigneeUserId,
                                        Integer completedStatus, Integer approveMethod, Boolean startNodeFlag) {
        LambdaUpdateWrapper<NotifyMessageDO> lambdaQueryWrapper = new LambdaUpdateWrapper<>();
        lambdaQueryWrapper.set(NotifyMessageDO::getCompletedStatus, completedStatus)
                .set(NotifyMessageDO::getCompletedTime, new Date())
                .set(NotifyMessageDO::getCompletedUser, userId)
                .eq(NotifyMessageDO::getPkMessage, processInstanceId);

//        if (StringUtil.isNotEmptyORNull(taskId)) {
//            lambdaQueryWrapper.eq(NotifyMessageDO::getPkMessage2, taskId);
//        }

        if (null != userId) {
            lambdaQueryWrapper.eq(NotifyMessageDO::getUserId, userId);
        }
        notifyMessageMapper.update(lambdaQueryWrapper);
    }

    @Override
    public Long sendSingleNotifyToMember(Long userId, String templateCode, Map<String, Object> templateParams) {
        return sendSingleNotify(userId, UserTypeEnum.MEMBER.getValue(), templateCode, templateParams);
    }

    @Override
    public Long sendSingleNotify(Long userId, Integer userType, String templateCode, Map<String, Object> templateParams) {
        // 校验模版
        NotifyTemplateDO template = validateNotifyTemplate(templateCode);
        if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
            log.info("[sendSingleNotify][模版({})已经关闭，无法给用户({}/{})发送]", templateCode, userId, userType);
            return null;
        }
        // 校验参数
        validateTemplateParams(template, templateParams);

        // 发送站内信
        String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), templateParams);
        return notifyMessageService.createNotifyMessage(userId, userType, template, content, templateParams);
    }

    @Override
    public Long sendNotifyList(List<NotifySendSingleToUserReqDTO> copyNotifyList) {
        // TODO
        Long tenantId = TenantContextHolder.getTenantId();
        Integer userType = UserTypeEnum.ADMIN.getValue();
        for (int i = 0; i < copyNotifyList.size(); i++) {
            NotifySendSingleToUserReqDTO notifyDTO = copyNotifyList.get(i);
            // 校验模版
            NotifyTemplateDO template = validateNotifyTemplate(notifyDTO.getTemplateCode());
            if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
                log.info("[sendSingleNotify][模版({})已经关闭，无法给用户({}/{})发送]", notifyDTO.getTemplateCode(), notifyDTO.getUserId(), userType);
                return null;
            }
            // 校验参数
            validateTemplateParams(template, notifyDTO.getTemplateParams());

            // 发送站内信
            String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), notifyDTO.getTemplateParams());
            NotifyMessageDO message = new NotifyMessageDO().setUserId(notifyDTO.getUserId()).setUserType(userType)
                    .setTemplateId(template.getId()).setPkMessage(notifyDTO.getPkMessage()).setPkMessage2(notifyDTO.getPkMessage2())
                    .setGotoUrl(notifyDTO.getGotoUrl()).setTemplateCode(template.getCode())
                    .setTemplateType(template.getType()).setTemplateNickname(template.getNickname())
                    .setTemplateContent(content).setTemplateParams(notifyDTO.getTemplateParams()).setReadStatus(false)
                    .setBusinessType(notifyDTO.getBusinessType()).setNotifyType(notifyDTO.getNotifyType()).setPriority(notifyDTO.getPriority())
                    .setTenantId(tenantId);
            notifyMessageMapper.insert(message);
        }
        return null;
    }

    @Override
    public Long sendMessageAndCorpMessage(NotifySendSingleToUserReqDTO reqDTO, CorpSendMessage corpSendMessage) {
        Integer userType = UserTypeEnum.ADMIN.getValue();
        // 校验模版
        NotifyTemplateDO template = validateNotifyTemplate(reqDTO.getTemplateCode());
        if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
            log.info("[sendMessageAndCorpMessage][模版({})已经关闭，无法给用户({}/{})发送]", reqDTO.getTemplateCode(), reqDTO.getUserId(), userType);
            return null;
        }
        // 校验参数
        validateTemplateParams(template, reqDTO.getTemplateParams());


        String buginessName = matchBuginessName(reqDTO.getTemplateCode());

        // 发送站内信
        String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), reqDTO.getTemplateParams());
        NotifyMessageDO message = new NotifyMessageDO().setUserId(reqDTO.getUserId()).setUserType(userType)
                .setTemplateId(template.getId()).setPkMessage(reqDTO.getPkMessage()).setPkMessage2(reqDTO.getPkMessage2())
                .setGotoUrl(reqDTO.getGotoUrl()).setTemplateCode(template.getCode()).setTemplateType(template.getType())
                .setTemplateNickname(template.getNickname()).setTemplateContent(content).setTemplateParams(reqDTO.getTemplateParams())
                .setReadStatus(false).setBusinessType(reqDTO.getBusinessType()).setBuginessName(buginessName).setNotifyType(reqDTO.getNotifyType())
                .setPriority(reqDTO.getPriority()).setCompletedStatus(reqDTO.getCompletedStatus()).setTenantId(reqDTO.getTenantId());
        notifyMessageMapper.insert(message);

        CorpWechatTextcard textcard = corpSendMessage.getTextcard();
        WechatMessageSaveReq req = corpSendMessage.getWechatMessageSaveReq();
        req.setContent(content);
        String corpContent = "";
        if (0 == corpSendMessage.getContentType()) {
            corpContent = CorpMsgUtil.getContent(CorpMsgUtil.builderHandledContent(
                    req.getTenantName(),
                    req.getTypeName(),
                    content,
                    req.getApplyTime(),
                    req.getReason()), CorpMsgUtil.NORMAL);
        }
        if (1 == corpSendMessage.getContentType()) {
            corpContent = CorpMsgUtil.getContent(CorpMsgUtil.builderTodoContent(content), CorpMsgUtil.NORMAL);
        }
        textcard.setDescription(CorpMsgUtil.getDescription(null, corpContent));
        corpWechatApi.sendMessage(corpSendMessage);
        return message.getId();
    }

    private String matchBuginessName(String templateCode) {
        String buginessName = "";
        if (StringUtil.isEmptyORNull(templateCode)) return buginessName;
        if (BpmMessageEnum.CHECK_IN_HANDLE.getTemplateCode().equals(templateCode)) {
            buginessName = NotieBusinessNameEnum.CHECK_IN_NAME.getName();
        }
        if (BpmMessageEnum.SYNTHETIC_ABILITY_HANDLE.getTemplateCode().equals(templateCode)) {
            buginessName = NotieBusinessNameEnum.SYNTHETIC_ABILITY_NAME.getName();
        }
        if (BpmMessageEnum.EXPENSE_ORDER_HANDLE.getTemplateCode().equals(templateCode)) {
            buginessName = NotieBusinessNameEnum.EXPENSE_ORDER_NAME.getName();
        }
        if (BpmMessageEnum.REFUND_SETTLEMENT_HANDLE.getTemplateCode().equals(templateCode)) {
            buginessName = NotieBusinessNameEnum.EREFUND_SETTLEMENT_NAME.getName();
        }
        if (BpmMessageEnum.SELF_MEDICATION_WARNING_HANDLE.getTemplateCode().equals(templateCode)) {
            buginessName = NotieBusinessNameEnum.SELF_MEDICATION_WARNING_NAME.getName();
        }
        if (BpmMessageEnum.SELF_MEDICATION_EXPIRE_HANDLE.getTemplateCode().equals(templateCode)) {
            buginessName = NotieBusinessNameEnum.SELF_MEDICATION_EXPIRE_NAME.getName();
        }
        if (BpmMessageEnum.ELDERLY_CONTRACT_EXPIRE_HANDLE.getTemplateCode().equals(templateCode)) {
            buginessName = NotieBusinessNameEnum.ELDERLY_CONTRACT_EXPIRE_NAME.getName();
        }
        if (BpmMessageEnum.ELDER_BIRTHDAY_HANDLE.getTemplateCode().equals(templateCode)) {
            buginessName = NotieBusinessNameEnum.ELDER_BIRTHDAY_NAME.getName();
        }
        return buginessName;
    }

    @Override
    public Long sendCorpMessageList(List<CorpSendMessage> corpList, NotifySendSingleToUserReqDTO notifyDTO) {
        Integer userType = UserTypeEnum.ADMIN.getValue();
        // 校验模版
        NotifyTemplateDO template = validateNotifyTemplate(notifyDTO.getTemplateCode());
        if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
            log.info("[sendCorpMessageList][模版({})已经关闭，无法给用户({}/{})发送]", notifyDTO.getTemplateCode(), notifyDTO.getUserId(), userType);
            return null;
        }
        // 校验参数
        validateTemplateParams(template, notifyDTO.getTemplateParams());
        // 发送站内信
        String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), notifyDTO.getTemplateParams());
        corpList.forEach(corpSendMessage -> {
            CorpWechatTextcard textcard = corpSendMessage.getTextcard();
            WechatMessageSaveReq req = corpSendMessage.getWechatMessageSaveReq();
            textcard.setDescription(CorpMsgUtil.getDescription(null, CorpMsgUtil.getContent(CorpMsgUtil.builderCopyContent(req.getTenantName(), content), CorpMsgUtil.NORMAL)));
            req.setContent(content);
            corpWechatApi.sendMessage(corpSendMessage);
        });
        return null;
    }

    @Override
    public Long sendCorpCancelMessage(CorpSendMessage corpSendMessage) {
        Integer userType = UserTypeEnum.ADMIN.getValue();
        // 校验模版
        NotifyTemplateDO template = validateNotifyTemplate(corpSendMessage.getTemplateCode());
        if (Objects.equals(template.getStatus(), CommonStatusEnum.DISABLE.getStatus())) {
            log.info("[sendCorpMessageList][模版({})已经关闭，无法给用户({}/{})发送]", corpSendMessage.getTemplateCode(), corpSendMessage.getTouser(), userType);
            return null;
        }
        // 校验参数
        validateTemplateParams(template, corpSendMessage.getTemplateParams());
        String content = notifyTemplateService.formatNotifyTemplateContent(template.getContent(), corpSendMessage.getTemplateParams());
        CorpWechatTextcard textcard = corpSendMessage.getTextcard();
        WechatMessageSaveReq req = corpSendMessage.getWechatMessageSaveReq();
        textcard.setTitle(TITLE_CANCEL);
        textcard.setBtntxt(BTNTXT_DETAIL_NOTICE);
        textcard.setDescription(CorpMsgUtil.getDescription(null, CorpMsgUtil.getContent(CorpMsgUtil.builderCancelContent(req.getTenantName(), req.getTypeName(), content), CorpMsgUtil.NORMAL)));
        req.setTitle(TITLE_CANCEL);
        req.setContent(content);
        corpWechatApi.sendMessage(corpSendMessage);
        return null;
    }

    @Override
    public Integer updateMessageByCodeAndPkMessage(String templateCode, String pkMessage) {
        LocalDateTime now = LocalDateTime.now();
        int update = notifyMessageMapper.update(new LambdaUpdateWrapper<NotifyMessageDO>()
                .set(NotifyMessageDO::getCompletedTime, now)
                .set(NotifyMessageDO::getReadTime, now)
                .set(NotifyMessageDO::getReadStatus, 1)
                .set(NotifyMessageDO::getCompletedStatus, 1)
                .eq(NotifyMessageDO::getTemplateCode, templateCode).eq(NotifyMessageDO::getPkMessage, pkMessage));
        return update;
    }

    @VisibleForTesting
    public NotifyTemplateDO validateNotifyTemplate(String templateCode) {
        // 获得站内信模板。考虑到效率，从缓存中获取
        NotifyTemplateDO template = notifyTemplateService.getNotifyTemplateByCodeFromCache(templateCode);
        // 站内信模板不存在
        if (template == null) {
            throw exception(NOTICE_NOT_FOUND);
        }
        return template;
    }

    /**
     * 校验站内信模版参数是否确实
     *
     * @param template 邮箱模板
     * @param templateParams 参数列表
     */
    @VisibleForTesting
    public void validateTemplateParams(NotifyTemplateDO template, Map<String, Object> templateParams) {
        template.getParams().forEach(key -> {
            Object value = templateParams.get(key);
            if (value == null) {
                throw exception(NOTIFY_SEND_TEMPLATE_PARAM_MISS, key);
            }
        });
    }
}
