package com.yeejoin.amos.supervision.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Sequence;
import com.google.common.base.Joiner;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.biz.common.enums.WorkFlowEnum;
import com.yeejoin.amos.boot.biz.common.service.IWorkflowExcuteService;
import com.yeejoin.amos.boot.biz.common.utils.RedisKey;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.boot.biz.common.workflow.feign.WorkflowFeignService;
import com.yeejoin.amos.feign.privilege.model.AgencyUserModel;
import com.yeejoin.amos.supervision.business.constants.XJConstant;
import com.yeejoin.amos.supervision.business.dao.mapper.PlanMapper;
import com.yeejoin.amos.supervision.business.dao.mapper.PointMapper;
import com.yeejoin.amos.supervision.business.dao.repository.*;
import com.yeejoin.amos.supervision.business.param.PlanInfoPageParam;
import com.yeejoin.amos.supervision.business.service.intfc.IPlanService;
import com.yeejoin.amos.supervision.common.enums.*;
import com.yeejoin.amos.boot.biz.common.enums.RuleTypeEnum;
import com.yeejoin.amos.supervision.core.async.AsyncTask;
import com.yeejoin.amos.supervision.core.common.request.AddPlanRequest;
import com.yeejoin.amos.supervision.core.common.response.PlanPointRespone;
import com.yeejoin.amos.supervision.core.util.DateUtil;
import com.yeejoin.amos.supervision.dao.entity.*;
import com.yeejoin.amos.supervision.feign.RemoteSecurityService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;

import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.stream.Collectors;

@Service("planService")
public class PlanServiceImpl implements IPlanService {

    private final static Logger log = LoggerFactory.getLogger(PlanServiceImpl.class);

    @Autowired
    private IPlanDao planDao;
    @Autowired
    private PlanMapper planMapper;

    @Autowired
    private IPlanTaskDao planTaskDao;

    @Autowired
    private IPlanTaskDetailDao planTaskDetailDao;

    @Autowired
    RemoteSecurityService remoteSecurityService;

    @Autowired
    IRouteDao iRouteDao;

    @Autowired
    IPointDao iPointDao;

    @Autowired
    private PointMapper pointMapper;

    @Autowired
    private IRoutePointDao iRoutePointDao;

    @Autowired
    private IPlanService planService;

    @Autowired
    private IWorkflowExcuteService workflowExcuteService;

    @Autowired
    private AsyncTask asyncTask;

    @Autowired
    private RulePlanService rulePlanService;

    @Autowired
    WorkflowFeignService workflowFeignService;

    @Autowired
    private IPlanAuditDao planAuditDao;

    @Autowired
    private Sequence sequence;

    @Value("${work.flow.processDefinitionKey}")
    private String processDefinitionKey;

    @Autowired
    private IPlanAuditLogDao planAuditLogDao;

    @Autowired
    RedisUtils redisUtils;

    @Override
    public Page<HashMap<String, Object>> getPlanInfo(PlanInfoPageParam param) {
        ReginParams reginParam = JSON.parseObject(redisUtils.get(RedisKey.buildReginKey(RequestContext.getExeUserId()
                , RequestContext.getToken())).toString(), ReginParams.class);
        param.setLoginUserId(reginParam.getPersonIdentity().getPersonSeq());
        long total = planMapper.countPlanInfoData(param);
        Set<String> userIds = new HashSet<>();
        List<HashMap<String, Object>> content = planMapper.getPlanInfo(param);
        content.forEach(c -> userIds.add(c.get("createBy") != null ? c.get("createBy").toString() : ""));
        userIds.remove("");
        String[] temp = Joiner.on(",").join(userIds).split(",");
        Set<String> distinctUserIds = new HashSet<>(Arrays.asList(temp));
        List<AgencyUserModel> userModels = remoteSecurityService.listUserByUserIds(Joiner.on(",").join(distinctUserIds));
        Map<String, String> userIdNameMap = userModels.stream().collect(Collectors.toMap(AgencyUserModel::getUserId, AgencyUserModel::getRealName));
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        content.forEach(c -> {
            this.buildUserName(c, "createBy", userIdNameMap);
            if (c.containsKey("status")) {
                String finishStatusDesc = PlanStatusEnum.getName(Integer.parseInt(c.get("status").toString()));
                c.put("statusDesc", finishStatusDesc);
            }
            if (c.containsKey("createDate")) {
                String createTime = dateTimeFormatter.format((TemporalAccessor) c.get("createDate"));
                c.put("createDateStr", createTime);
            }
        });
        return new PageImpl<>(content, param, total);
    }

    private void buildUserNames(HashMap<String, Object> c, String key, Map<String, String> userIdNameMap) {
        String[] userIdArray = c.get(key) != null ? c.get(key).toString().split(",") : new String[1];
        Set<String> names = new HashSet<>();
        for (String userId : userIdArray) {
            names.add(userIdNameMap.get(userId));
        }
        names.remove(null);
        names.remove("");
        c.put(key + "Name", String.join(",", names));
    }

    private void buildUserName(HashMap<String, Object> c, String key, Map<String, String> userIdNameMap) {
        String userId = c.get(key) != null ? c.get(key).toString() : "";
        c.put(key + "Name", userIdNameMap.get(userId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addPlan(HashMap<String, Object> map, ReginParams reginParams) throws Exception {
        // 新增路线
        AddPlanRequest addPlanRequest = (AddPlanRequest) map.get("param");
        Plan plan = addPlanRequest.getPlan();
        ReginParams.PersonIdentity personIdentity = reginParams.getPersonIdentity();
        String orgCode = map.get("org_code") == null ? "" : map.get("org_code").toString();
        String userId = personIdentity.getPersonSeq();
        plan.setOrgCode(orgCode);
        plan.setNextGenDate(DateUtil.getIntervalDate(new Date(), 0));
        plan.setCreateBy(userId);
		/* 设置检查单位的去重 by 陈浩 on 2022-01-24 start  bug:4320*/
        String checkUnitId=   plan.getCheckUnitId();
        if(org.apache.commons.lang3.StringUtils.isNotBlank(checkUnitId) && checkUnitId.contains(",")) {
        	  String [] checkUnitIdStr= checkUnitId.split(",");
        	  String checkUnitName=   plan.getCheckUnitName();
        	  String [] checkUnitNameStr=  checkUnitName.split(",");
        	  StringBuffer checkUnitNameStringBuffer = new StringBuffer();
        	  checkUnitNameStringBuffer.append(checkUnitNameStr[0]);
              String  validCheckUnitId = checkUnitIdStr[0];
              for (int i = 1; i < checkUnitIdStr.length; i++) {
				if(!validCheckUnitId.contains(checkUnitIdStr[i])) {
					validCheckUnitId = validCheckUnitId+","+checkUnitIdStr[i];
					checkUnitNameStringBuffer.append(","+checkUnitNameStr[i]);
				}
			}
              plan.setCheckUnitId(validCheckUnitId);  
              plan.setCheckUnitName(checkUnitNameStringBuffer.toString());
        }
      
        /* 设置检查单位的去重 by 陈浩 on 2022-01-24 start */
        addPlanRequest.setPlan(plan);
        Route route = save(addPlanRequest);
        if (!ObjectUtils.isEmpty(route)) {
            plan.setRouteId(route.getId());
            //编辑计划
            if (plan.getId() > 0) {
                Plan oriPlan = planDao.findById(plan.getId()).get();
                plan.setCreateDate(oriPlan.getCreateDate());
                plan.setCreateBy(oriPlan.getCreateBy());
                plan.setLastUpdBy(userId);
                plan.setFirstFlag(XJConstant.PLAN_FIRST_STATUS_YES);
            }
            if (XJConstant.FIX_DATE_NO.equals(plan.getIsFixedDate()) && (XJConstant.PLAN_TYPE_MONTH.equals(plan.getPlanType()) || XJConstant.PLAN_TYPE_YEAR.equals(plan.getPlanType()))) {
                plan.setDayBegin(DateUtil.formatStrToTime("00:00:00"));
                plan.setDayEnd(DateUtil.formatStrToTime("23:59:59"));
            }
            planDao.save(plan);
            Integer status = plan.getStatus();
            if (status != null && status == 1) {
                CheckTypeSuEnum checkTypeSuEnum = CheckTypeSuEnum.getEumByCode(plan.getCheckTypeId());
                DangerCheckTypeLevelEnum levelEnum = DangerCheckTypeLevelEnum.getEumByCode(plan.getCheckLevel());
                String branch = workFlowExcuteBranch(levelEnum.getCondition(), checkTypeSuEnum.getCondition());
                try {
                    String processInstanceId;
                    PlanAudit audit = planAuditDao.findByPlanId(plan.getId());
                    if (audit != null) {
                        //执行一步
                        processInstanceId = audit.getProcessInstanceId();
                        workflowExcuteService.excuteTask(processInstanceId, branch);
                        //更新时间
                        audit.setUpdateDate(new Date());
                        planAuditDao.save(audit);
                        this.getUserIdsByWorkflow(plan, processInstanceId, null, null);
                        //记录执行流水-启动节点
                        insertAuditLog(reginParams, plan, personIdentity, audit);
                    } else {
                        //启动
                        processInstanceId = workflowExcuteService.startAndComplete(processDefinitionKey, branch);
                        audit = new PlanAudit();
                        audit.setPlanId(plan.getId());
                        audit.setBusinessKey(String.valueOf(sequence.nextId()));
                        audit.setProcessDefinitionKey(processDefinitionKey);
                        audit.setProcessInstanceId(processInstanceId);
                        audit.setStartUserId(userId);
                        planAuditDao.save(audit);
                        //记录执行流水-启动节点
                        insertAuditLog(reginParams, plan, personIdentity, audit);
                        this.getUserIdsByWorkflow(plan, processInstanceId,null, null);
                    }
                } catch (Exception e) {
                    log.error("=============防火监督，计划提交，工作流启动失败！！！=============");
                }
            }
        }
    }

    /**
     * 根据工作流获取下一审核人角色下的所有用户ID =》规则推送消息
     * @return
     */
    protected List<String> getUserIdsByWorkflow (Plan plan, String processInstanceId, Integer status, Integer excuteState){
        List<String> userIds = new ArrayList<>();
        JSONObject teskObject = workflowFeignService.getTaskList(processInstanceId);
        JSONArray taskDetailArray = teskObject.getJSONArray(WorkFlowEnum.DATA.getCode());
        for (Object obj : taskDetailArray) {
            JSONObject detail = JSONObject.parseObject(JSONObject.toJSONString(obj));
            JSONArray informerList = detail.getJSONArray(WorkFlowEnum.INFORMERLIST.getCode());
            if (informerList.size() > 0) {
                userIds = informerList.stream().map(item -> {
                    JSONObject jsonItem = (JSONObject) item;
                    return jsonItem.getString("userId");
                }).collect(Collectors.toList());
            }
            try {
                if (ValidationUtil.isEmpty(status)){
                    rulePlanService.addPlanRule(plan, userIds, RuleTypeEnum.计划提交, null); // 计划提交
                } else {
                    if (PlanStatusEnum.EXAMINE_THREE.getValue() != status){
                   //     rulePlanService.addPlanAuditRule(plan, userIds, RuleTypeEnum.计划审核, ExecuteStateNameEnum.getNameByCode(excuteState)); // 计划审核
                        rulePlanService.addPlanAuditRule(plan, userIds, RuleTypeEnum.计划审核任务, ExecuteStateNameEnum.getNameByCode(excuteState)); // 计划审核任务通知
                    } else {
                        rulePlanService.addPlanAuditRule(plan, userIds, RuleTypeEnum.计划审核完成, ExecuteStateNameEnum.getNameByCode(excuteState)); // 计划审核完成
                    }
                }
            } catch (Exception e) {
                log.info("规则调用失败");
            }
        }
        return userIds;
    }

    private void insertAuditLog(ReginParams reginParams, Plan param, ReginParams.PersonIdentity personIdentity, PlanAudit audit) {
        PlanAuditLog planAuditLog = new PlanAuditLog();
        planAuditLog.setPlanAuditId(audit.getId());
        planAuditLog.setPlanId(param.getId());
        planAuditLog.setFlowTaskName("发起审核");
        planAuditLog.setExcuteUserId(param.getCreateBy());
        planAuditLog.setExcuteUserName(personIdentity.getPersonName());
        planAuditLog.setExcuteState(0);
        planAuditLog.setRoleName(reginParams.getRole().getRoleName());
        planAuditLogDao.save(planAuditLog);
    }

    /**
     * 判断走哪一种工作流WorkFlowBranchEnum
     */
    public static String workFlowExcuteBranch (String dangerCheckTypeLevel, String checkType) {
        String branch = "";
        List<Map<String, String>> enumList = WorkFlowBranchEnum.getEnumList();
        if (!ObjectUtils.isEmpty(enumList)){
            List<Map<String, String>> list = enumList.stream().filter(map ->
                    (map.get("dangerCheckTypeLevel").contains(dangerCheckTypeLevel) && map.get("checkType").contains(checkType)
            )).collect(Collectors.toList());

            if (!ObjectUtils.isEmpty(list)) {
                branch = list.get(0).get("workFlowBranch");
            }
        }
        return branch;
    }

    /**
     * 默认新增路线
     */
    public Route save(AddPlanRequest addPlanRequest) {
        Route saveRoute = new Route();
        Plan plan = addPlanRequest.getPlan();

        // 判断是新增还是修改
        if (plan.getId() > 0) {
            // 删除相关点项内容
            iRoutePointDao.delRoutePointByRouteId(plan.getRouteId());
//            iRoutePointItemDao.delRoutePointItem(plan.getRouteId());
            saveRoute.setId(plan.getRouteId());
        }

        saveRoute.setName(plan.getName());
        saveRoute.setOrgCode(plan.getOrgCode());
        saveRoute.setCreatorId(plan.getCreateBy());
        if (!ObjectUtils.isEmpty(addPlanRequest.getOwnerId())) {
            String ownerId = StringUtils.join(addPlanRequest.getOwnerId().toArray(), ",");
            saveRoute.setOwnerId(ownerId);
        }
        Route route = iRouteDao.save(saveRoute);

        if (!ObjectUtils.isEmpty(route.getId())) {
            // 新增路线与点关系
            List<Long> ownerIds = addPlanRequest.getOwnerId();
            if (!ObjectUtils.isEmpty(ownerIds)) {
                List<Long> pointVos = pointMapper.getPointRouteList(ownerIds);
                if (!ObjectUtils.isEmpty(pointVos)) {
                    if (!ObjectUtils.isEmpty(pointVos)) {
                        pointVos.forEach(point -> {
                            RoutePoint routePoint = new RoutePoint();
                            routePoint.setOrgCode(plan.getOrgCode());
                            routePoint.setCreatorId(plan.getCreateBy());
                            routePoint.setRouteId(route.getId());
                            routePoint.setPointId(point);
                            iRoutePointDao.save(routePoint);

//                            List<PointInputItem> pointInputItems = pointMapper.getCheckPointById(point);
//                            List<PointInputItem> pointInputItems = iPointInputItemDao.getPointInputItemByPointId(point);
//                            pointMapper.getPointClassInputItemById(point);
//                            if (!ObjectUtils.isEmpty(pointInputItems)) {
//                                pointInputItems.forEach(pointInputItem -> {
//                                    RoutePointItem routePointItem = new RoutePointItem();
//                                    routePointItem.setRoutePointId(routePoint.getId());
//                                    routePointItem.setPointInputItemId(pointInputItem.getId());
//                                    iRoutePointItemDao.save(routePointItem);
//                                });
//                            }
                        });
                    }
                }
            }
        }
        return route;
    }

    @Override
    @Transactional
    public void delPlanById(String[] param) throws Exception {
        List<Long> ids = new ArrayList<>();
        for (int i = 0; i < param.length; i++) {
            ids.add(Long.parseLong(param[i]));
        }
        if (ValidationUtil.isEmpty(ids)) {
            throw new Exception("参数为空");
        }
        // 校验是否可以删除（PlanStatusEnum.DRAFT：草稿状态可删除）
        if (planDao.countNotDeletable(ids, PlanStatusEnum.DRAFT.getValue()) > 0) {
            throw new Exception("非草稿状态计划不能删除");
        }
        planDao.updatePlanDel(ids);
        // 删除对应该计划今天往后的所有计划执行信息
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        Date zero = calendar.getTime();
        List<Long> taskIdList = planTaskDao.getPlanTaskIdByPlanIdAndDate(ids, zero);
        if (taskIdList.size() > 0) {
            planTaskDao.deletePlanTaskByIdAndDate(ids, zero);

            // 删除对应该计划的计划执行详情新
            planTaskDetailDao.deletePlanTaskDetailByTaskNo(taskIdList);
        }

    }

    @Override
    public void planSaveAs(String[] param) {
        for (int i = 0; i < param.length; i++) {
            planMapper.saveAs(param[i]);
        }

    }

    @Override
    public List<Plan> getPlanByRouteId(Long routeId) {
        List<Plan> planList = planMapper.getPlanByRouteId(routeId);
        return planList;
    }

    @Override
    public void disablePlan(Long[] planIds) {
        // plan表中status字段置为1
        for (long planId : planIds) {
            List<Plan> planList = getPlanByRouteId(planId);
            for (Plan plan : planList) {
                plan.setStatus(1);
                planDao.save(plan);
            }
        }
    }

    @Override
    public Plan queryPlanById(Long id) {
        Plan plan = null;
        Optional<Plan> op = planDao.findById(id);
        if (op.isPresent()) {
            plan = op.get();
            Optional<Route> optionalRoute = iRouteDao.findById(plan.getRouteId());
            plan.setOwnerId(optionalRoute.isPresent() ? optionalRoute.get().getOwnerId() : "");
        }
        return plan;
    }

    @Override
    public List<HashMap<String, Object>> getPlanInfoNoPage(PlanInfoPageParam param) {
        return planMapper.getPlanInfoNoPage(param);
    }

    @Override
    public List<HashMap<String, Object>> queryPlanListByOrgCode(String loginOrgCode) {
        return planMapper.queryPlanListByOrgCode(loginOrgCode);
    }

    @Override
    public void setplanstatus(Long id, Integer status) {
        Plan oriPlan = planDao.findById(id).get();
        oriPlan.setStatus(status);
        planDao.save(oriPlan);
    }

    @Override
    public PlanPointRespone getplandetails(Long id) {
        PlanPointRespone planRequest = new PlanPointRespone();
        Plan plan = planService.queryPlanById(id);
        if (!ObjectUtils.isEmpty(plan)) {
            List<Long> ids = pointMapper.getPointoriginalidbyrouteid(plan.getRouteId());
            planRequest.setPlan(plan);
            planRequest.setOwnerId(ids);
        }
        return planRequest;
    }
}
