package com.xjrsoft.module.databoard.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.ObjectUtil;
import com.github.yulichang.toolkit.MPJWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.xjrsoft.common.enums.GenderDictionaryEnum;
import com.xjrsoft.common.model.result.RT;
import com.xjrsoft.common.mybatis.SqlRunnerAdapter;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.module.databoard.dto.StatisticsDetailDto;
import com.xjrsoft.module.databoard.vo.DistributionVo;
import com.xjrsoft.module.databoard.vo.DurationVo;
import com.xjrsoft.module.databoard.vo.HealthItemCountVo;
import com.xjrsoft.module.databoard.vo.HealthStatisticsDetailVo;
import com.xjrsoft.module.databoard.vo.ItemCountVo;
import com.xjrsoft.module.databoard.vo.ProcessStatisticsDetailVo;
import com.xjrsoft.module.databoard.vo.TeacherStatisticsDetailVo;
import com.xjrsoft.module.system.entity.DictionaryDetail;
import com.xjrsoft.module.teacher.entity.BaseTeacher;
import com.xjrsoft.module.teacher.entity.XjrUser;
import com.xjrsoft.module.teacher.service.ITeacherbaseManagerService;
import com.xjrsoft.module.teacher.vo.XjrUserPageVo;
import com.xjrsoft.module.workflow.constant.WorkflowConstant;
import com.xjrsoft.module.workflow.entity.WorkflowExtra;
import com.xjrsoft.module.workflow.entity.WorkflowSchema;
import com.xjrsoft.module.workflow.service.IWorkflowExtraService;
import com.xjrsoft.module.workflow.utils.WorkFlowUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.camunda.bpm.engine.HistoryService;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricProcessInstanceQuery;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
* @title: 数据看板代码
* @Date: 2024年8月2日
* @Version 1.0
*/
@RestController
@RequestMapping("/datadetail" + "/datadetail")
@Api(value = "/datadetail"  + "/datadetail",tags = "数据看板详情代码")
@AllArgsConstructor
public class DatadetailController {

    private final HistoryService historyService;
    private final IWorkflowExtraService extraService;
    private final ITeacherbaseManagerService teacherService;
    @GetMapping(value = "/process-statistics")
    @ApiOperation(value="流程统计详情")
    @SaCheckPermission("datadetail:detail")
    public RT<ProcessStatisticsDetailVo> processStatistics(@Valid StatisticsDetailDto dto){
        HistoricProcessInstanceQuery instanceQuery = historyService.createHistoricProcessInstanceQuery();
        if(dto.getUserId() != null){
            instanceQuery.variableValueEquals(WorkflowConstant.PROCESS_START_USER_ID_KEY, dto.getUserId());
        }
        if (!ObjectUtil.isNull(dto.getStartDate())) {
            instanceQuery.startedAfter(WorkFlowUtil.getStartOfDay(dto.getStartDate()));
        }
        if (!ObjectUtil.isNull(dto.getEndDate())) {
            instanceQuery.startedBefore(WorkFlowUtil.getEndOfDay(dto.getEndDate()));
        }

        List<WorkflowExtra> workflowExtras = extraService.list(
                new MPJLambdaWrapper<WorkflowExtra>()
                        .select(WorkflowExtra.class, x -> VoToColumnUtil.fieldsToColumns(WorkflowExtra.class).contains(x.getProperty()))
                        .leftJoin(WorkflowSchema.class, WorkflowSchema::getId, WorkflowExtra::getSchemaId)
                        .leftJoin(DictionaryDetail.class, DictionaryDetail::getId, WorkflowSchema::getCategory)
        );


        ProcessStatisticsDetailVo result = new ProcessStatisticsDetailVo();
        List<HistoricProcessInstance> list = instanceQuery.list();
        List<WorkflowExtra> allCountList = new ArrayList<>();
        for (HistoricProcessInstance historicProcessInstance : list) {
            workflowExtras.stream()
                    .filter(e -> e.getProcessId().equals(historicProcessInstance.getId()))
                    .max(Comparator.comparing(WorkflowExtra::getStartTime))
                    .ifPresent(e -> {
                        allCountList.add(e);
                    });
        }
        result.setAllCount(Long.parseLong(allCountList.size() + ""));

        List<HistoricProcessInstance> finished = historyService.createHistoricProcessInstanceQuery().finished().list();
        List<WorkflowExtra> completeCountList = new ArrayList<>();
        for (HistoricProcessInstance historicProcessInstance : finished) {
            workflowExtras.stream()
                    .filter(e -> e.getProcessId().equals(historicProcessInstance.getId()))
                    .max(Comparator.comparing(WorkflowExtra::getStartTime))
                    .ifPresent(e -> {
                        completeCountList.add(e);
                    });
        }
        result.setCompleteCount(Long.parseLong(completeCountList.size() + ""));

        List<HistoricProcessInstance> unfinished = historyService.createHistoricProcessInstanceQuery().unfinished().list();
        List<WorkflowExtra> uncompleteCountList = new ArrayList<>();
        for (HistoricProcessInstance historicProcessInstance : unfinished) {
            workflowExtras.stream()
                    .filter(e -> e.getProcessId().equals(historicProcessInstance.getId()))
                    .max(Comparator.comparing(WorkflowExtra::getStartTime))
                    .ifPresent(e -> {
                        uncompleteCountList.add(e);
                    });
        }
        result.setUncompleteCount(Long.parseLong(uncompleteCountList.size() + ""));

        Map<String, Long> finishedMap = completeCountList.stream()
                .collect(Collectors.groupingBy(WorkflowExtra::getSchemaName, Collectors.counting()));
        Map<String, Long> unfinishedMap = uncompleteCountList.stream()
                .collect(Collectors.groupingBy(WorkflowExtra::getSchemaName, Collectors.counting()));

        Set<String> itemSet = new HashSet<>();
        itemSet.addAll(finishedMap.keySet());
        itemSet.addAll(unfinishedMap.keySet());
        List<DistributionVo> distributionList = new ArrayList();
        for (String item : itemSet) {
            Long uncompleteCount = unfinishedMap.get(item) == null?0L:unfinishedMap.get(item);
            Long completeCount = finishedMap.get(item) == null?0L:finishedMap.get(item);
            distributionList.add(
                    new DistributionVo(){{
                        setItem(item);
                        setCompleteCount(completeCount);
                        setUncompleteCount(uncompleteCount);
                    }}
            );
        }
        result.setDistributionList(distributionList);
        List<DurationVo> durationList = new ArrayList<>();

        List<Duration> timeDifferences = new ArrayList<>();
        completeCountList.stream().filter(el -> el.getEndTime() != null && el.getStartTime() != null).forEach(el -> {
            Duration diff = Duration.between(el.getEndTime(), el.getStartTime());
            timeDifferences.add(diff);
        });

        int zeroHour = 0;
        int oneHour = 0;
        int fourHour = 0;
        int eightHour = 0;
        for (Duration timeDifference : timeDifferences) {
            long seconds = timeDifference.getSeconds();
            if(seconds < 3600){
                zeroHour ++;
            }else if(seconds < 14400){
                oneHour ++;
            }else if(seconds < 28800){
                fourHour ++;
            }else{
                eightHour ++;
            }
        }
        DurationVo zeroVo = new DurationVo();
        zeroVo.setDuration("0-1h");
        zeroVo.setCount(zeroHour);
        durationList.add(zeroVo);

        zeroVo = new DurationVo();
        zeroVo.setDuration("1-4h");
        zeroVo.setCount(oneHour);
        durationList.add(zeroVo);

        zeroVo = new DurationVo();
        zeroVo.setDuration("4-8h");
        zeroVo.setCount(fourHour);
        durationList.add(zeroVo);

        zeroVo = new DurationVo();
        zeroVo.setDuration("8h以上");
        zeroVo.setCount(eightHour);
        durationList.add(zeroVo);

        result.setDurationList(durationList);

        return RT.ok(result);
    }

    @GetMapping(value = "/health-statistics")
    @ApiOperation(value="学生健康统计")
    @SaCheckPermission("datadetail:detail")
    public RT<HealthStatisticsDetailVo> healthStatistics(@Valid StatisticsDetailDto dto){
        String sql = "SELECT gender,COUNT(*) AS a_count FROM student_infection WHERE status = 1 GROUP BY gender";
        List<Map<String, Object>> list = SqlRunnerAdapter.db().selectList(sql);
        HealthStatisticsDetailVo result = new HealthStatisticsDetailVo();
        for (Map<String, Object> objectMap : list) {
            if(objectMap.get("gender") != null && GenderDictionaryEnum.FEMALE.getCode().equals(objectMap.get("gender").toString())){
                result.setInfectionFemaleCount(Integer.parseInt(objectMap.get("a_count").toString()));
            }if(objectMap.get("gender") != null && GenderDictionaryEnum.MALE.getCode().equals(objectMap.get("gender").toString())){
                result.setInfectionMaleCount(Integer.parseInt(objectMap.get("a_count").toString()));
            }
        }

        sql = "SELECT gender,COUNT(*) AS a_count FROM student_psychological WHERE status = 1 GROUP BY gender";
        list = SqlRunnerAdapter.db().selectList(sql);
        for (Map<String, Object> objectMap : list) {
            if(objectMap.get("gender") != null && GenderDictionaryEnum.FEMALE.getCode().equals(objectMap.get("gender").toString())){
                result.setInfectionFemaleCount(Integer.parseInt(objectMap.get("a_count").toString()));
            }if(objectMap.get("gender") != null && GenderDictionaryEnum.MALE.getCode().equals(objectMap.get("gender").toString())){
                result.setPsychologicalMaleCount(Integer.parseInt(objectMap.get("a_count").toString()));
            }
        }

        List<HealthItemCountVo> gradeList = new ArrayList<>();
        sql = "SELECT t1.name as item,COUNT(t3.student_id) AS count FROM base_grade t1" +
            " INNER JOIN base_student_school_roll t2 ON t1.id=  t2.grade_id" +
            " INNER JOIN student_infection t3 ON t3.student_id = t2.user_id" +
            " WHERE t3.status = 1 AND t1.delete_mark = 0" +
            " GROUP BY t1.name";
        list = SqlRunnerAdapter.db().selectList(sql);
        for (Map<String, Object> objectMap : list) {
            gradeList.add(
                new HealthItemCountVo(){{
                    setItem(objectMap.get("item").toString());
                    setInfectionCount(Long.parseLong(objectMap.get("count").toString()));
                }}
            );
        }

        sql = "SELECT t1.name as item,COUNT(t3.student_id) AS count FROM base_grade t1" +
            " INNER JOIN base_student_school_roll t2 ON t1.id=  t2.grade_id" +
            " INNER JOIN student_psychological t3 ON t3.student_id = t2.user_id" +
            " WHERE t3.status = 1 AND t1.delete_mark = 0" +
            " GROUP BY t1.name";
        list = SqlRunnerAdapter.db().selectList(sql);
        for (Map<String, Object> objectMap : list) {
            for (HealthItemCountVo itemCountVo : gradeList) {
                if(objectMap.get("item").toString().equals(itemCountVo.getItem())){
                    itemCountVo.setPsychologicalCount(Long.parseLong(objectMap.get("count").toString()));
                }
            }
        }
        result.setGradeList(gradeList);

        List<HealthItemCountVo> classList = new ArrayList<>();
        sql = "SELECT t1.name as item,COUNT(t3.student_id) AS count FROM base_class t1" +
                " INNER JOIN base_student_school_roll t2 ON t1.id=  t2.grade_id" +
                " INNER JOIN student_infection t3 ON t3.student_id = t2.user_id" +
                " WHERE t3.status = 1 AND t1.delete_mark = 0" +
                " GROUP BY t1.name";
        list = SqlRunnerAdapter.db().selectList(sql);
        for (Map<String, Object> objectMap : list) {
            classList.add(
                    new HealthItemCountVo(){{
                        setItem(objectMap.get("item").toString());
                        setInfectionCount(Long.parseLong(objectMap.get("count").toString()));
                    }}
            );
        }

        sql = "SELECT t1.name as item,COUNT(t3.student_id) AS count FROM base_class t1" +
                " INNER JOIN base_student_school_roll t2 ON t1.id=  t2.grade_id" +
                " INNER JOIN student_psychological t3 ON t3.student_id = t2.user_id" +
                " WHERE t3.status = 1 AND t1.delete_mark = 0" +
                " GROUP BY t1.name";
        list = SqlRunnerAdapter.db().selectList(sql);
        for (Map<String, Object> objectMap : list) {
            for (HealthItemCountVo itemCountVo : classList) {
                if(objectMap.get("item").toString().equals(itemCountVo.getItem())){
                    itemCountVo.setPsychologicalCount(Long.parseLong(objectMap.get("count").toString()));
                }
            }
        }
        result.setClassList(classList);
        return RT.ok(result);
    }


    @GetMapping(value = "/person-statistics")
    @ApiOperation(value="教职工详情数据统计")
    @SaCheckPermission("datadetail:detail")
    public RT<TeacherStatisticsDetailVo> teahcerStatistics(@Valid StatisticsDetailDto dto) throws ParseException {
        String sql = "SELECT IFNULL(t2.name,'未填写') AS item ,COUNT(*) AS count FROM base_teacher_education t1" +
                " LEFT JOIN xjr_dictionary_detail t2 ON t1.education  = t2.code" +
                " WHERE t1.delete_mark = 0 GROUP BY t2.name";
        List<Map<String, Object>> list = SqlRunnerAdapter.db().selectList(sql);
        TeacherStatisticsDetailVo result = new TeacherStatisticsDetailVo();
        List<ItemCountVo> educationList = new ArrayList<>();
        for (Map<String, Object> objectMap : list) {
            educationList.add(
                    new ItemCountVo(){{
                        setItem(objectMap.get("item").toString());
                        setCount(Integer.parseInt(objectMap.get("count").toString()));
                    }}
            );
        }
        result.setEducationList(educationList);

        sql = "SELECT IFNULL(t3.name,'未填写') AS item ,COUNT(*) AS COUNT FROM xjr_user t1" +
            " INNER JOIN base_teacher t2 ON t1.id = t2.user_id" +
            " LEFT JOIN xjr_dictionary_detail t3 ON t1.gender  = t3.code AND t3.item_id = 2023000000000000004" +
            " WHERE t1.delete_mark = 0 GROUP BY t3.name";
        list = SqlRunnerAdapter.db().selectList(sql);
        List<ItemCountVo> genderList = new ArrayList<>();
        for (Map<String, Object> objectMap : list) {
            genderList.add(
                    new ItemCountVo(){{
                        setItem(objectMap.get("item").toString());
                        setCount(Integer.parseInt(objectMap.get("count").toString()));
                    }}
            );
        }
        result.setGenderList(genderList);

        sql = "SELECT IFNULL(t3.name,'未填写') AS item ,COUNT(*) AS COUNT FROM xjr_user t1" +
            " INNER JOIN base_teacher t2 ON t1.id = t2.user_id" +
            " LEFT JOIN xjr_dictionary_detail t3 ON t2.employ_way  = t3.code AND t3.item_id = 2023000000000000016" +
            " WHERE t1.delete_mark = 0 GROUP BY t3.name";
        list = SqlRunnerAdapter.db().selectList(sql);
        List<ItemCountVo> employList = new ArrayList<>();
        for (Map<String, Object> objectMap : list) {
            genderList.add(
                    new ItemCountVo(){{
                        setItem(objectMap.get("item").toString());
                        setCount(Integer.parseInt(objectMap.get("count").toString()));
                    }}
            );
        }
        result.setEmployList(employList);

        sql = "SELECT IFNULL(t4.name,'未填写') AS item ,COUNT(*) AS COUNT FROM xjr_user t1" +
            " INNER JOIN base_teacher t2 ON t1.id = t2.user_id" +
            " LEFT JOIN xjr_user_dept_relation t3 ON t1.id  = t3.user_id" +
            " LEFT JOIN xjr_department t4 ON t3.dept_id = t4.id" +
            " WHERE t1.delete_mark = 0 AND t4.is_major = 1 GROUP BY t4.name";

        list = SqlRunnerAdapter.db().selectList(sql);
        List<ItemCountVo> deptList = new ArrayList<>();
        for (Map<String, Object> objectMap : list) {
            genderList.add(
                    new ItemCountVo(){{
                        setItem(objectMap.get("item").toString());
                        setCount(Integer.parseInt(objectMap.get("count").toString()));
                    }}
            );
        }
        result.setDeptList(deptList);

        MPJLambdaWrapper<XjrUser> queryWrapper = MPJWrappers.<XjrUser>lambdaJoin()
                .disableSubLogicDel()
                .orderByDesc(XjrUser::getId)
                .select(XjrUser::getId)
                .select(XjrUser.class,x -> VoToColumnUtil.fieldsToColumns(XjrUserPageVo.class).contains(x.getProperty()))
                .innerJoin(BaseTeacher.class,BaseTeacher::getUserId,XjrUser::getId)
                .leftJoin(DictionaryDetail.class,DictionaryDetail::getCode,BaseTeacher::getJobState, ext->ext.selectAs(DictionaryDetail::getName, XjrUserPageVo::getJobState))
                .leftJoin(DictionaryDetail.class,DictionaryDetail::getCode,XjrUser::getCredentialType,ext->ext.selectAs(DictionaryDetail::getName, XjrUserPageVo::getCredentialType))
                .leftJoin(DictionaryDetail.class,DictionaryDetail::getCode,BaseTeacher::getEmployWay,ext->ext.selectAs(DictionaryDetail::getName, XjrUserPageVo::getEmployWay))

                .selectAsClass(BaseTeacher.class, XjrUserPageVo.class);

        List<XjrUserPageVo> teacherList = teacherService.selectJoinList(XjrUserPageVo.class, queryWrapper);
        List<String> idCardList = teacherList.stream().map(XjrUserPageVo::getCredentialNumber).collect(Collectors.toList());
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        LocalDate currentDate = LocalDate.now();
        int age20 = 0;
        int age30 = 0;
        int age40 = 0;
        int age50 = 0;
        int age60 = 0;
        for (String idCard : idCardList) {
            String birthdayStr = idCard.substring(6, 14);
            java.util.Date date = sdf.parse(birthdayStr);

            // 将Date对象转换为LocalDate对象
            LocalDate birthDate = date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
            Period ageObj = Period.between(birthDate, currentDate);
            int age = ageObj.getYears();
            if(age >= 20 && age <= 29){
                age20 ++;
            }else if(age >= 30 && age <= 39){
                age30 ++;
            }else if(age >= 40 && age <= 49){
                age40 ++;
            }else if(age >= 50 && age <= 59){
                age50 ++;
            }else if(age >= 60){
                age60 ++;
            }
        }

        List<ItemCountVo> ageList = new ArrayList<>();
        ItemCountVo itemCountVo = new ItemCountVo();
        itemCountVo.setItem("20-29岁");
        itemCountVo.setCount(age20);
        ageList.add(itemCountVo);

        itemCountVo = new ItemCountVo();
        itemCountVo.setItem("30-39岁");
        itemCountVo.setCount(age30);
        ageList.add(itemCountVo);

        itemCountVo = new ItemCountVo();
        itemCountVo.setItem("40-49岁");
        itemCountVo.setCount(age40);
        ageList.add(itemCountVo);

        itemCountVo = new ItemCountVo();
        itemCountVo.setItem("50-59岁");
        itemCountVo.setCount(age50);
        ageList.add(itemCountVo);

        itemCountVo = new ItemCountVo();
        itemCountVo.setItem("60岁以上");
        itemCountVo.setCount(age60);
        ageList.add(itemCountVo);

        result.setAgeList(ageList);

        return RT.ok(result);
    }

}