package com.rongwei.sfcommon.sys.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rongwe.scentity.domian.CheckItemsDo;
import com.rongwe.scentity.domian.PointCheckDo;
import com.rongwe.scentity.vo.CheckAreaVo;
import com.rongwe.scentity.vo.CheckItemsTypeVo;
import com.rongwe.scentity.vo.ImportCheckItemsVo;
import com.rongwe.scentity.vo.UserMailOrgVo;
import com.rongwei.rwadmincommon.system.domain.SysDictDo;
import com.rongwei.rwadmincommon.system.service.impl.SysDictServiceImpl;
import com.rongwei.rwadmincommon.system.vo.SysUserVo;
import com.rongwei.rwcommon.base.R;
import com.rongwei.rwcommon.base.exception.CustomException;
import com.rongwei.rwcommon.utils.SecurityUtil;
import com.rongwei.rwcommonentity.commonservers.domain.SysFileItemDo;
import com.rongwei.safecommon.utils.CXCommonUtils;
import com.rongwei.sfcommon.sys.dao.CheckItemsDao;
import com.rongwei.sfcommon.sys.dao.SaveCheckCommonDao;
import com.rongwei.sfcommon.sys.service.CheckItemsService;
import com.rongwei.sfcommon.sys.service.PointCheckService;
import com.rongwei.sfcommon.utils.ClassExcelVerifyHandler;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;

import java.io.File;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static com.rongwei.safecommon.utils.SaveConstans.*;
import static com.rongwei.safecommon.utils.SaveConstans.NotifyContent.*;
import static com.rongwei.safecommon.utils.SaveConstans.NotifyTitle.EQUIPMENT_CHECK_TITLE;
import static com.rongwei.safecommon.utils.SaveConstans.NotifyTitle.SPECIALEQUIPMENT_TITLE;
import static com.rongwei.safecommon.utils.SaveConstans.NotifyType.SPECIALEQUIPMENT;

@Service("checkItemsService")
public class CheckItemsServiceImpl extends ServiceImpl<CheckItemsDao, CheckItemsDo> implements CheckItemsService {
    /**
     * 组织机构代码
     */
    public static final List<String> ROLE_CODE_LIST = new ArrayList<String>() {{
        add("code-safe");
        add("role031");
    }};
    // 设备使用状态数据字典
    public static final String SBSYZT_DICT = "sbsyzt";
    // 特种设备类别数据字典
    public static final String TZSBLB_DICT = "tzsblb";
    // 检验结论
    public static final String JYJL_DICT = "jyjl";
    // 设备类型
    public static final String CHECK_ITEM_TYPE_DICT = "asp_check_item_type";

    /**
     * 特种设备管理员
     */
    public static final String SPECIAL_EQUIPMENT_ADMINISTRATOR = "role031";
    public static final String EMPTY_ERROR_MSG = "第%s行 %s 为空";
    public static final String DATE_FORMAT_ERROR_MSG = "第%s行 %s 时间格式不正确";
    public static final List<SimpleDateFormat> DATE_FORMAT_LIST = new ArrayList<SimpleDateFormat>() {{
        add(new SimpleDateFormat("yyyy-MM-dd"));
        add(new SimpleDateFormat("yyyy/MM/dd"));
        add(new SimpleDateFormat("yyyy.MM.dd"));
        add(new SimpleDateFormat("yyyyMMdd"));
    }};
    private final Logger log = LoggerFactory.getLogger(this.getClass().getName());
    @Autowired
    @Lazy
    private PointCheckService pointCheckService;
    @Autowired
    private CheckItemsDao checkItemsDao;
    @Autowired
    private SaveCheckCommonDao saveCheckCommonDao;
    @Autowired
    private SysDictServiceImpl sysDictService;
    @Autowired
    private ClassExcelVerifyHandler verifyHandler;

    /**
     * 设备列表删除验证是否存在点检记录
     *
     * @param ids
     */
    public String deleteValidate(List<String> ids) {
        String warnMes = null;
        List<String> retWarn = new ArrayList<>();
        if (ids != null && ids.size() > 0) {
            for (String id : ids) {
                CheckItemsDo checkItemsDo = this.getById(id);
                Map<String, Object> pcMap = new HashMap<>();
                pcMap.put("CHECKITEMID", id);
                List<PointCheckDo> pointCheckDos = (List<PointCheckDo>) pointCheckService.listByMap(pcMap);
                if (pointCheckDos != null && pointCheckDos.size() > 0) {
                    retWarn.add("设备编号：" + checkItemsDo.getCheckitemcode() + "；设备名称：" + checkItemsDo.getCheckitemname());
                } else {
                    this.removeById(id);
                }
            }
        }
        if (retWarn != null && retWarn.size() > 0) {
            warnMes = String.join("\n", retWarn);
            warnMes = warnMes + "\n" + "以上设备已存在点检记录不可删除；";
        }
        return warnMes;
    }

    /**
     * 备用设备列表
     *
     * @return
     */
    @Override
    public IPage<CheckItemsDo> spareList(Page page) {

        return checkItemsDao.spareList(page);
    }

    @Override
    public void reportEquMessage() {
        //查询所有需要提醒的设备
        List<CheckItemsTypeVo> list = this.baseMapper.selectNeedReportEqu();
        //查询设备的工厂，三个工厂的设备管理员角色下的员工ID
        Set<String> tenantIds = list.stream().map(CheckItemsDo::getTenantid).collect(Collectors.toSet());
        Map<String, List<String>> equManager = new HashMap<>();
        tenantIds.forEach(tenantId -> {
            List<String> userIdList = this.baseMapper.selectUserByTenantId(tenantId);
            equManager.put(tenantId, userIdList);
        });
        // 多个设备 只给收件人发送一条信息
//        equManager.forEach((k,v)->{
//            String content = list.stream().filter(data->k.equals(data.getTenantid())).map(item ->{
//                String typeName = ObjectUtil.isNotEmpty(item.getTypeName()) ? item.getTypeName() : "";
//                String checkItemCode = ObjectUtil.isNotEmpty(item.getCheckitemcode()) ? item.getCheckitemcode() : "";
//                String checkItemName = ObjectUtil.isNotEmpty(item.getCheckitemname()) ? item.getCheckitemname() : "";
//                String nextCheckDate = ObjectUtil.isNotEmpty(item.getNextcheckdate()) ? DateUtil.format(item.getNextcheckdate(), "yyy-MM-dd") : "";
//                return  String.format(SPECIALEQUIPMENT_CONTENT,typeName,checkItemCode,checkItemName,nextCheckDate);
//            }).collect(Collectors.joining("</br>"));
//            //发送消息
//            CXCommonUtils.sendNotify(SPECIALEQUIPMENT_TITLE,
//                    SPECIALEQUIPMENT_CONTENT_PREFIX +  content,
//                    null,
//                    equManager.get(k.getTenantid()),
//                    item.getId(),
//                    SPECIALEQUIPMENT);
//        });
        //根据设备的所属工厂给自己工厂的设备管理员发送消息
        list.forEach(item -> {
            if (ObjectUtil.isNotEmpty(item.getTenantid())) {
                String typeName = ObjectUtil.isNotEmpty(item.getTypeName()) ? item.getTypeName() : "";
                String checkItemCode = ObjectUtil.isNotEmpty(item.getCheckitemcode()) ? item.getCheckitemcode() : "";
                String checkItemName = ObjectUtil.isNotEmpty(item.getCheckitemname()) ? item.getCheckitemname() : "";
                String nextCheckDate = ObjectUtil.isNotEmpty(item.getNextcheckdate()) ? DateUtil.format(item.getNextcheckdate(), "yyy-MM-dd") : "";

                //发送消息
                CXCommonUtils.sendNotify(SPECIALEQUIPMENT_TITLE,
                        SPECIALEQUIPMENT_CONTENT_PREFIX + String.format(SPECIALEQUIPMENT_CONTENT, typeName, checkItemCode, checkItemName, nextCheckDate),
                        null,
                        equManager.get(item.getTenantid()),
                        item.getId(),
                        SPECIALEQUIPMENT);
            }
        });


    }

    @Override
    public R stateUpdateScheduledTasks() {
        // 获取所有的安全附件
        List<CheckItemsDo> safeAttachmentsDos = this.list(new LambdaQueryWrapper<CheckItemsDo>().eq(CheckItemsDo::getSource, 1));
        if (safeAttachmentsDos.isEmpty()) {
            return R.ok("暂无设备需要检验");
        }
        // 对数据按照租户ID 分组
        Map<String, List<CheckItemsDo>> dataMap = safeAttachmentsDos.stream().collect(Collectors.groupingBy(CheckItemsDo::getTenantid));
        List<CheckItemsDo> saveData = new ArrayList<>();
        // 发送消息提醒的参数
        Map<CheckItemsDo, List<String>> sendNotifyMap = new HashMap<CheckItemsDo, List<String>>();
        dataMap.forEach((k, v) -> {
            //获取工厂对应的 安全管理员和特种设备管理员
            List<UserMailOrgVo> userInfO = saveCheckCommonDao.getUserInfoByTenantIdAndRoleCode(k, ROLE_CODE_LIST);
            Date manufacturedate;
            long daysBetween;
            List<String> alertUser;
            for (CheckItemsDo data : v) {
                manufacturedate = data.getNextcheckdate();
                LocalDate date1 = manufacturedate.toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDate();
                daysBetween = ChronoUnit.DAYS.between(LocalDate.now(), date1);
                if (daysBetween <= 30) {
                    data.setReminderstate(HIGH);
                    alertUser = userInfO.stream().map(UserMailOrgVo::getId).collect(Collectors.toList());
                    sendNotifyMap.put(data, alertUser);
                } else if (daysBetween <= 90) {
                    alertUser = userInfO.stream().filter(user -> SPECIAL_EQUIPMENT_ADMINISTRATOR.equals(user.getRoleCode())).map(UserMailOrgVo::getId).collect(Collectors.toList());
                    sendNotifyMap.put(data, alertUser);
                    data.setReminderstate(MIDDLE);
                } else {
                    data.setReminderstate(LOW);
                }

                saveData.add(data);
            }
        });
        this.updateBatchById(saveData);
        sendNotify(sendNotifyMap);
        return R.ok();
    }

    public void sendNotify(Map<CheckItemsDo, List<String>> sendNotifyMap) {
        if (sendNotifyMap.isEmpty()) {
            return;
        }
        // 获取数据字典
        List<SysDictDo> aspCheckItemType = sysDictService.getDictsByType("asp_check_item_type");
        sendNotifyMap.forEach((k, v) -> {
            SysDictDo sysDictDo = aspCheckItemType.stream().filter(info -> info.getValue().equals(k.getCheckitemtype())).findFirst().orElse(null);

            // 发送消息提醒
            CXCommonUtils.sendNotify(EQUIPMENT_CHECK_TITLE,
                    String.format(EQUIPMENT_CHECK_CONTENT, sysDictDo == null ? "" : sysDictDo.getName(), k.getCheckitemcode(), k.getCheckitemname(),
                            k.getNextcheckdate()), null, v.stream().distinct().collect(Collectors.toList()),
                    k.getId(), SPECIALEQUIPMENT, false);
        });
    }


    @Override
    public R importExcel(Map<String, String> queryParameter) {
        String fileId = queryParameter.getOrDefault("fileId", "");
        CXCommonUtils.parameterCheck(() -> StringUtils.isBlank(fileId), "请上传文件", "文件ID为空");
        SysFileItemDo fileItemDo = saveCheckCommonDao.getFileItemByID(fileId);
        CXCommonUtils.parameterCheck(() -> fileItemDo == null, "文件解析有误!请联系管理员", "无法根据文件ID:{},获取到文件信息", fileId);
        String realPath = fileItemDo.getFullpath();
        CXCommonUtils.parameterCheck(() -> StringUtils.isBlank(realPath), "文件解析有误!请联系管理员", "根据文件ID:{},无法获取到文件路径", fileId);
        File file = new File(realPath);
        CXCommonUtils.parameterCheck(() -> !file.exists(), "文件解析有误!请联系管理员", "根据文件地址:{},无法获取文件", realPath);
        ImportParams params = new ImportParams();
        params.setTitleRows(0);
        params.setHeadRows(1);
        params.setVerifyHandler(verifyHandler);
        List<ImportCheckItemsVo> userList = ExcelImportUtil.importExcel(file, ImportCheckItemsVo.class, params);
        log.info("userList:{}", userList);
        // 存放异常数据
        List<String> errorMsgList = new ArrayList<>();
        // 解析后的数据
        List<CheckItemsDo> saveList = new ArrayList<>();
        ImportCheckItemsVo importCheckItemsVo;
        SysUserVo currentUser = CXCommonUtils.getCurrentUser();
        String tenantId = currentUser.getOrganizationDoList().get(0).getFullpid().split(",")[1];
        //获取区域信息
        Map<String, String> checkAreaMap = saveCheckCommonDao.getCheckArea(tenantId).stream().collect(Collectors.toMap(CheckAreaVo::getName, CheckAreaVo::getId, (v1, v2) -> v1));
        // 获取组织机构信息
        Map<String, String> orgMap = null;
        //saveCheckCommonDao.getOrgByTenantId(tenantId).stream().collect(Collectors.toMap(SysOrganizationDo::getFullname, SysOrganizationDo::getId, (v1, v2) -> v1));
        // 用户信息
        Map<String, String> userMap = null;
        // saveCheckCommonDao.getUserInfoByTenantId(tenantId).stream().collect(Collectors.toMap(SysUserDo::getName, SysUserDo::getId, (v1, v2) -> v1));
        // 设备使用状态数据字典
        Map<String, String> deviceStatusDict = sysDictService.getDictsByType(SBSYZT_DICT).stream().collect(Collectors.toMap(SysDictDo::getName, SysDictDo::getValue, (v1, v2) -> v1));
        Map<String, String> deviceTypeDict = sysDictService.getDictsByType(TZSBLB_DICT).stream().collect(Collectors.toMap(SysDictDo::getName, SysDictDo::getValue, (v1, v2) -> v1));
        Map<String, String> conclusionTypeDict = sysDictService.getDictsByType(JYJL_DICT).stream().collect(Collectors.toMap(SysDictDo::getName, SysDictDo::getValue, (v1, v2) -> v1));
        Map<String, String> typeDict = sysDictService.getDictsByType(CHECK_ITEM_TYPE_DICT).stream().collect(Collectors.toMap(SysDictDo::getName, SysDictDo::getValue, (v1, v2) -> v1));

        for (int i = 0; i < userList.size(); i++) {
            importCheckItemsVo = userList.get(i);
            // 数据格式校验
            checkDataAccuracy(i + 1, importCheckItemsVo, errorMsgList);

            if (StringUtils.isNotBlank(importCheckItemsVo.getTreeName()) && !checkAreaMap.containsKey(importCheckItemsVo.getTreeName())) {
                errorMsgList.add("第" + (i + 1) + "行树表名称不存在");
            }
            if (errorMsgList.isEmpty()) {
                saveList.add(voToDo(importCheckItemsVo, checkAreaMap, tenantId, deviceStatusDict,
                        deviceTypeDict, conclusionTypeDict, typeDict));
            }
        }
        if (!errorMsgList.isEmpty()) {
            log.error("excel数据异常:{}", errorMsgList);
            throw new CustomException(StringUtils.join(errorMsgList, ";"));
        }
        if (!saveList.isEmpty()) {
            this.saveBatch(saveList, 100);
        }

        return R.ok();
    }

    private void checkDataAccuracy(int index, ImportCheckItemsVo checkItemsVo, List<String> errorMsgList) {
        if (StringUtils.isBlank(checkItemsVo.getTreeName())) {
            errorMsgList.add(String.format(EMPTY_ERROR_MSG, index, "左侧树表"));
        }
        if (StringUtils.isBlank(checkItemsVo.getCheckitemname())) {
            errorMsgList.add(String.format(EMPTY_ERROR_MSG, index, "设备名称"));
        }
        if (StringUtils.isBlank(checkItemsVo.getCheckitemtype())) {
            errorMsgList.add(String.format(EMPTY_ERROR_MSG, index, "设备类型"));
        }
        if (StringUtils.isBlank(checkItemsVo.getDevicecheckstr())) {
            errorMsgList.add(String.format(EMPTY_ERROR_MSG, index, "检验日期"));
        }
        if (StringUtils.isBlank(checkItemsVo.getNextcheckstr())) {
            errorMsgList.add(String.format(EMPTY_ERROR_MSG, index, "下次检验日期"));
        }
        if (StringUtils.isBlank(checkItemsVo.getWarnstr())) {
            errorMsgList.add(String.format(EMPTY_ERROR_MSG, index, "预计提醒日期"));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(checkItemsVo.getDevicecheckstr()) && dateFormatValidation(checkItemsVo.getDevicecheckstr()) == null) {
            errorMsgList.add(String.format(DATE_FORMAT_ERROR_MSG, index, "检验日期"));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(checkItemsVo.getNextcheckstr()) && dateFormatValidation(checkItemsVo.getNextcheckstr()) == null) {
            errorMsgList.add(String.format(DATE_FORMAT_ERROR_MSG, index, "下次检验日期"));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(checkItemsVo.getWarnstr()) && dateFormatValidation(checkItemsVo.getWarnstr()) == null) {
            errorMsgList.add(String.format(DATE_FORMAT_ERROR_MSG, index, "预计提醒日期"));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(checkItemsVo.getDevicecreatestr()) && dateFormatValidation(checkItemsVo.getDevicecreatestr()) == null) {
            errorMsgList.add(String.format(DATE_FORMAT_ERROR_MSG, index, "设备制造日期"));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(checkItemsVo.getDeviceinstallstr()) && dateFormatValidation(checkItemsVo.getDeviceinstallstr()) == null) {
            errorMsgList.add(String.format(DATE_FORMAT_ERROR_MSG, index, "设备安装日期"));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(checkItemsVo.getDevicestartstr()) && dateFormatValidation(checkItemsVo.getDevicestartstr()) == null) {
            errorMsgList.add(String.format(DATE_FORMAT_ERROR_MSG, index, "开始使用日期"));
        }
    }

    private Date dateFormatValidation(String strDate) {
        Date date = null;
        for (SimpleDateFormat simpleDateFormat : DATE_FORMAT_LIST) {
            try {
                date = simpleDateFormat.parse(strDate);
                break;
            } catch (Exception e) {
                log.info("时间格式不正确");
            }
        }
        return date;
    }

    /**
     * vo 转 do
     *
     * @param checkArea
     * @param vo
     * @return
     */
    private CheckItemsDo voToDo(ImportCheckItemsVo vo,
                                Map<String, String> checkAreaMap,
                                String tenantId,
                                Map<String, String> deviceStatusDict,
                                Map<String, String> deviceTypeDict,
                                Map<String, String> conclusionTypeDict,
                                Map<String, String> typeDict) {
        CheckItemsDo checkItemsDo = new CheckItemsDo();
        BeanUtils.copyProperties(vo, checkItemsDo);
        checkItemsDo.setId(SecurityUtil.getUUID());
        checkItemsDo.setTenantid(tenantId);
        checkItemsDo.setCreatedate(new Date());
        checkItemsDo.setCheckitemareaid(checkAreaMap.get(vo.getTreeName()));
        checkItemsDo.setSource("1");
        if (StringUtils.isNotBlank(vo.getCheckitemtype())) {
            checkItemsDo.setCheckitemtype(typeDict.get(vo.getCheckitemtype()));
        }
        if (StringUtils.isNotBlank(vo.getDevicestatus())) {
            checkItemsDo.setDevicestatus(deviceStatusDict.get(vo.getDevicestatus()));
        }
        if (StringUtils.isNotBlank(vo.getCategory())) {
            checkItemsDo.setCategory(deviceTypeDict.get(vo.getCategory()));
        }
        if (StringUtils.isNotBlank(vo.getInspectresult())) {
            checkItemsDo.setInspectresult(conclusionTypeDict.get(vo.getInspectresult()));
        }

        if (StringUtils.isNotBlank(vo.getNextcheckstr())) {
            checkItemsDo.setNextcheckdate(dateFormatValidation(vo.getNextcheckstr()));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(vo.getWarnstr())) {
            checkItemsDo.setWarndate(dateFormatValidation(vo.getWarnstr()));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(vo.getDevicecreatestr())) {
            checkItemsDo.setDevicecreatedate(dateFormatValidation(vo.getDevicecreatestr()));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(vo.getDeviceinstallstr())) {
            checkItemsDo.setDeviceinstalldate(dateFormatValidation(vo.getDeviceinstallstr()));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(vo.getDevicestartstr())) {
            checkItemsDo.setDevicestartdate(dateFormatValidation(vo.getDevicestartstr()));
        }
        // 校验时间格式是否正确
        if (StringUtils.isNotBlank(vo.getDevicecheckstr())) {
            checkItemsDo.setDevicecheckdate(dateFormatValidation(vo.getDevicecheckstr()));
        }

        LocalDate date1 = checkItemsDo.getNextcheckdate().toInstant()
                .atZone(ZoneId.systemDefault())
                .toLocalDate();
        long daysBetween = ChronoUnit.DAYS.between(LocalDate.now(), date1);
        if (daysBetween <= 30) {
            checkItemsDo.setReminderstate(HIGH);
        } else if (daysBetween <= 60) {
            checkItemsDo.setReminderstate(MIDDLE);
        } else {
            checkItemsDo.setReminderstate(LOW);
        }
        return checkItemsDo;
    }
}
