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

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.rongwei.bscommon.sys.config.LinkedServerConfig;
import com.rongwei.bscommon.sys.dao.QuShiftLeaderDao;
import com.rongwei.bscommon.sys.dao.QuestionCommonSqlDao;
import com.rongwei.bscommon.sys.dao.sqlserver.EQUDao;
import com.rongwei.bscommon.sys.fegin.SysNotifyFeginService;
import com.rongwei.bscommon.sys.service.MesDataSyncService;
import com.rongwei.bscommon.sys.utils.NoticeUtils;
import com.rongwei.bscommon.sys.utils.QuestionUtils;
import com.rongwei.bsentity.domin.*;
import com.rongwei.bsentity.vo.EQUVo;
import com.rongwei.bsentity.vo.FiveWhyVo;
import com.rongwei.rwadmincommon.system.domain.SysOrganizationDo;
import com.rongwei.rwadmincommon.system.domain.SysUserDo;
import com.rongwei.rwcommon.base.BaseDo;
import com.rongwei.rwcommon.utils.SecurityUtil;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.rongwei.bscommon.sys.utils.QuestionConstants.FACTORY_NAME;
import static com.rongwei.bscommon.sys.utils.QuestionConstants.MES.DEFAULT_USER;
import static com.rongwei.bscommon.sys.utils.QuestionConstants.MES.EQU_TABLE_NAME;
import static com.rongwei.bscommon.sys.utils.QuestionConstants.ProblemLevel.FACTORY;
import static com.rongwei.bscommon.sys.utils.QuestionConstants.ProblemLevel.WORKSHOP;
import static com.rongwei.bscommon.sys.utils.QuestionConstants.Shift.*;
import static com.rongwei.bscommon.sys.utils.QuestionConstants.TriggerLevel.PS;
import static com.rongwei.bscommon.sys.utils.QuestionConstants.TriggerLevel.RCA;
import static com.rongwei.bscommon.sys.utils.QuestionUtils.dateConvertStr;
import static com.rongwei.bscommon.sys.utils.QuestionUtils.save5W1H;

/**
 * MesDataSyncService class
 *
 * @author XH
 * @date 2023/03/25
 */
@RefreshScope
@Service
public class MesEQUDataSyncServiceImpl implements MesDataSyncService {
    private final Logger log = LoggerFactory.getLogger(this.getClass().getName());
    @Autowired
    private EQUDao equDao;
    @Autowired
    private LinkedServerConfig linkedServerConfig;
    @Autowired
    private SrmIssuesListServiceImpl srmIssuesListService;
    @Autowired
    private QuShiftLeaderDao quShiftLeaderDao;
    @Autowired
    private QuSyncMesErrorServiceImpl quSyncMesErrorService;
    @Autowired
    private QuestionCommonSqlDao questionCommonSqlDao;
    @Autowired
    private QuShiftServiceImpl quShiftService;
    @Autowired
    private QuShiftDetailServiceImpl quShiftDetailService;
    @Autowired
    private NoticeUtils noticeUtils;

    /**
     * 2.5H<生产停机<4H(PS) 9000秒 车间级
     */
    public static final long EQU_DAMAGE_STANDARD = 9000;

    /**
     * 2.5H<生产停机<4H(PS) 14400秒 车间级
     */
    public static final long EQU_DAMAGE_RCA_STANDARD = 14400;
    /**
     * 生产停机≥ 16H 57600秒 工厂级别
     */
    public static final long EQU_DAMAGE_RCA_FACTORY_STANDARD = 57600;

    public static final String QUESTION_TITLE = "卷号:%s----缺陷名称:%s";
    @Autowired
    private SysNotifyFeginService sysNotifyFeginService;

    /**
     * 设备故障同步逻辑
     *
     * @param startDate
     * @param endDate
     */
    @Override
    public void syncMesData(Date startDate, Date endDate) {
        // 如果没有传 时间格式 默认用 当前时间-3天 作为起始时间
        if (startDate == null || endDate == null) {
            endDate = new Date();
            Calendar cal = Calendar.getInstance();
            cal.setTime(endDate);
            cal.add(Calendar.DATE, -3);// 24小时制
            startDate = cal.getTime();
        }
        // 获取时间段内的所有质量报废信息
        List<EQUVo> mesData = equDao.selectInfoByDate(linkedServerConfig.getDBLink(), linkedServerConfig.getDataBaseName(), linkedServerConfig.getDataBaseModel(), startDate, endDate);
        log.debug("获取到设备故障：{}",mesData);
        // 筛选停机时间大于等于 2.5H的数据
        mesData = mesData.stream().filter(info -> calculateSeconds(info.getFinishTime(), info.getStartTime()) >= EQU_DAMAGE_STANDARD).collect(Collectors.toList());
        if (mesData.isEmpty()) {
            log.debug("时间段内：{}-{}暂无设备故障", startDate, endDate);
            return;
        }
        List<String> mesIds = mesData.stream().map(EQUVo::getOpsID).collect(Collectors.toList());
        // 根据mesId 获取 问题列表判断当前问题是已经记录过
        List<SrmIssuesListDo> srmIssuesRecord = srmIssuesListService.getListByMesId(mesIds);
        // 同步失败的 数据信息
        List<QuSyncMesErrorDo> quSyncMesErrorList = new ArrayList<>();
        // 需要加入issue 列表的记录
        List<SrmIssuesListDo> saveIssueList = new ArrayList<>();

        List<FiveWhyVo> fiveWhyVoList = new ArrayList<>();

        List<EQUVo> waitHandleData = filterQuaBasicErrorData(mesData, quSyncMesErrorList, srmIssuesRecord);


        // 获取班组对应的员工信息
        List<QuShiftLeaderDo> quShiftLeaderDos = quShiftLeaderDao.selectList(null);
        // 获取班组对应的组织机构信息
        List<SysOrganizationDo> orgInfo = questionCommonSqlDao.getOrgInfo(null);

        // 获取班组为空的数据
        // List<EQUVo> emptyShiftNoData = waitHandleData.stream().filter(info -> StringUtils.isBlank(info.getShiftNo())).collect(Collectors.toList());
        // 获取班组为空的数据
        // List<EQUVo> nomalShiftNoData = waitHandleData.stream().filter(info -> StringUtils.isNotBlank(info.getShiftNo())).collect(Collectors.toList());
        // TODO:XH add 2023/5/8 统一按照不含班组的数据处理
        // 含有班组数据处理
        // mesDataHandle(nomalShiftNoData, quShiftLeaderDos, saveIssueList, quSyncMesErrorList, orgInfo, true);
        // 不含班组数据处理
        mesDataHandle(waitHandleData, quShiftLeaderDos, saveIssueList, quSyncMesErrorList, orgInfo, true, fiveWhyVoList);
        // 存表
        save5W1H(saveIssueList.stream().map(SrmIssuesListDo::getId).collect(Collectors.toList()), fiveWhyVoList);
        srmIssuesListService.saveBatch(saveIssueList);
        quSyncMesErrorService.saveBatch(quSyncMesErrorList);

        //消息提醒
        noticeUtils.notice(saveIssueList);
    }

    /**
     * 正常数据 处理
     *
     * @param mesDatas
     * @param quShiftLeaderDos
     * @param saveIssueList
     * @param quSyncMesErrorList
     */
    public void mesDataHandle(List<EQUVo> mesDatas, List<QuShiftLeaderDo> quShiftLeaderDos,
                              List<SrmIssuesListDo> saveIssueList, List<QuSyncMesErrorDo> quSyncMesErrorList,
                              List<SysOrganizationDo> orgInfo, boolean emptyShiftNo,
                              List<FiveWhyVo> fiveWhyVoList) {
        mesDatas.forEach(info -> {
            // 机台号
            String machineNo = info.getMachineNo();
            List<SysOrganizationDo> orgs = getOrgInfoByRoptionCode(orgInfo, machineNo);
            if (orgs.size() != 1) {
                log.debug("根据机台号:{}没有获取到工段信息或获取到多个工段信息", machineNo);
                quSyncMesErrorList.add(orgErrorDo(info.getOpsID(), String.format("根据机台号:{%s}没有获取到工段信息或获取到多个工段信息", machineNo)));
                return;
            }
            SysOrganizationDo sysOrganizationDo = orgs.get(0);
            // 获取班组信息
            QuShiftLeaderDo quShiftLeaderDo = null;
            String shiftNo;
            if (emptyShiftNo) {
                // 根据发生日期 获取所在班组信息
                quShiftLeaderDo = getShiftNo(info.getFinishTime(), machineNo, sysOrganizationDo, quShiftLeaderDos);
                if (quShiftLeaderDo == null) {
                    log.debug("根据异常结束时间 :{} 和机台号：{} 没有获取前对应的班组信息", dateConvertStr(info.getFinishTime(), "yyyy-MM-dd HH:mm:ss"), machineNo);
                    quSyncMesErrorList.add(orgErrorDo(info.getOpsID(), String.format("根据异常结束时间 :{%s} 和机台号：{%s} 没有获取前对应的班组信息", dateConvertStr(info.getFinishTime(), "yyyy-MM-dd HH:mm:ss"), machineNo)));
                    return;
                }
            } else {
                shiftNo = info.getShiftNo();
                // 获取对应的班组信息
                List<QuShiftLeaderDo> quShiftLeaderDoList = quShiftLeaderDos.stream().filter(shiftLeaderDo -> shiftLeaderDo.getShifttype().contains(shiftNo) &&
                        shiftLeaderDo.getSection().equals(sysOrganizationDo.getId())).collect(Collectors.toList());
                if (quShiftLeaderDoList.isEmpty()) {
                    log.debug("根据班组 :{}和机台号：{} 没有获取前对应的班组信息", shiftNo, machineNo);
                    quSyncMesErrorList.add(orgErrorDo(info.getOpsID(), String.format("根据班组 :{%s}和机台号：{%s} 没有获取前对应的班组信息", shiftNo, machineNo)));
                    return;
                }
                quShiftLeaderDo = quShiftLeaderDoList.get(0);
            }
            SrmIssuesListDo srmIssuesListDo = recordIssue(info, quShiftLeaderDo, sysOrganizationDo);
            // 记录数据
            saveIssueList.add(srmIssuesListDo);
            FiveWhyVo fiveWhyVo = new FiveWhyVo();
            fiveWhyVo.setId(srmIssuesListDo.getId());
            fiveWhyVo.setWhoDesc(info.getCreateUser());
            fiveWhyVo.setWhatDesc(info.getOpUuid() + "-" + info.getDescription());
            fiveWhyVoList.add(fiveWhyVo);
        });
    }


    /**
     * 根据 机台号和 事故发生时间获取当前班组是 甲乙丙丁
     *
     * @param startDate         发生时间
     * @param machineNo         机台号
     * @param sysOrganizationDo 机台号对应组织机构
     * @param quShiftLeaderDos  所有的班组信息
     * @return
     */
    public QuShiftLeaderDo getShiftNo(Date startDate, String machineNo, SysOrganizationDo sysOrganizationDo,
                                      List<QuShiftLeaderDo> quShiftLeaderDos) {
        log.debug("开始根据结束时间：{},机台信息:{} 获取班长",startDate,sysOrganizationDo.getFullname());
        Calendar instance = Calendar.getInstance();
        instance.setTime(startDate);
        // 根据开始日期判断这条记录所处的班别
        int year = instance.get(Calendar.YEAR);
        int month = instance.get(Calendar.MONTH) + 1;
        int date = instance.get(Calendar.DATE);
        String sectionId = sysOrganizationDo.getId();
        // 通过年月 车间ID 获取排班信息
        List<QuShiftDo> quShiftDos = quShiftService.list(new LambdaQueryWrapper<QuShiftDo>()
                .eq(BaseDo::getDeleted, "0")
                .eq(QuShiftDo::getYearnum, year)
                .eq(QuShiftDo::getMonthnum, month)
                .eq(QuShiftDo::getSection, sectionId));
        if (quShiftDos.size() != 1) {
            return null;
        }
        QuShiftDo quShiftDo = quShiftDos.get(0);
        String shiftId = quShiftDo.getId();
        // 根据日期获取当天具体的排班信息
        List<QuShiftDetailDo> list = quShiftDetailService.list(new LambdaQueryWrapper<QuShiftDetailDo>()
                .eq(BaseDo::getDeleted, "0")
                .eq(QuShiftDetailDo::getDaynum, date)
                .eq(QuShiftDetailDo::getShiftid, shiftId));
        if (list.size() != 1) {
            return null;
        }
        // 获取班长
        String shiftLeaderId = getShiftLeaderByDate(list.get(0), startDate);
        if (StringUtils.isBlank(shiftLeaderId)) {
            return null;
        }
        // 根据班组和工段 获取对应班组信息
        QuShiftLeaderDo quShiftLeaderDo = quShiftLeaderDos.stream()
                .filter(info -> info.getSection().equals(sectionId) && info.getLeaderid().equals(shiftLeaderId))
                .findFirst()
                .orElse(null);
        return quShiftLeaderDo;
    }

    /**
     * 根据发生时间获取班长ID
     *
     * @return
     */
    private String getShiftLeaderByDate(QuShiftDetailDo quShiftDetailDo, Date occurrenceTime) {
        log.debug("根据异常结束时间：{}和当天的排班信息：{} 获取对应的班长", occurrenceTime, quShiftDetailDo);
        int minuteSecond = new BigDecimal(SHIFT_FORMAT.format(occurrenceTime)).intValue();
        Function<QuShiftDetailDo, String> getShiftLaderIdFunction = null;
        // 发生时间在8点30-16点30 早班
        if (minuteSecond >= MORNING_SHIFT && minuteSecond < MIDDLE_SHIFT) {
            getShiftLaderIdFunction = QuShiftDetailDo::getForeshift;
        }
        // 发生时间在大于16点30小于0点30 中班
        if (minuteSecond >= MIDDLE_SHIFT || minuteSecond < NIGHT_SHIFT) {
            getShiftLaderIdFunction = QuShiftDetailDo::getMiddleshift;
        }
        // 发生时间在 0点30-8点30  晚班
        if (minuteSecond >= NIGHT_SHIFT && minuteSecond <= MORNING_SHIFT) {
            getShiftLaderIdFunction = QuShiftDetailDo::getNightshift;
        }
        if (getShiftLaderIdFunction == null) {
            return "";
        }
        return getShiftLaderIdFunction.apply(quShiftDetailDo);
    }

    /**
     * 组织错误数据
     *
     * @param id
     * @param errorDesc
     * @return
     */
    private QuSyncMesErrorDo orgErrorDo(String id, String errorDesc) {
        QuSyncMesErrorDo errorDo = new QuSyncMesErrorDo();
        errorDo.setId(SecurityUtil.getUUID());
        errorDo.setErrordesc(errorDesc);
        errorDo.setMestablename(EQU_TABLE_NAME);
        errorDo.setMesid(id);
        return errorDo;
    }

    /**
     * 筛选出质量报废有数据错误的数据
     *
     * @param mesData
     * @param quSyncMesErrorDaos
     * @param srmIssuesRecord    已生成过记录的问题信息
     * @return
     */
    private List<EQUVo> filterQuaBasicErrorData(List<EQUVo> mesData, List<QuSyncMesErrorDo> quSyncMesErrorDaos, List<SrmIssuesListDo> srmIssuesRecord) {
        // 机台号为空的数据
        List<String> emptyMachineNoData = mesData.stream().filter(info -> info.getMachineNo() == null).map(EQUVo::getOpsID).collect(Collectors.toList());
        // 开始时间或者结束时间为空的数据
        List<String> emptyDateData = mesData.stream().filter(info -> info.getStartTime() == null || info.getFinishTime() == null)
                .map(EQUVo::getOpsID).collect(Collectors.toList());
        if (!emptyMachineNoData.isEmpty()) {
            quSyncMesErrorDaos.add(orgErrorDo(String.join(",", emptyMachineNoData), "机台号为空"));
        }
        if (!emptyDateData.isEmpty()) {
            quSyncMesErrorDaos.add(orgErrorDo(String.join(",", emptyDateData), "记录创建人为空"));
        }
        List<String> errorIds = emptyMachineNoData;
        errorIds.addAll(emptyDateData);
        errorIds.addAll(srmIssuesRecord.stream().map(SrmIssuesListDo::getMesid).collect(Collectors.toList()));
        return mesData.stream().filter(info -> !errorIds.contains(info.getOpsID())).collect(Collectors.toList());
    }

    /**
     * 根据班组 和机台号获取 对应排班信息
     *
     * @param organizationDoList
     * @param machineNo
     * @return
     */
    private List<SysOrganizationDo> getOrgInfoByRoptionCode(List<SysOrganizationDo> organizationDoList, String machineNo) {
        // 解决大小写字符不一致 全部转为大写
        List<SysOrganizationDo> organizationDos = organizationDoList.stream().filter(info -> StringUtils.isNotBlank(info.getRoption()) &&
                        Arrays.asList(((String) JSON.parseObject(info.getRoption(), HashMap.class).getOrDefault("code", "")).toLowerCase(Locale.ROOT).split(",")).contains(machineNo.toLowerCase(Locale.ROOT)))
                .collect(Collectors.toList());
        return organizationDos;
    }

    /***
     * 记录数据
     * @param mesData
     * @param quShiftLeaderDo
     * @param section
     * @return
     */
    private SrmIssuesListDo recordIssue(EQUVo mesData, QuShiftLeaderDo quShiftLeaderDo, SysOrganizationDo section) {
        // 区分工厂级还是车间级 大于等于4小时为RCA
        String triggerLevel = calculateSeconds(mesData.getFinishTime(), mesData.getStartTime()) >= EQU_DAMAGE_RCA_STANDARD ? RCA : PS;
        // 大于等于16小时 为工厂级
        String level = calculateSeconds(mesData.getFinishTime(), mesData.getStartTime()) >= EQU_DAMAGE_RCA_FACTORY_STANDARD ? FACTORY : WORKSHOP;
        SrmIssuesListDo srmIssuesListDo = new SrmIssuesListDo();
        srmIssuesListDo.setId(SecurityUtil.getUUID());
        // 问题分类
        srmIssuesListDo.setTriggerlevel(triggerLevel);
        // 类型 车间级
        srmIssuesListDo.setLevle(level);
        srmIssuesListDo.setFailuredate(mesData.getStartTime());
        srmIssuesListDo.setPsinitiator(quShiftLeaderDo.getShiftleader());
        srmIssuesListDo.setPsinitiatorid(quShiftLeaderDo.getLeaderid());
        srmIssuesListDo.setCreatedate(new Date());
        srmIssuesListDo.setModifydate(new Date());
        srmIssuesListDo.setCreateuserid(DEFAULT_USER);
        srmIssuesListDo.setCreateusername(DEFAULT_USER);
        srmIssuesListDo.setModifyusername(DEFAULT_USER);
        srmIssuesListDo.setModifyuserid(DEFAULT_USER);
        srmIssuesListDo.setFactory(FACTORY_NAME);
        srmIssuesListDo.setMesid(mesData.getOpsID());
        srmIssuesListDo.setFollowupdays("30");
        // 故障时间 分钟
        srmIssuesListDo.setStopminutes((int) (calculateSeconds(mesData.getFinishTime(), mesData.getStartTime()) / 60));
        srmIssuesListDo.setWorkshopordepartment(quShiftLeaderDo.getWorkshop());
        srmIssuesListDo.setIssuestatus("10");
        srmIssuesListDo.setWorkshopsection(section.getId());
        // modify at [ 2023-10-19 13:49:12 ] by xh: 改为直接使用缺陷名称
        srmIssuesListDo.setQuestiontitle(mesData.getDescription());
        //srmIssuesListDo.setQuestiontitle( String.format(QUESTION_TITLE,mesData.getOpUuid(),mesData.getDescription()));
        List<SysUserDo> dept_ps_admin = QuestionUtils.getPSManagerByOrgId(section, "dept_ps_admin");
        // PS负责人
        srmIssuesListDo.setDeppsresperson(dept_ps_admin.stream().map(SysUserDo::getName).collect(Collectors.joining(",")));
        srmIssuesListDo.setDeppsrespersonids(dept_ps_admin.stream().map(SysUserDo::getId).collect(Collectors.joining(",")));
        List<SysUserDo> dept_leader = QuestionUtils.getPSManagerByOrgId(section, "dept_leader");
        // 部门经理
        srmIssuesListDo.setLeader(dept_leader.stream().map(SysUserDo::getName).collect(Collectors.joining(",")));
        srmIssuesListDo.setTenantid(dept_leader.stream().map(SysUserDo::getId).collect(Collectors.joining(",")));
        //srmIssuesListDo.setQuestionno(QuestionUtils.streamCodeGeneration("srm_issues_list_QUESTIONNO", "@{date:yyyy}@{serialNumber:#000}"));
        return srmIssuesListDo;
    }

    /**
     * 计算时间段相差多少秒
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public long calculateSeconds(Date startDate, Date endDate) {
        if (startDate == null || endDate == null) {
            return 0;
        }
        return (startDate.getTime() - endDate.getTime()) / 1000;
    }
}
