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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yeejoin.amos.boot.biz.common.utils.SecureRandomUtil;
import com.yeejoin.amos.patrol.business.constants.XJConstant;
import com.yeejoin.amos.patrol.business.dao.mapper.PlanTaskMapper;
import com.yeejoin.amos.patrol.business.dao.repository.*;
import com.yeejoin.amos.patrol.business.param.*;
import com.yeejoin.amos.patrol.business.service.intfc.IPlanTaskService;
import com.yeejoin.amos.patrol.business.service.intfc.ISynDataService;
import com.yeejoin.amos.patrol.business.util.CommonResponse;
import com.yeejoin.amos.patrol.business.util.CommonResponseUtil;
import com.yeejoin.amos.patrol.business.util.Toke;
import com.yeejoin.amos.patrol.core.util.DateUtil;
import com.yeejoin.amos.patrol.dao.entity.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigInteger;
import java.util.*;
import java.util.stream.Collectors;

@Service("synDataService")
public class SynDataServiceImpl implements ISynDataService {

    @Autowired
    private IInputItemDao iInputItemDao;

    @Autowired
    private IPointDao iPointDao;

    @Autowired
    private IPointInputItemDao iPointInputItemDao;

    @Autowired
    private IRouteDao iRouteDao;

    @Autowired
    private IRoutePointDao iRoutePointDao;

    @Autowired
    private IRoutePointItemDao iRoutePointItemDao;

    @Autowired
    private IPlanDao iPlanDao;

    @Autowired
    private IPlanTaskDao iplanTaskDao;

    @Autowired
    private PlanTaskMapper planTaskMapper;

    @Autowired
    private IPointClassifyDao iPointClassifyDao;

    @Autowired
    private IPlanTaskService iPlanTaskService;

    private static final String YYMMMMDD = "yyyy-MM-dd";

    @Override
    public CommonResponse saveInputItem(SynInputItemParam synInputItemParam, String userId, String orgCode) {
        InputItem inputItem = iInputItemDao.findByOriginalId(synInputItemParam.getOriginalId());
        if (inputItem == null) {
            if (StringUtils.isEmpty(synInputItemParam.getItemType())) {
                return CommonResponseUtil.failure("请选择类型");
            }
            inputItem = buildSaveItemByType(synInputItemParam, userId, orgCode);
            iInputItemDao.save(inputItem);
        }
        return CommonResponseUtil.success();
    }

    @Override
    public CommonResponse updateInputItem(SynInputItemParam synInputItemParam) {
        InputItem inputItem = iInputItemDao.findByOriginalId(synInputItemParam.getOriginalId());
        if (inputItem != null) {
            inputItem.setName(synInputItemParam.getItemName());
            inputItem.setBasisJson(JSON.toJSONString(synInputItemParam.getBasisJson()));
            iInputItemDao.save(inputItem);
        }

        return CommonResponseUtil.success();
    }

    @Override
    public CommonResponse deleteInputItem(SynInputItemParam synInputItemParam) {
        InputItem inputItem = iInputItemDao.findByOriginalId(synInputItemParam.getOriginalId());
        if (inputItem != null) {
            inputItem.setIsDelete(true);
            iInputItemDao.save(inputItem);
        }
        return CommonResponseUtil.success();
    }

    @Override
    public CommonResponse saveSinglePoint(SynPointParam synPointParam, String userId, String orgCode) {
        Point point = iPointDao.findByOriginalId(synPointParam.getOriginalId());
        if (point == null) {
            point = buildSavePoint(synPointParam, userId, orgCode);
            iPointDao.save(point);
        }
        return CommonResponseUtil.success();
    }

    @Override
    public CommonResponse updateSinglePoint(SynPointParam synPointParam) {
        Point point = iPointDao.findByOriginalId(synPointParam.getOriginalId());
        if (point != null) {
            point.setName(synPointParam.getPointName());
            point.setCoordinates(synPointParam.getCoordinates());
            point.setChargeDeptId(synPointParam.getChargeDeptId());
            point.setChargePersonId(synPointParam.getChargePersonId());
            point.setLastUpdateTime(new Date());
            iPointDao.save(point);
        }
        return CommonResponseUtil.success();
    }

    @Override
    public CommonResponse deleteSinglePoint(SynPointParam synPointParam) {
        Point point = iPointDao.findByOriginalId(synPointParam.getOriginalId());
        if (point != null) {
            point.setIsDelete(true);
            iPointDao.save(point);
        }
        return CommonResponseUtil.success();
    }

    @Transactional
    @Override
    public CommonResponse synPointItemRelation(SynPointParam synPointParam, Boolean isSave, String userId) {
        Point point = iPointDao.findByOriginalId(synPointParam.getOriginalId());
        if (point != null) {
            point.setLevel(synPointParam.getLevel());
            point.setLastUpdateTime(new Date());
            iPointDao.save(point);
            long pointId = point.getId();
            List<SynItemInfoParam> itemInfoList = synPointParam.getItemInfo();
            if (!CollectionUtils.isEmpty(itemInfoList)) {
                if (!isSave) {
                    List<Long> pointIds = Lists.newArrayList(pointId);
                    iPointClassifyDao.deleteByPointId(pointIds);
                    iPointInputItemDao.deleteByPointId(pointIds);
                }
                Map<String, PointClassify> pointClassifyMap = Maps.newHashMap();
                //保存分类
                List<SynClassifyInfoParam> classifyInfoParamList = synPointParam.getClassifyInfo();
                if (!CollectionUtils.isEmpty(classifyInfoParamList)) {
                    List<PointClassify> pointClassifyList = Lists.newArrayList();
                    int classfyOrderNo = 0;
                    for (SynClassifyInfoParam synClassifyInfoParam : classifyInfoParamList) {
                        PointClassify pointClassify = new PointClassify();
                        pointClassify.setPointId(pointId);
                        pointClassify.setName(synClassifyInfoParam.getName());
                        pointClassify.setOrderNo(classfyOrderNo);
                        pointClassify.setCreatorId(userId);
                        pointClassify.setOriginalId(synClassifyInfoParam.getClassifyOriginalId());
                        pointClassifyList.add(pointClassify);
                        classfyOrderNo++;
                    }
                    iPointClassifyDao.saveAll(pointClassifyList);
                    pointClassifyMap = Maps.uniqueIndex(pointClassifyList, PointClassify::getOriginalId);
                }
                //保存点和项的关系
                List<InputItem> inputItemList = iInputItemDao.listByOriginalIds(Lists.transform(itemInfoList, SynItemInfoParam::getItemOriginalId));
                Map<String, InputItem> inputItemVoMap = Maps.uniqueIndex(inputItemList, InputItem::getOriginalId);
                List<PointInputItem> pointInputItemList = Lists.newArrayList();
                int orderNo = 0;
                for (SynItemInfoParam itemInfo : itemInfoList) {
                    List<String> classifyOriginalIds = itemInfo.getClassifyOriginalIds();
                    String classifyIds = "";
                    if (!CollectionUtils.isEmpty(classifyOriginalIds)) {
                        StringBuilder classifyIdsBuilder = new StringBuilder();
                        for (String classifyOriginalId : classifyOriginalIds) {
                            PointClassify classify = pointClassifyMap.get(classifyOriginalId);
                            if (classify != null) {
                                classifyIdsBuilder.append(classify.getId()).append(",");
                            }
                        }
                        classifyIds = classifyIdsBuilder.substring(0, classifyIdsBuilder.length() - 1);
                    }
                    PointInputItem pointInputItem = new PointInputItem();
                    pointInputItem.setOrderNo(orderNo);
                    pointInputItem.setPointId(pointId);
                    pointInputItem.setInputItemId(inputItemVoMap.get(itemInfo.getItemOriginalId()).getId());
                    pointInputItem.setClassifyIds(classifyIds);
                    pointInputItemList.add(pointInputItem);
                    orderNo++;
                }
                iPointInputItemDao.saveAll(pointInputItemList);
            }
        }
        return CommonResponseUtil.success();
    }

    @Transactional
    @Override
    public CommonResponse saveRoute(SynRouteParam synRouteParam, String userId, String orgCode) {
        String routeOriginalId = synRouteParam.getOriginalId();
        Route route = iRouteDao.findByOriginalId(routeOriginalId);
        if (route == null) {
            route = buildSaveRoute(synRouteParam, orgCode, userId);
            iRouteDao.save(route);
            long routeId = route.getId();
            List<SynRouteInfoParam> routeInfoList = synRouteParam.getRouteInfo();
            synRouteRelation(routeInfoList, routeId, true, userId, orgCode);
        }
        return CommonResponseUtil.success();
    }

    @Transactional
    @Override
    public CommonResponse updateRoute(SynRouteParam synRouteParam, String userId, String orgCode) {
        String routeOriginalId = synRouteParam.getOriginalId();
        Route route = iRouteDao.findByOriginalId(routeOriginalId);
        if (route != null) {
            route.setBoss(synRouteParam.getBoss());
            route.setName(synRouteParam.getRouteName());
            iRouteDao.save(route);
            long routeId = route.getId();
            List<SynRouteInfoParam> routeInfoList = synRouteParam.getRouteInfo();
            synRouteRelation(routeInfoList, routeId, false, userId, orgCode);
        }
        return CommonResponseUtil.success();
    }

    @Override
    public CommonResponse savePlan(SynPlanParam synPlanParam, String userId, String orgCode,Toke toke) {
        Plan plan = iPlanDao.findByOriginalId(synPlanParam.getOriginalId());
        Route route = iRouteDao.findByOriginalId(synPlanParam.getOriginalId());
        if (plan == null && route != null) {
            boolean tag = savePlanAndRedo(route, synPlanParam, userId, orgCode,toke);
            if (!tag) {
                return CommonResponseUtil.failure("计划重做失败");
            }
        }
        return CommonResponseUtil.success();
    }

    private boolean savePlanAndRedo(Route route, SynPlanParam synPlanParam, String userId, String orgCode, Toke toke) {
        Date now = new Date();
        Plan plan = new Plan();
        plan.setName(synPlanParam.getPlanName());
        plan.setOrgCode(orgCode);
        plan.setDeptId(synPlanParam.getDeptId());
        plan.setRouteId(route.getId());
        plan.setIsFixedDate("0");
        plan.setInOrder("0");
        plan.setPlanBegin(synPlanParam.getPlanBegin());
        plan.setPlanEnd(synPlanParam.getPlanEnd());
        plan.setDayRate(0);
        plan.setDayInterval(1);
        plan.setDayIntervalUnit("1");
        plan.setExecuteInterval(1);
        plan.setMonthType("1");
        plan.setWhatDay("1");
        plan.setWhatWeek(1);
        plan.setWeekDay(1);
        plan.setUserId(synPlanParam.getUserId());
        plan.setWeekBeginNum(1);
        plan.setWeekEndNum(7);
        plan.setMinSpace(0);
        plan.setIsScore("1");
        plan.setError(0);
        plan.setStatus(Byte.parseByte(XJConstant.PLAN_STATUS_START));
        plan.setNextGenDate(DateUtil.getIntervalDate(now, 0));
        plan.setCreateBy(userId.toString());
        plan.setRemark1(synPlanParam.getRemark());
        plan.setFirstFlag(0);
        plan.setOriginalId(synPlanParam.getOriginalId());
        HashMap<String, Object> map = Maps.newHashMap();
        switch (synPlanParam.getPlanType()) {
            case "1":
                plan.setDayTime("00:00:00");
                plan.setDayBegin(DateUtil.formatStrToTime("08:00:00"));
                plan.setDayEnd(DateUtil.formatStrToTime("18:00:00"));
                plan.setDuration(1440);
                plan.setRemark("每1天的00:00执行");
                plan.setExecuteRate(1);
                plan.setPlanType("1");
                map.put("beginDate", DateUtil.formatDatrToStr(now, "yyyy-MM-dd"));
                map.put("endDate", DateUtil.formatDatrToStr(now, "yyyy-MM-dd"));
                break;
            case "2":
                plan.setDayTime("08:00:00");
                plan.setDayBegin(DateUtil.formatStrToTime("00:00:00"));
                plan.setDayEnd(DateUtil.formatStrToTime("23:59:59"));
                plan.setDuration(60);
                plan.setRemark("执行区间:第1天00:00至第7天23:59结束");
                plan.setExecuteRate(1);
                plan.setPlanType("2");
                map.put("beginDate", DateUtil.formatDatrToStr(DateUtil.getThisWeekMonday(now), YYMMMMDD));
                map.put("endDate", DateUtil.formatDatrToStr(now, YYMMMMDD));
                break;
            case "3":
                plan.setDayTime("08:00:00");
                plan.setDayBegin(DateUtil.formatStrToTime("00:00:00"));
                plan.setDayEnd(DateUtil.formatStrToTime("23:59:59"));
                plan.setDuration(60);
                plan.setRemark("执行区间:开始日期所在月1号00:00至下月1号00:00");
                plan.setExecuteRate(1);
                plan.setPlanType("3");
                map.put("beginDate", DateUtil.formatDatrToStr(DateUtil.getThisMonthFirstDay(now), YYMMMMDD));
                map.put("endDate", DateUtil.formatDatrToStr(now, YYMMMMDD));
                break;
            case "4":
                plan.setDayTime("08:00:00");
                plan.setDayBegin(DateUtil.formatStrToTime("00:00:00"));
                plan.setDayEnd(DateUtil.formatStrToTime("23:59:59"));
                plan.setDuration(60);
                plan.setRemark("每1年执行,执行频率：1年1次");
                plan.setExecuteRate(1);
                plan.setPlanType("4");
                map.put("beginDate", DateUtil.formatDatrToStr(DateUtil.getCurrYearFirst(), YYMMMMDD));
                map.put("endDate", DateUtil.formatDatrToStr(DateUtil.getCurrYearLast(), YYMMMMDD));
                break;
            case "5":
                plan.setDayTime("08:00:00");
                plan.setDayBegin(DateUtil.formatStrToTime("00:00:00"));
                plan.setDayEnd(DateUtil.formatStrToTime("23:59:59"));
                plan.setDuration(60);
                plan.setRemark("每1年执行,执行频率：1年4次");
                plan.setExecuteRate(4);
                plan.setPlanType("4");
                map.put("beginDate", DateUtil.formatDatrToStr(DateUtil.getCurrYearFirst(), YYMMMMDD));
                map.put("endDate", DateUtil.formatDatrToStr(DateUtil.getCurrYearLast(), YYMMMMDD));
                break;
            default:
                break;
        }
        iPlanDao.save(plan);
        map.put("planId", plan.getId());
        map.put("changeFlag", "0");
        try {
            iPlanTaskService.reGenPlanTask(map);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    @Override
    public CommonResponse updatePlan(SynPlanParam synPlanParam, String userId, String orgCode) {
        return CommonResponseUtil.success();
    }

    @Transactional
    @Override
    public CommonResponse stopPlan(String originalId) {
        Plan plan = iPlanDao.findByOriginalId(originalId);
        if (plan != null) {
            plan.setStatus(Byte.parseByte(XJConstant.PLAN_STATUS_STOP));
            iPlanDao.save(plan);
            List<PlanTask> planTasks = iplanTaskDao.findByPlanId(plan.getId());
            if (!CollectionUtils.isEmpty(planTasks)) {
                planTasks.forEach(e -> {
                    e.setStatus(Integer.parseInt(XJConstant.PLAN_STATUS_STOP));
                });
                iplanTaskDao.saveAll(planTasks);
            }
        }
        return CommonResponseUtil.success();
    }

    @Override
    public CommonResponse getPlanFinishTimes(String originalIds) {
        List<Map<String, Object>> list = Lists.newArrayList();
        List<String> originalIdList = Arrays.asList(originalIds.split(","));
        List<Plan> planList = iPlanDao.findByOriginalIds(originalIdList);
        if (!CollectionUtils.isEmpty(planList)) {
            Map<Long, Plan> planMap = Maps.uniqueIndex(planList, Plan::getId);
            List<Long> planIds = Lists.transform(planList, Plan::getId);
            list = planTaskMapper.countFinishByPlanIds(planIds);
            if (!CollectionUtils.isEmpty(list)) {
                list.forEach(e -> {
                    e.put("originalId", planMap.get(Long.valueOf(e.get("planId").toString())).getOriginalId());
                });
            }

        }
        return CommonResponseUtil.success(list);
    }


    private void synRouteRelation(List<SynRouteInfoParam> routeInfoList, Long routeId, Boolean isSave, String userId, String orgCode) {
        if (!CollectionUtils.isEmpty(routeInfoList)) {
            List<String> pointOriginalIds = Lists.newArrayList();
            for (SynRouteInfoParam synRouteInfoParam : routeInfoList) {
                pointOriginalIds.add(synRouteInfoParam.getPointOriginalId());
            }
            List<Point> pointVos = iPointDao.listByOriginalIds(pointOriginalIds);
            List<Long> pointIds = Lists.transform(pointVos, Point::getId);
            if (!isSave) {
                List<BigInteger> routePointIds = iRoutePointDao.queryRoutePointByPointId(pointIds);
                if (!CollectionUtils.isEmpty(routePointIds)) {
                    routePointIds.forEach(id -> iRoutePointItemDao.delRoutePointItem(id.longValue()));
                }
                iRoutePointDao.delRoutePointByRouteId(routeId);
            }
            Map<String, Point> pointVoMap = Maps.uniqueIndex(pointVos, Point::getOriginalId);
            int routePointOrderNo = 0;
            for (SynRouteInfoParam synRouteInfoParam : routeInfoList) {
                long pointId = pointVoMap.get(synRouteInfoParam.getPointOriginalId()).getId();
                RoutePoint routePoint = new RoutePoint();
                routePoint.setOrgCode(orgCode);
                routePoint.setRouteId(routeId);
                routePoint.setPointId(pointId);
                routePoint.setOrderNo(routePointOrderNo);
                routePoint.setCreatorId(userId);
                iRoutePointDao.save(routePoint);
                routePointOrderNo++;
                List<SynRouteItemInfoParam> itemInfo = synRouteInfoParam.getItemInfo();
                List<String> itemOriginalIds = Lists.transform(itemInfo, SynRouteItemInfoParam::getItemOriginalId);
                List<InputItem> inputItemVos = iInputItemDao.listByOriginalIds(itemOriginalIds);
                List<Long> itemIds = Lists.transform(inputItemVos, InputItem::getId);
                Map<String, InputItem> inputItemVoMap = Maps.uniqueIndex(inputItemVos, InputItem::getOriginalId);
                List<RoutePointItem> routePointItems = Lists.newArrayList();
                List<PointInputItem> pointInputItems = iPointInputItemDao.findAllPointInputItemByPointIdAndItemIds(pointId, itemIds);
                Map<String, PointInputItem> pointInputItemMap = Maps.uniqueIndex(pointInputItems, (PointInputItem i) -> {
                    if (i != null) {
                        return buildId(i.getPointId(), i.getInputItemId());
                    }
                    return "";
                });
                int routePointItemOrderNo = 0;
                for (SynRouteItemInfoParam item : itemInfo) {
                    Long itemId = inputItemVoMap.get(item.getItemOriginalId()).getId();
                    PointInputItem pointInputItem = pointInputItemMap.get(buildId(pointId, itemId));
                    if (pointInputItem != null) {
                        String classifyIds = pointInputItem.getClassifyIds();
                        if (StringUtils.isEmpty(classifyIds)) {
                            RoutePointItem routePointItem = buildRoutePointItem(pointInputItem, routePoint, routePointItemOrderNo, userId, item.getBasisJson().toJSONString());
                            routePointItems.add(routePointItem);
                            routePointItemOrderNo++;
                        } else {
                            List<Long> classifyIdList = Arrays.stream(classifyIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList());
                            int orderNo = 0;
                            for (Long classifyId : classifyIdList) {
                                RoutePointItem routePointItem = buildRoutePointItem(pointInputItem, routePoint, orderNo, userId, item.getBasisJson().toJSONString());
                                routePointItem.setPointClassifyId(classifyId);
                                routePointItems.add(routePointItem);
                                orderNo++;
                            }
                        }
                    }
                }
                iRoutePointItemDao.saveAll(routePointItems);
            }
        }
    }

    private RoutePointItem buildRoutePointItem(PointInputItem pointInputItem, RoutePoint routePoint, Integer orderNo, String userId, String json) {
        RoutePointItem routePointItem = new RoutePointItem();
        routePointItem.setRoutePointId(routePoint.getId());
        routePointItem.setPointInputItemId(pointInputItem.getId());
        routePointItem.setOrderNo(orderNo);
        routePointItem.setCreatorId(userId);
        routePointItem.setBasisJson(json);
        return routePointItem;
    }

    private String buildId(Long pointId, Long itemId) {
        return pointId + "_" + itemId;
    }

    private Route buildSaveRoute(SynRouteParam synRouteParam, String orgCode, String userId) {
        Route route = new Route();
        route.setCreatorId(userId);
        route.setOrgCode(orgCode);
        route.setName(synRouteParam.getRouteName());
        route.setBoss(synRouteParam.getBoss());
        route.setOriginalId(synRouteParam.getOriginalId());
        return route;
    }

    private InputItem buildSaveItemByType(SynInputItemParam synInputItemParam, String userId, String orgCode) {
        InputItem inputItem = new InputItem();
        if ("选择".equals(synInputItemParam.getItemType())) {
            inputItem.setOrgCode(orgCode);
            inputItem.setName(synInputItemParam.getItemName());
            inputItem.setOrderNo(0);
            inputItem.setItemType(synInputItemParam.getItemType());
            inputItem.setIsMust("否");
            inputItem.setIsScore("否");
            inputItem.setIsMultiline("否");
            inputItem.setCatalogId(0L);
            String dataJsonStr = "[{\"name\":\"通过\",\"isOk\":\"是\",\"isChecked\":\"否\",\"orderNo\":0},{\"name\":\"不通过\",\"isOk\":\"否\",\"isChecked\":\"否\",\"orderNo\":1}]";
            JSONArray dataJson = JSONArray.parseArray(dataJsonStr);
            inputItem.setDataJson(dataJson.toJSONString());
            String pictureJsonStr = "[{\"isMust\":\"是\",\"name\":\"照片\",\"orderNo\":0}]";
            JSONArray pictureJson = JSONArray.parseArray(pictureJsonStr);
            inputItem.setPictureJson(pictureJson.toJSONString());
            if (StringUtils.isEmpty(synInputItemParam.getItemNo())) {
                inputItem.setItemNo(buildRandomNum(synInputItemParam.getOriginalId()));
            } else {
                inputItem.setItemNo(synInputItemParam.getItemNo());
            }
            inputItem.setOriginalId(synInputItemParam.getOriginalId());
            inputItem.setCreateBy(userId);
            inputItem.setBasisJson(synInputItemParam.getBasisJson().toJSONString());
        }
        return inputItem;
    }

    private Point buildSavePoint(SynPointParam synPointParam, String userId, String orgCode) {
        Point point = new Point();
        point.setName(synPointParam.getPointName());
        if (StringUtils.isEmpty(synPointParam.getPointNo())) {
            point.setPointNo(buildRandomNum(synPointParam.getOriginalId()));
        } else {
            point.setPointNo(synPointParam.getPointNo());
        }
        point.setSaveGps("0");
        point.setDistance(0);
        point.setShotMinNumber(0);
        point.setShotMaxNumber(10);
        point.setFixedShot(10);
        point.setOffline(false);
        point.setIsFixed("1");
        point.setCatalogId(0L);
        point.setIsScore("0");
        point.setOrgCode(orgCode);
        point.setCoordinates(synPointParam.getCoordinates());
        point.setChargeDeptId(synPointParam.getChargeDeptId());
        point.setChargePersonId(synPointParam.getChargePersonId());
        point.setStatus("0");
        point.setIsIndoor(false);
        point.setOriginalId(synPointParam.getOriginalId());
        point.setCreatorId(userId);
        return point;
    }

    private String buildRandomNum(String originalId) {
        return "SPC_" + (int) ((SecureRandomUtil.getIntSecureRandomByDouble() * 9 + 1) * 100000) + "_" + originalId;
    }
}
