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

import com.alibaba.fastjson.JSON;
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.OPRDao;
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.QuShiftLeaderDo;
import com.rongwei.bsentity.domin.QuSyncMesErrorDo;
import com.rongwei.bsentity.domin.SrmIssuesListDo;
import com.rongwei.bsentity.vo.FiveWhyVo;
import com.rongwei.bsentity.vo.OPRVo;
import com.rongwei.rwadmincommon.system.domain.SysOrganizationDo;
import com.rongwei.rwadmincommon.system.domain.SysUserDo;
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.util.*;
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.OPR_TABLE_NAME;
import static com.rongwei.bscommon.sys.utils.QuestionConstants.ProblemLevel.WORKSHOP;
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 MesOPRDataSyncServiceImpl implements MesDataSyncService {
    private final Logger log = LoggerFactory.getLogger(this.getClass().getName());
    @Autowired
    private OPRDao oprDao;
    @Autowired
    private LinkedServerConfig linkedServerConfig;
    @Autowired
    private SrmIssuesListServiceImpl srmIssuesListService;
    @Autowired
    private QuShiftLeaderDao quShiftLeaderDao;
    @Autowired
    private QuSyncMesErrorServiceImpl quSyncMesErrorService;
    @Autowired
    private QuestionCommonSqlDao questionCommonSqlDao;
    @Autowired
    private MesEQUDataSyncServiceImpl mesEQUDataSyncService;
    @Autowired
    private NoticeUtils noticeUtils;

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

    /**
     * 2.5H<生产停机<4H(PS) 900秒
     */
    public static final long OPR_DAMAGE_RCA_STANDARD = 14400;
    public static final String QUESTION_TITLE = "卷号:%s----缺陷名称:%s";
    /**
     * 质量报废同步逻辑
     *
     * @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<OPRVo> mesData = oprDao.selectInfoByDate(linkedServerConfig.getDBLink(), linkedServerConfig.getDataBaseName(), linkedServerConfig.getDataBaseModel(), startDate, endDate);
        log.debug("获取到生产损失记录：{}",mesData);
        // 筛选停机时间大于等于 2.5H的数据
        mesData = mesData.stream().filter(info -> calculateSeconds(info.getFinishTime(), info.getStartTime()) >= OPR_DAMAGE_STANDARD).collect(Collectors.toList());
        if (mesData.isEmpty()) {
            log.debug("时间段内：{}-{}暂无生产损失", startDate, endDate);
            return;
        }
        List<String> mesIds = mesData.stream().map(OPRVo::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<OPRVo> waitHandleData = filterQuaBasicErrorData(mesData, quSyncMesErrorList, srmIssuesRecord);
        // 获取班组对应的员工信息
        List<QuShiftLeaderDo> quShiftLeaderDos = quShiftLeaderDao.selectList(null);
        // 获取班组对应的组织机构信息
        List<SysOrganizationDo> orgInfo = questionCommonSqlDao.getOrgInfo(null);

        // 获取班组为空的数据
        //List<OPRVo> emptyShiftNoData = waitHandleData.stream().filter(info -> StringUtils.isBlank(info.getShiftNo())).collect(Collectors.toList());
        // 获取班组为空的数据
        // List<OPRVo> 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 nomalShiftNoData
     * @param quShiftLeaderDos
     * @param saveIssueList
     * @param quSyncMesErrorList
     */
    public void mesDataHandle(List<OPRVo> nomalShiftNoData, List<QuShiftLeaderDo> quShiftLeaderDos,
                              List<SrmIssuesListDo> saveIssueList, List<QuSyncMesErrorDo> quSyncMesErrorList,
                              List<SysOrganizationDo> orgInfo, boolean emptyShiftNo, List<FiveWhyVo> fiveWhyVoList) {
        nomalShiftNoData.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 = mesEQUDataSyncService.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
     * @return
     */
    private String getShiftNo(Date startDate, String machineNo) {
        // modify at [ 2023-03-29 14:47:04 ] by xh: 逻辑待处理
        return "甲";
    }

    ;

    /**
     * 组织错误数据
     *
     * @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(OPR_TABLE_NAME);
        errorDo.setMesid(id);
        return errorDo;
    }

    /**
     * 筛选出质量报废有数据错误的数据
     *
     * @param mesData
     * @param quSyncMesErrorDaos
     * @param srmIssuesRecord    已生成过记录的问题信息
     * @return
     */
    private List<OPRVo> filterQuaBasicErrorData(List<OPRVo> mesData, List<QuSyncMesErrorDo> quSyncMesErrorDaos, List<SrmIssuesListDo> srmIssuesRecord) {
        // 机台号为空的数据
        List<String> emptyMachineNoData = mesData.stream().filter(info -> info.getMachineNo() == null).map(OPRVo::getOpsID).collect(Collectors.toList());
        // 开始时间或者结束时间为空的数据
        List<String> emptyDateData = mesData.stream().filter(info -> info.getStartTime() == null || info.getFinishTime() == null)
                .map(OPRVo::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(OPRVo mesData, QuShiftLeaderDo quShiftLeaderDo, SysOrganizationDo section) {
        String triggerLevel = calculateSeconds(mesData.getFinishTime(), mesData.getStartTime()) >= OPR_DAMAGE_RCA_STANDARD ? RCA : PS;
        SrmIssuesListDo srmIssuesListDo = new SrmIssuesListDo();
        srmIssuesListDo.setId(SecurityUtil.getUUID());
        // 问题分类
        srmIssuesListDo.setTriggerlevel(triggerLevel);
        // 类型 车间级
        srmIssuesListDo.setLevle(WORKSHOP);
        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.setFollowupdays("30");
        srmIssuesListDo.setMesid(mesData.getOpsID());
        srmIssuesListDo.setWorkshopordepartment(quShiftLeaderDo.getWorkshop());
        srmIssuesListDo.setWorkshopsection(section.getId());
        // 故障时间 分钟
        srmIssuesListDo.setStopminutes((int) (calculateSeconds(mesData.getFinishTime(), mesData.getStartTime()) / 60));
        // modify at [ 2023-10-19 13:49:12 ] by xh: 改为直接使用缺陷名称
        srmIssuesListDo.setQuestiontitle(mesData.getDescription());
        //srmIssuesListDo.setQuestiontitle(String.format(QUESTION_TITLE,mesData.getOpUuid(),mesData.getDescription()));
        srmIssuesListDo.setIssuestatus("10");
        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;
    }
}
