package com.skr.mdm.api.content.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.skr.mdm.api.content.service.ContentInformService;
import com.skr.mdm.api.template.AbstractDefaultStrategyService;
import com.skr.mdm.bean.bo.QuartzJobBO;
import com.skr.mdm.bean.qo.BaseQueryQO;
import com.skr.mdm.bean.qo.BaseStrategyQO;
import com.skr.mdm.bean.qo.content.ContentInformQO;
import com.skr.mdm.bean.so.CmdInfoSO;
import com.skr.mdm.bean.so.content.ContentInformSO;
import com.skr.mdm.bean.vo.content.ContentInformVO;
import com.skr.mdm.constant.JobConstant;
import com.skr.mdm.dao.ContentInformDao;
import com.skr.mdm.dao.ContentInformUserDao;
import com.skr.mdm.dao.DeviceEquipmentDao;
import com.skr.mdm.dto.LoginAdminDTO;
import com.skr.mdm.entity.ContentInformDO;
import com.skr.mdm.entity.ContentInformUserDO;
import com.skr.mdm.enums.InformCmdEnum;
import com.skr.mdm.enums.IssuedTypeEnum;
import com.skr.mdm.enums.LogAdminTypeEnum;
import com.skr.mdm.enums.StrategyEquipmentStateEnum;
import com.skr.mdm.handle.IssuedUserHandler;
import com.skr.mdm.handle.LogAdminHandler;
import com.skr.mdm.quartz.SchedulerHandler;
import com.skr.mdm.quartz.job.InformJob;
import com.skr.mdm.result.ResponseResult;
import com.skr.mdm.util.MdmListUtil;
import com.skr.mdm.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author nfq
 * @date 2022-09-28 9:59
 */
@Service
@Slf4j
public class ContentInformServiceImpl extends AbstractDefaultStrategyService implements ContentInformService {

    private final String MODULE = "通知管理";

    private ContentInformQO contentInformQO;

    @Autowired
    private ContentInformDao contentInformDao;
    @Autowired
    private ContentInformUserDao contentInformUserDao;
    @Autowired
    private LogAdminHandler logAdminHandler;
    @Autowired
    private IssuedUserHandler issuedUserHandler;
    @Autowired
    private SchedulerHandler schedulerHandler;
    @Autowired
    private DeviceEquipmentDao equipmentDao;

    @Override
    protected Object getStrategyInfo(Integer strategyId) {
        return null;
    }

    @Override
    protected long strategyStateCheck(List<Integer> strategyIdList) {
        return 0;
    }

    @Override
    protected void updateStrategyIssuedState(List<Integer> strategyIdList, BaseStrategyQO baseStrategyQO) {
        new LambdaUpdateChainWrapper<>(contentInformDao)
                .set(ContentInformDO::getState, baseStrategyQO.getState())
                .in(ContentInformDO::getId, strategyIdList)
                .update();
    }

    @Override
    protected void updateStrategyDeptGlobal(List<Integer> strategyIdList, List<Integer> departmentIdList) {
        new LambdaUpdateChainWrapper<>(contentInformDao)
                .set(ContentInformDO::getDepartmentIds, JSONUtil.toJsonStr(departmentIdList))
                .in(ContentInformDO::getId, strategyIdList)
                .update();
    }

    @Override
    protected List<Integer> getEquipmentIdListByStrategyIdList(List<Integer> strategyIdList) {
        return contentInformUserDao.listEquipmentIdByInformIds(strategyIdList);
    }

    @Override
    protected List<Integer> getEquipmentIdListByStrategyIdListForDeptGlobal(List<Integer> strategyIdList) {
        List<ContentInformDO> contentInformDOList = contentInformDao.selectBatchIds(strategyIdList);
        if (CollectionUtils.isNotEmpty(contentInformDOList)) {
            if (StringUtils.isNotBlank(contentInformDOList.get(0).getDepartmentIds())) {
                List<Integer> departmentIdList = JSONUtil.toList(JSONUtil.parseArray(
                        contentInformDOList.get(0).getDepartmentIds()), Integer.class);
                if (CollectionUtils.isNotEmpty(departmentIdList)) {
                    return equipmentDao.listEquipmentIdByDepartmentIdList(departmentIdList);
                }
            }
        }
        return Collections.emptyList();
    }

    @Override
    protected void adminLogIssuedStrategy(List<Integer> strategyIdList, BaseStrategyQO baseStrategyQO, LoginAdminDTO loginAdminDTO) {
        String content = "";
        List<ContentInformDO> contentInformDOList = contentInformDao.selectBatchIds(strategyIdList);
        if (CollectionUtils.isNotEmpty(contentInformDOList)) {
            List<String> strategyNameList = contentInformDOList.stream().map(ContentInformDO::getInformTitle).collect(Collectors.toList());
            content = "下发通知 通知标题：" + StringUtils.join(strategyNameList, "，");
        }
        logAdminHandler.log(baseStrategyQO, loginAdminDTO, LogAdminTypeEnum.CONTENT_TYPE.getLogType(), content, MODULE);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveStrategyUser(List<Integer> strategyIdList, List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO) {
        Date date = new Date();
        strategyIdList.forEach(strategyId -> {
            List<Integer> listEquipmentIdByStrategyIds = contentInformUserDao.listEquipmentIdByInformIds(ListUtil.toList(strategyId));
            List<Integer> addEquipmentIdList = MdmListUtil.difference(equipmentIdList, listEquipmentIdByStrategyIds);
            List<Integer> updateEquipmentIdList = MdmListUtil.intersection(equipmentIdList, listEquipmentIdByStrategyIds);
            Integer state = StrategyEquipmentStateEnum.ISSUING.getState();
            ContentInformDO contentInformDO = contentInformDao.selectById(strategyId);
            if (contentInformDO.getExecuteTime() != null && contentInformDO.getExecuteTime().after(new Date())) {
                state = StrategyEquipmentStateEnum.WAIT_ISSUED.getState();
            }
            // 新增
            insertStrategyUser(addEquipmentIdList, loginAdminDTO, date, strategyId, state);
            // 更新
            updateStrategyUser(date, strategyId, updateEquipmentIdList, state);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doIssuedStrategy(List<Integer> strategyIdList, List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO) {
        DateTime date = DateUtil.date();

        List<ContentInformSO> cmdMessageSOList = Lists.newArrayListWithCapacity(strategyIdList.size());
        List<ContentInformSO> cmdForceMessageSOList = Lists.newArrayListWithCapacity(strategyIdList.size());

        strategyIdList.forEach(strategyId -> {
            ContentInformDO contentInformDO = contentInformDao.selectById(strategyId);
            ContentInformSO contentInformSO = new ContentInformSO();
            contentInformSO.setActionId(strategyId);
            contentInformSO.setMsg(contentInformDO.getInformContent());
            contentInformSO.setReadDelay(contentInformDO.getReadDelay());
            if (contentInformDO.getExecuteTime() != null && contentInformDO.getExecuteTime().after(new Date())) {
                contentInformSO.setSendTime(contentInformDO.getExecuteTime());
                List<CmdInfoSO> cmdMessageSOS = Lists.newArrayListWithCapacity(1);
                cmdMessageSOS.add(contentInformSO);
                // 定时通知
                QuartzJobBO quartzJobBO = QuartzJobBO.builder()
                        .cmd(contentInformDO.getInformType() == 1 ? InformCmdEnum.ISSUED_INFORM.getCmd() :
                                InformCmdEnum.ISSUED_FORCE_INFORM.getCmd())
                        .message(JSONUtil.toJsonStr(cmdMessageSOS))
                        .id(strategyId)
                        .companyId(loginAdminDTO.getCompanyId())
                        .executeTime(contentInformDO.getExecuteTime())
                        .prefix(JobConstant.JOB_INFORM_KEY_PREFIX)
                        .build();
                schedulerHandler.handleJob(quartzJobBO, InformJob.class);
            } else {
                contentInformSO.setSendTime(date);
                // 立即下发
                if (contentInformDO.getInformType() == 1) {
                    // 非强制
                    cmdMessageSOList.add(contentInformSO);
                } else {
                    // 强制
                    cmdForceMessageSOList.add(contentInformSO);
                }
            }
        });

        if (CollectionUtils.isNotEmpty(cmdMessageSOList)) {
            issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(), InformCmdEnum.ISSUED_INFORM.getCmd(), JSONUtil.toJsonStr(cmdMessageSOList));
        }
        if (CollectionUtils.isNotEmpty(cmdForceMessageSOList)) {
            issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(), InformCmdEnum.ISSUED_FORCE_INFORM.getCmd(), JSONUtil.toJsonStr(cmdForceMessageSOList));
        }
    }

    @Override
    protected long checkDuplicateStrategy(Object operationObject, LoginAdminDTO loginAdminDTO) {
        this.contentInformQO = (ContentInformQO) operationObject;
        return new LambdaQueryChainWrapper<>(contentInformDao)
                .eq(ContentInformDO::getInformTitle, contentInformQO.getInformTitle())
                .eq(ContentInformDO::getCompanyId, loginAdminDTO.getCompanyId())
                .ne(contentInformQO.getId() != null, ContentInformDO::getId, contentInformQO.getId())
                .count();
    }

    @Override
    protected Integer saveStrategy(Object operationObject, LoginAdminDTO loginAdminDTO) {
        ContentInformDO contentInformDO = ContentInformDO.builder()
                .companyId(loginAdminDTO.getCompanyId())
                .operator(loginAdminDTO.getAdminName())
                .operateTime(new Date())
                .departmentIds("")
                .build();
        BeanUtils.copyProperties(contentInformQO, contentInformDO);
        contentInformDao.insert(contentInformDO);
        return contentInformDO.getId();
    }

    @Override
    protected void adminLogAddStrategy(LoginAdminDTO loginAdminDTO) {
        String logContent = "通知 标题：" + contentInformQO.getInformTitle();
        if (contentInformQO.getId() == null) {
            logContent = "创建" + logContent;
        } else {
            logContent = "编辑" + logContent;
        }
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.CONTENT_TYPE.getLogType(), logContent, MODULE, Collections.emptyList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer updateStrategyById(Object operationObject, LoginAdminDTO loginAdminDTO) {
        ContentInformDO contentInformDO = ContentInformDO.builder()
                .operator(loginAdminDTO.getAdminName())
                .operateTime(new Date())
                .build();
        BeanUtils.copyProperties(contentInformQO, contentInformDO);
        contentInformDao.updateById(contentInformDO);
        return contentInformDO.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void issuedStrategyDelete(List<Integer> strategyIdList, LoginAdminDTO loginAdminDTO) {
        List<Integer> listEquipmentIdByStrategyIds = contentInformUserDao.listEquipmentIdByInformIds(strategyIdList);
        issuedUserHandler.issuedUser(listEquipmentIdByStrategyIds, loginAdminDTO.getCompanyId(),
                InformCmdEnum.DELETE_INFORM.getCmd(), JSONUtil.toJsonStr(strategyIdList));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doDeleteStrategy(List<Integer> strategyIdList) {
        contentInformDao.deleteBatchIds(strategyIdList);
        contentInformUserDao.delete(Wrappers.lambdaQuery(ContentInformUserDO.class).in(ContentInformUserDO::getInformId, strategyIdList));
        // quartz 尝试删除
        List<String> jobKeyIdList = strategyIdList.stream()
                .map(strategyId -> JobConstant.JOB_INFORM_KEY_PREFIX + strategyId)
                .collect(Collectors.toList());
        schedulerHandler.deleteJob(jobKeyIdList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void adminLogDeleteStrategy(List<Integer> strategyIdList, LoginAdminDTO loginAdminDTO) {
        strategyIdList.forEach(strategyId -> {
            ContentInformDO contentInformDO = contentInformDao.selectById(strategyId);
            List<Integer> listEquipmentIdByStrategyIds = contentInformUserDao.listEquipmentIdByInformIds(Collections.singletonList(strategyId));
            String content = "删除通知 标题：" + contentInformDO.getInformTitle();
            logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.CONTENT_TYPE.getLogType(), content, MODULE, listEquipmentIdByStrategyIds);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doUpdateStrategyState(Integer strategyId, Integer state, LoginAdminDTO loginAdminDTO) {
        new LambdaUpdateChainWrapper<>(contentInformDao)
                .set(state != null, ContentInformDO::getState, state)
                .set(ContentInformDO::getOperator, loginAdminDTO.getAdminName())
                .set(ContentInformDO::getOperateTime, new Date())
                .eq(ContentInformDO::getId, strategyId)
                .update();
    }

    @Override
    protected BaseStrategyQO createBaseStrategyQO(Integer strategyId) {
        ContentInformDO informDO = contentInformDao.selectById(strategyId);
        if (informDO == null) {
            log.warn("informDO 不应该为null");
            return null;
        }
        BaseStrategyQO baseStrategyQO = new BaseStrategyQO();
        baseStrategyQO.setState(informDO.getState());
        baseStrategyQO.setGlobalType(IssuedTypeEnum.DEVICE.getIssuedType());
        return baseStrategyQO;
    }

    @Override
    protected void adminLogUpdateStrategyState(Integer strategyId, Integer state, List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO) {

    }

    @Override
    protected void issuedStrategyState(Integer strategyId, Integer state, List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO) {

    }

    @Override
    protected List<?> getStrategyUserList(Integer strategyId, Integer searchType, String searchValue) {
        BaseQueryQO baseQueryQO = new BaseQueryQO();
        baseQueryQO.setSearchType(searchType);
        baseQueryQO.setSearchValue(searchValue);
        return contentInformUserDao.listContentInformUserVO(strategyId, baseQueryQO);
    }

    @Override
    protected void issuedStrategyUserDelete(Integer strategyId, List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO) {
        issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(),
                InformCmdEnum.DELETE_INFORM.getCmd(), JSONUtil.toJsonStr(ListUtil.toList(strategyId)));
    }

    @Override
    protected void doDeleteStrategyUser(Integer strategyId, List<Integer> equipmentIdList) {
        contentInformUserDao.delete(
                Wrappers.lambdaQuery(ContentInformUserDO.class)
                        .eq(ContentInformUserDO::getInformId, strategyId)
                        .in(ContentInformUserDO::getEquipmentId, equipmentIdList));
    }

    @Override
    protected void adminLogDeleteStrategyUser(Integer strategyId, List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO) {
        ContentInformDO informDO = contentInformDao.selectById(strategyId);
        if (informDO == null) {
            log.warn("informDO 不应该为null");
            return;
        }
        String content = "通知作用人删除 标题：" + informDO.getInformTitle();
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.CONTENT_TYPE.getLogType(), content, MODULE, equipmentIdList);
    }

    @Override
    public ResponseResult list(BaseQueryQO baseQueryQO, LoginAdminDTO loginAdminDTO) {
        PageHelper.startPage(baseQueryQO.getPageNum(), baseQueryQO.getPageSize());
        List<ContentInformVO> contentInformVOList = contentInformDao.listContentInformVO(baseQueryQO, loginAdminDTO.getCompanyId());
        return ResponseResult.success(PageUtil.getPageResult(contentInformVOList));
    }

    private void updateStrategyUser(Date date, Integer strategyId, List<Integer> deviceIdList, Integer state) {
        if (CollectionUtils.isNotEmpty(deviceIdList)) {
            new LambdaUpdateChainWrapper<>(contentInformUserDao)
                    .set(ContentInformUserDO::getState, state)
                    .set(ContentInformUserDO::getIssuedTime, date)
                    .eq(ContentInformUserDO::getInformId, strategyId)
                    .in(ContentInformUserDO::getEquipmentId, deviceIdList)
                    .update();
        }
    }

    private void insertStrategyUser(List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO, Date date, Integer strategyId, Integer state) {
        if (CollectionUtils.isNotEmpty(equipmentIdList)) {
            List<ContentInformUserDO> contentInformUserDOList = Lists.newArrayListWithCapacity(equipmentIdList.size());
            equipmentIdList.forEach(equipmentId -> {
                ContentInformUserDO contentInformUserDO = ContentInformUserDO.builder()
                        .equipmentId(equipmentId)
                        .companyId(loginAdminDTO.getCompanyId())
                        .informId(strategyId)
                        .state(state)
                        .issuedTime(date)
                        .build();
                contentInformUserDOList.add(contentInformUserDO);
            });
            contentInformUserDao.insertBatch(contentInformUserDOList);
        }
    }
}