package com.yeejoin.amos.boot.module.tcm.biz.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yeejoin.amos.boot.biz.common.utils.DateUtils;
import com.yeejoin.amos.boot.biz.common.utils.IdWorker;
import com.yeejoin.amos.boot.module.tcm.api.dto.ThreeSystemsDto;
import com.yeejoin.amos.boot.module.tcm.api.entity.ThreeSystems;
import com.yeejoin.amos.boot.module.tcm.api.enums.SupervisoryEnum;
import com.yeejoin.amos.boot.module.tcm.api.mapper.ThreeSystemsMapper;
import com.yeejoin.amos.boot.module.tcm.api.service.IThreeSystemsService;
import com.yeejoin.amos.component.robot.AmosRequestContext;
import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.apache.commons.compress.utils.Lists;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.rdbms.service.BaseService;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author system_generator
 * @date 2023-11-01
 */
@Service
public class ThreeSystemsServiceImpl extends BaseService<ThreeSystemsDto, ThreeSystems, ThreeSystemsMapper> implements IThreeSystemsService {

	@Autowired
	AmosRequestContext amosRequestContext;

	@Autowired
	ThreeSystemsMapper threeSystemsMapper;

	@Autowired
	TzsTwoStaffingServiceImpl tzsTwoStaffingService;

	/**
	 * 列表查询
	 */
	@Override
	public List<ThreeSystems> getStatisticsMessage(List<LinkedHashMap> list, String planType, String startDate, String endDate) throws ParseException {
		String orgCode = null;
		String nextLevel = null;
		SupervisoryEnum enumByLevel = null;
		if (!ObjectUtils.isEmpty(list) && !ObjectUtils.isEmpty(list.get(0))) {
			orgCode = String.valueOf(list.get(0).get("orgCode"));
			enumByLevel = SupervisoryEnum.getEnumByLevel(String.valueOf(list.get(0).get("level")));
			nextLevel = !ObjectUtils.isEmpty(enumByLevel) ? enumByLevel.getNextLevel() : null;
		}
		DecimalFormat decimalFormat = new DecimalFormat("0.##");
		if (!ObjectUtils.isEmpty(orgCode)) {
			if ("50*56".equals(orgCode)) {
				nextLevel = "organization";
			}
			LambdaQueryWrapper<ThreeSystems> queryWrapper = new LambdaQueryWrapper<>();
			queryWrapper.like(ThreeSystems::getSupervisoryUnitOrgCode, orgCode);
			queryWrapper.eq(ThreeSystems::getIsDelete, false);
			queryWrapper.eq(ThreeSystems::getPlanType, planType);
			queryWrapper.between(ThreeSystems::getCheckDate, ValidationUtil.isEmpty(startDate) ?
					DateUtils.dateFormat(new Date(), DateUtils.DATE_PATTERN) :
					startDate, ValidationUtil.isEmpty(endDate) ? DateUtils.dateFormat(new Date(), DateUtils.DATE_PATTERN) : endDate);
			queryWrapper.eq(ThreeSystems::getSupervisoryUnitLevel, nextLevel);
			List<ThreeSystems> listByOrgCode = this.getBaseMapper().selectList(queryWrapper);
			ThreeSystems summary = new ThreeSystems();
			summary.setSupervisoryUnitName("汇总");
			if (!ValidationUtil.isEmpty(listByOrgCode)) {
				listByOrgCode.forEach(i -> {
					summary.setCheckNum(ValidationUtil.isEmpty(summary.getCheckNum()) ? i.getCheckNum() :
							summary.getCheckNum() + i.getCheckNum());
					summary.setFinishCheckNum(ValidationUtil.isEmpty(summary.getFinishCheckNum()) ?
							i.getFinishCheckNum() : summary.getFinishCheckNum() + i.getFinishCheckNum());
					summary.setRegisteredNum(ValidationUtil.isEmpty(summary.getRegisteredNum()) ?
							i.getRegisteredNum() : summary.getRegisteredNum() + i.getRegisteredNum());
				});
				if (ValidationUtil.isEmpty(summary.getCheckNum()) || ValidationUtil.isEmpty(summary.getRegisteredNum())) {
					summary.setProportion("0%");
				} else {
					BigDecimal result = new BigDecimal(summary.getFinishCheckNum()).divide(new BigDecimal(summary.getCheckNum())
							, 4,
							RoundingMode.HALF_UP);
					summary.setProportion(decimalFormat.format(result.multiply(new BigDecimal(100))) + "%");
				}
				listByOrgCode.add(summary);
				return listByOrgCode;
			}
		}
		return Lists.newArrayList();
	}


    @Override
    public void getThreeSystemsStatistics(String type) {
        if (type.equals("1")) {
            createDayThreeSystemsStatistics();
        } else if (type.equals("2")) {
            createWeekThreeSystemsStatistics();
        } else if (type.equals("3")) {
            createMonthThreeSystemsStatistics();
        }
    }


    @Scheduled(cron = "0 0 2,13 * * ?")
    @SchedulerLock(name = "createDayThreeSystemsStatistics", lockAtMostFor = "PT1H")
    public void createDayThreeSystemsStatistics() {
        createThreeSystemsStatistics(1, false);
    }


    @Scheduled(cron = "0 0 2,13 * * ?")
    @SchedulerLock(name = "createWeekThreeSystemsStatistics", lockAtMostFor = "PT1H")
    public void createWeekThreeSystemsStatistics() {
        Date nowDay = new Date();
        String day = DateUtils.convertDateToString(nowDay, DateUtils.DATE_HOUR_PATTERN);

        Calendar c = DateUtils.getCalendar();
        c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        String monday = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_HOUR_PATTERN);
        Boolean flag = false;
        //判断今天是否本周第一天凌晨2点，如果是则最后一次更新上月数据
        if (day.equals(monday)) {
            flag = true;
        }
        createThreeSystemsStatistics(2, flag);

    }

    @Scheduled(cron = "0 0 2,13 * * ?")
    @SchedulerLock(name = "createMonthThreeSystemsStatistics", lockAtMostFor = "PT1H")
    public void createMonthThreeSystemsStatistics() {
        Date nowDay = new Date();
        String day = DateUtils.convertDateToString(nowDay, DateUtils.DATE_HOUR_PATTERN);

        String monthday = DateUtils.convertDateToString(nowDay, DateUtils.MONTH_PATTERN) + "-01 02";
        Boolean flag = false;
        //判断今天是否本月第一天凌晨2点，如果是则最后一次更新上月数据
        if (day.equals(monthday)) {
            flag = true;
        }
        createThreeSystemsStatistics(3, flag);
    }


    private void createThreeSystemsStatistics(Integer type, Boolean isFirstDay) {
        // 所有监管单位列表
        List<LinkedHashMap<String, Object>> supervisionList = tzsTwoStaffingService.getSupervisionList();
        List<ThreeSystems> resultList;
        // 所有企业排查情况列表(昨天/上周/上月)
        List<ThreeSystemsDto> oldStaffingCompanyList;
        // 所有企业排查情况列表(今天/本周/本月)
        List<ThreeSystemsDto> newStaffingCompanyList;

        String start = " 00:00:00";
        String end = " 23:59:59";

        Date nowDay = new Date();
        String day = DateUtils.getDateNowShortStr();
        String hour = DateUtils.convertDateToString(nowDay, DateUtils.DATE_HOUR_PATTERN);
        String startDay = "";
        String endDay = "";
        String lastStart = "";
        String lastEnd = "";
        // 根据类型设置不同的时间
        if (1 == type) {
            startDay = day + start;
            endDay = day + end;

            if (hour.endsWith("02")) {
                LocalDate today = LocalDate.now();
                // 获取昨天日期
                LocalDate yesterday = today.minusDays(1);
                lastStart = yesterday + start;
                lastEnd = yesterday + end;
            }
        } else if (2 == type) {
            //获取当前周第一天：
            Calendar c = DateUtils.getCalendar();
            c.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
            startDay = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_PATTERN) + start;

            //获取当前周最后一天
            c.add(Calendar.DAY_OF_WEEK, 6);
            endDay = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_PATTERN) + end;

            if (isFirstDay) {
                //获取上前周第一天
                c.add(Calendar.DAY_OF_WEEK, -13);
                lastStart = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_PATTERN) + start;

                //获取上前周最后一天
                c.add(Calendar.DAY_OF_WEEK, 6);
                lastEnd = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_PATTERN) + end;
            }
        } else {

            //获取当前月第一天：
            Calendar c = DateUtils.getCalendar();
            c.add(Calendar.MONTH, 0);
            c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
            startDay = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_PATTERN) + start;

            //获取当前月最后一天
            c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
            endDay = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_PATTERN) + end;
            if (isFirstDay) {
                //获取上月第一天
                c.add(Calendar.MONTH, -1);
                c.set(Calendar.DAY_OF_MONTH, 1);//设置为1号,当前日期既为本月第一天
                lastStart = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_PATTERN) + start;

                //获取上月最后一天
                c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
                lastEnd = DateUtils.convertDateToString(c.getTime(), DateUtils.DATE_PATTERN) + end;
            }
        }
        // todo 此处统计表名通过接口查询得到 接口暂未实现
        String planType = type.toString();
        String staticTableName = "p_static_day";
        //获取当天、本周、本月新的数据
        newStaffingCompanyList = threeSystemsMapper.getCompanyThreeSystemsStatisticsList(staticTableName, startDay, endDay);
        //调用三项制度统计方法处理数据
        resultList = getThreeSystems(supervisionList, newStaffingCompanyList, type, startDay, true);
        //将新数据存入统计表中
        threeSystemsMapper.saveOrUpdateBatch(resultList);

        List<ThreeSystems> oldList;
        //根据lastStart和lastEnd判断是否需要更新日/周/月的历史数据（为空不更新）
        if (!"".equals(lastStart) && !"".equals(lastEnd)) {
            //获取日/周/月的历史数据
            oldStaffingCompanyList = threeSystemsMapper.getOldCompanyThreeSystemsStatisticsList(type, DateUtils.dateToString(lastStart));
            //获取日/周/月历史数据的最新状态数据
            // todo 此处统计表名通过接口查询得到 接口暂未实现

            List<ThreeSystemsDto> oldNewStaffingCompanyList = threeSystemsMapper.getCompanyThreeSystemsStatisticsList(staticTableName, lastStart, lastEnd);
            if (!ValidationUtil.isEmpty(oldNewStaffingCompanyList)) {
                // 调用三项制度统计方法处理数据
                oldList = ValidationUtil.isEmpty(oldStaffingCompanyList) ?
                        getThreeSystems(supervisionList, oldNewStaffingCompanyList, type, lastStart, true) :
                        getThreeSystems(supervisionList, oldNewStaffingCompanyList, type, lastStart, false);
                //如果历史数据不为空，更新历史数据实体
                if (!ValidationUtil.isEmpty(oldStaffingCompanyList)) {
                    if (!ValidationUtil.isEmpty(oldList)) {
                        for (ThreeSystems e : oldList) {
                            ThreeSystemsDto threeSystemsDto = oldStaffingCompanyList.stream().filter(i ->
                                    i.getSupervisoryUnitOrgCode().equals(e.getSupervisoryUnitOrgCode()) &&
                                            i.getCheckDate().equals(e.getCheckDate())).collect(Collectors.toList()).get(0);
                            e.setSequenceNbr(threeSystemsDto.getSequenceNbr());
                        }
                    }
                }
                //更新统计表历史数据
                threeSystemsMapper.saveOrUpdateBatch(oldList);
            }
        }
    }

    /**
     * 三项制度统计数据方法
     *
     * @param supervisionList     行政区划列表
     * @param StaffingCompanyList 需要统计处理的数据列表
     * @param type                统计类型
     * @param date                统计日期，做筛选用，日统计传当天日期，周统计传周一日期，月统计传月一号日期
     * @param isNew               是否新数据，新数据会生成主键
     * @return
     */
    private List<ThreeSystems> getThreeSystems(List<LinkedHashMap<String, Object>> supervisionList,
                                               List<ThreeSystemsDto> StaffingCompanyList,
                                               Integer type,
                                               String date,
                                               boolean isNew) {

        List<ThreeSystems> resultList = new ArrayList<>();

        supervisionList.forEach(i -> {
            ThreeSystemsDto nowDayThreeSystemsDto = new ThreeSystemsDto();
            //对应orgCode下所有注册企业列表
            List<ThreeSystemsDto> allUnit = StaffingCompanyList.stream().filter(ii -> ii.getSupervisoryUnitOrgCode().startsWith(i.get("orgCode").toString())).collect(Collectors.toList());
            //对应注册企业下所有应排查的企业列表
            List<ThreeSystemsDto> shouldCheckUnit = allUnit.stream().filter(ii -> !(ii.getUnitType().contains("个人主体") || ii.getUnitType().equals("检验检测机构"))).collect(Collectors.toList());
            //应排查的企业下已排查完成的企业列表
            List<ThreeSystemsDto> checkCompleteUnit = shouldCheckUnit.stream().filter(ii -> "1".equals(ii.getCheckStatus())).collect(Collectors.toList());

            if (isNew) {
                try {
                    nowDayThreeSystemsDto.setSequenceNbr(IdWorker.getFlowIdWorkerInstance().nextId());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }

            nowDayThreeSystemsDto.setRecDate(new Date());
            nowDayThreeSystemsDto.setRecUserId(amosRequestContext.getUserId());
            nowDayThreeSystemsDto.setRecUserName(amosRequestContext.getUserName());
            nowDayThreeSystemsDto.setIsDelete(false);

            nowDayThreeSystemsDto.setSupervisoryUnitId(i.get("sequenceNbr").toString());
            nowDayThreeSystemsDto.setSupervisoryUnitLevel(i.get("level").toString());
            nowDayThreeSystemsDto.setSupervisoryUnitName(i.get("companyName").toString());

            nowDayThreeSystemsDto.setRegisteredNum(allUnit.size());
            nowDayThreeSystemsDto.setCheckNum(shouldCheckUnit.size());
            nowDayThreeSystemsDto.setFinishCheckNum(checkCompleteUnit.size());

            nowDayThreeSystemsDto.setSupervisoryUnitOrgCode(i.get("orgCode").toString());
            nowDayThreeSystemsDto.setPlanType(1 == type ? "1" : 2 == type ? "2" : "3");
            nowDayThreeSystemsDto.setCheckDate(DateUtils.dateToString(date));

            if (shouldCheckUnit.size() == 0) {
                nowDayThreeSystemsDto.setProportion("0%");
            } else {
                DecimalFormat decimalFormat = new DecimalFormat("0.##");
                BigDecimal result = new BigDecimal(checkCompleteUnit.size()).divide(new BigDecimal(shouldCheckUnit.size()), 4, RoundingMode.HALF_UP);
                nowDayThreeSystemsDto.setProportion(decimalFormat.format(result.multiply(new BigDecimal(100))) + "%");
            }

            ThreeSystems threeSystems = new ThreeSystems();
            BeanUtils.copyProperties(nowDayThreeSystemsDto, threeSystems);
            resultList.add(threeSystems);
        });
        return resultList;
    }
}