package com.mortals.xhx.module.attendance.web;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.annotation.Excel;
import com.mortals.framework.common.Rest;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.model.OrderCol;
import com.mortals.framework.model.PageInfo;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.util.DateUtils;
import com.mortals.framework.util.StringUtils;
import com.mortals.framework.utils.ReflectUtils;
import com.mortals.framework.utils.ServletUtils;
import com.mortals.framework.utils.poi.ExcelUtil;
import com.mortals.framework.web.BaseCRUDJsonBodyMappingController;
import com.mortals.xhx.base.system.param.service.ParamService;
import com.mortals.xhx.base.system.upload.service.UploadService;
import com.mortals.xhx.common.code.*;
import com.mortals.xhx.common.keys.RedisCacheKeys;
import com.mortals.xhx.module.attendance.model.*;
import com.mortals.xhx.module.attendance.model.vo.AttendStatInfo;
import com.mortals.xhx.module.attendance.service.AttendanceClassService;
import com.mortals.xhx.module.attendance.service.AttendanceExportRecordService;
import com.mortals.xhx.module.attendance.service.AttendanceGroupService;
import com.mortals.xhx.module.dept.model.DeptEntity;
import com.mortals.xhx.module.dept.model.DeptQuery;
import com.mortals.xhx.module.dept.service.DeptService;
import com.mortals.xhx.module.perform.model.PerformAttendAppealEntity;
import com.mortals.xhx.module.perform.model.vo.AppealInfo;
import com.mortals.xhx.module.staff.service.StaffService;
import org.apache.commons.io.IOUtils;
import org.apache.poi.ss.formula.functions.T;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mortals.framework.model.Context;

import com.mortals.xhx.module.attendance.service.AttendanceRecordService;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * 考勤打卡记录信息
 *
 * @author zxfei
 * @date 2023-04-07
 */
@RestController
@RequestMapping("attendance/record")
public class AttendanceRecordController extends BaseCRUDJsonBodyMappingController<AttendanceRecordService, AttendanceRecordEntity, Long> {

    @Autowired
    private ParamService paramService;
    @Autowired
    private UploadService uploadService;

    @Autowired
    private AttendanceGroupService attendanceGroupService;

    @Autowired
    private DeptService deptService;

    @Autowired
    private AttendanceClassService attendanceClassService;
    @Autowired
    private StaffService staffService;
    @Autowired
    private AttendanceExportRecordService attendanceExportRecordService;
    @Autowired
    private AttendanceGroupService groupService;
    @Autowired
    private AttendanceClassService classService;
    @Autowired
    private ICacheService cacheService;

    public AttendanceRecordController() {
        super.setModuleDesc("考勤打卡记录信息");
    }

    @Override
    protected void init(Map<String, Object> model, Context context) {
        this.addDict(model, "deptId", deptService.find(new DeptQuery()).stream().collect(Collectors.toMap(x -> x.getId().toString(), y -> y.getDeptName())));
        this.addDict(model, "groupId", attendanceGroupService.find(new AttendanceGroupQuery()).stream().collect(Collectors.toMap(x -> x.getId().toString(), y -> y.getGroupName())));
        this.addDict(model, "classId", attendanceClassService.find(new AttendanceClassQuery()).stream().collect(Collectors.toMap(x -> x.getId().toString(), y -> y.getClassName())));
        this.addDict(model, "goWorkResult", GoWorkResultEnum.getEnumMap());
        this.addDict(model, "offWorkResult", OffWorkResultEnum.getEnumMap());
        this.addDict(model, "signInResult", NormalEnum.getEnumMap());
        this.addDict(model, "signOutResult", NormalEnum.getEnumMap());
        this.addDict(model, "punchResult", NormalEnum.getEnumMap());
        //设置
        super.init(model, context);
    }

    @Override
    public void doExportBefore(Context context, AttendanceRecordEntity query, List<String> properties) throws AppException {

        if (query.getGroupId() != null) {
            query.setAttendanceGroupId(query.getGroupId());
        }

        if (ObjectUtils.isEmpty(query.getAttendanceDateStart())) {
            query.setAttendanceDateStart(DateUtil.today());
            query.setAttendanceDateEnd(DateUtil.today());
        }
        try {
            if (DateUtils.getBetween(query.getAttendanceDateStart(), query.getAttendanceDateEnd(), DateUtils.P_yyyy_MM_dd, 2) != 0) {
                throw new AppException("选择的日期跨度不能超过一天");
            }
        }catch (Exception e){
            throw new AppException(e.getMessage());
        }
        if (ObjectUtils.isEmpty(query.getOrderColList())) {
            query.setOrderColList(Arrays.asList(new OrderCol("attendanceDate", OrderCol.DESCENDING), new OrderCol("staffName", OrderCol.DESCENDING)));
        } else {
            query.getOrderColList().add(new OrderCol("attendanceDate", OrderCol.DESCENDING));
            // query.getOrderColList().add(new OrderCol("staffName", OrderCol.DESCENDING));
        }

        if (!ObjectUtils.isEmpty(query.getProperties())) {
          /*  //通过属性转换为注解
            List<Field> tempFields = new ArrayList<>();
            tempFields.addAll(Arrays.asList(ReflectUtil.getFields(AttendanceRecordEntity.class)));
            for (Field field : tempFields) {
                if (field.isAnnotationPresent(Excel.class)) {
                    Excel column = field.getAnnotation(Excel.class);
                    if (column != null && query.getProperties().contains(field.getName())) {
                        properties.add(column.name());
                    }
                }
            }*/

            properties.addAll(query.getProperties());
        }
        String bool = cacheService.get(RedisCacheKeys.getRecordExportLockKey());
        if (!ObjectUtils.isEmpty(bool)) {
            throw new AppException("每日数据正在导出中，请稍后再试！");
        }
        cacheService.set(RedisCacheKeys.getRecordExportLockKey(), "111", 120L);
    }

    @Override
    public void doExportAfter(Context context, List<AttendanceRecordEntity> list) throws AppException {
      /*  ArrayList<AttendanceRecordEntity> attendanceRecordEntities = new ArrayList<>();

        for (AttendanceRecordEntity recordEntity : list) {
            List<AttendanceRecordDetailEntity> attendanceRecordDetailList = recordEntity.getAttendanceRecordDetailList();
            if (!ObjectUtils.isEmpty(attendanceRecordDetailList)) {
                for (AttendanceRecordDetailEntity detailEntity : attendanceRecordDetailList) {
                    AttendanceRecordEntity attendanceRecord = new AttendanceRecordEntity();
                    attendanceRecord.initAttrValue();
                    BeanUtils.copyProperties(recordEntity, attendanceRecord);
                    attendanceRecord.setAttendanceRecordDetailEntity(detailEntity);
                    attendanceRecordEntities.add(attendanceRecord);
                }
            } else {
                AttendanceRecordEntity attendanceRecord = new AttendanceRecordEntity();
                attendanceRecord.initAttrValue();
                BeanUtils.copyProperties(recordEntity, attendanceRecord);
                attendanceRecordEntities.add(attendanceRecord);
            }
        }

        if (!ObjectUtils.isEmpty(attendanceRecordEntities)) {
            list.clear();
            list.addAll(attendanceRecordEntities);
        }*/
    }

    @Override
    public void exportExcel(@RequestBody AttendanceRecordEntity query) {
        Context context = getContext();
        String busiDesc = "导出" + getModuleDesc();
        List<String> properties = new ArrayList<>();
        try {
            doExportBefore(context, query, properties);
            String name = StringUtils.trim(this.moduleDesc);
            if (StringUtils.isEmpty(name)) {
                name = System.currentTimeMillis() + "";
            }

            String fileName = name + ".xlsx";
            PageInfo pageInfo = new PageInfo(-1);
            List<AttendanceRecordEntity> list = this.service.find(query, pageInfo, getContext()).getList();
            Class<AttendanceRecordEntity> tClass = ReflectUtils.getClassGenricType(getClass(), 1);
            ExcelUtil<AttendanceRecordEntity, Long> util = new ExcelUtil<>(tClass);
            doExportAfter(context, list);
            //求详细最大数量
            final Integer colSize = list.parallelStream().map(i -> i.getAttendanceRecordDetailList().size()).max(Comparator.comparing(Integer::intValue)).orElseGet(() -> 0);
            //构建动态list
            List<List<Map<String, Object>>> lists = list.stream().map(item -> {
                List<AttendanceRecordDetailEntity> attendanceRecordDetailList = item.getAttendanceRecordDetailList();
                List<Map<String, Object>> mapList = new ArrayList<>();

                for (int i = 0; i < colSize; i++) {

                    if (attendanceRecordDetailList.size() > i) {
                        AttendanceRecordDetailEntity detailEntity = attendanceRecordDetailList.get(i);
                        //生成上班  结果  下班 结果 4个map
                        Map<String, Object> map = new HashMap<>();
                        map.put("name", String.format("上班打卡时间%d", i + 1));
                        map.put("value", detailEntity.getGoWorkDate()==null?"--":DateUtil.formatDateTime(detailEntity.getGoWorkDate()));
                        mapList.add(map);

                        map = new HashMap<>();
                        map.put("name", String.format("上班打卡结果%d", i + 1));
                        map.put("value", detailEntity.getGoWorkResult()==null?"--":GoWorkResultEnum.getByValue(detailEntity.getGoWorkResult()).getDesc());
                        mapList.add(map);

                        map = new HashMap<>();
                        map.put("name", String.format("下班打卡时间%d", i + 1));
                        map.put("value", detailEntity.getOffWorkDate()==null?"--":DateUtil.formatDateTime(detailEntity.getOffWorkDate()));
                        mapList.add(map);

                        map = new HashMap<>();
                        map.put("name", String.format("下班打卡结果%d", i + 1));
                        map.put("value",detailEntity.getOffWorkResult()==null?"--":OffWorkResultEnum.getByValue(detailEntity.getOffWorkResult()).getDesc());
                        mapList.add(map);
                    } else {
                        Map<String, Object> map = new HashMap<>();
                        map.put("name", String.format("上班打卡时间%d", i + 1));
                        map.put("value", "--");
                        mapList.add(map);

                        map = new HashMap<>();
                        map.put("name", String.format("上班打卡结果%d", i + 1));
                        map.put("value", "--");
                        mapList.add(map);

                        map = new HashMap<>();
                        map.put("name", String.format("下班打卡时间%d", i + 1));
                        map.put("value", "--");
                        mapList.add(map);

                        map = new HashMap<>();
                        map.put("name", String.format("下班打卡结果%d", i + 1));
                        map.put("value", "--");
                        mapList.add(map);
                    }
                }
                return mapList;
            }).collect(Collectors.toList());

            byte[] data = util.exportExcel(list, properties, lists, name);

            // byte[] data = util.exportExcel(list, properties, name);
            doExportFileAfter(data, query, context);
            this.responseStream(response, data, fileName);
            recordSysLog(request, busiDesc + " 【成功】");
        } catch (Exception e) {
            JSONObject ret = new JSONObject();
            ret.put("code", -1);
            ret.put("msg", e.getMessage());
            try {
                IOUtils.write(ret.toJSONString(), response.getOutputStream());
            }catch (IOException e1){

            }

            this.doException(request, "导出异常", new HashMap<>(), e);
        }
    }

    @Override
    public void doExportFileAfter(byte[] data, AttendanceRecordEntity query, Context context) throws AppException {
        InputStream inputStream = new ByteArrayInputStream(data);
        MultipartFile file = ServletUtils.getMultipartFile(inputStream, "kaoqin.xlsx");
        String filePath = uploadService.saveFileUpload(file, "file/fileupload", null);
        AttendanceExportRecordEntity attendanceExportRecordEntity = new AttendanceExportRecordEntity();
        attendanceExportRecordEntity.initAttrValue();
        attendanceExportRecordEntity.setFilePath(filePath);
        if (query.getIdList() != null) {
            List<Long> idList = query.getIdList();
            attendanceExportRecordEntity.setRecordIdList(idList.stream().map(i -> i.toString()).collect(Collectors.joining(",")));
        }
        if (query.getAttendanceDateStart() != null) {
            String attendanceDate = query.getAttendanceDateStart() + "~" + query.getAttendanceDateEnd();
            attendanceExportRecordEntity.setAttendanceDate(attendanceDate);
        }
        if (!ObjectUtils.isEmpty(query.getAttendanceGroupId())) {
            AttendanceGroupEntity attendanceGroupEntity = groupService.get(query.getAttendanceGroupId());
            attendanceExportRecordEntity.setGroupName(attendanceGroupEntity == null ? "" : attendanceGroupEntity.getGroupName());
        }

        if (!ObjectUtils.isEmpty(query.getDeptId())) {
            DeptEntity deptEntity = deptService.get(query.getDeptId(), context);
            attendanceExportRecordEntity.setDeptName(deptEntity == null ? "" : deptEntity.getDeptName());
        }

        if (!ObjectUtils.isEmpty(query.getClassId())) {
            AttendanceClassEntity attendanceClassEntity = classService.get(query.getClassId(), context);
            attendanceExportRecordEntity.setClassName(attendanceClassEntity == null ? "" : attendanceClassEntity.getClassName());
        }
        attendanceExportRecordEntity.setCreateUserId(context.getUser().getId());
        attendanceExportRecordEntity.setCreateTime(new Date());
        attendanceExportRecordEntity.setType(2);
        attendanceExportRecordEntity.setExportOrImport(1);
        attendanceExportRecordService.save(attendanceExportRecordEntity);

        cacheService.del(RedisCacheKeys.getRecordExportLockKey());

    }


    @Override
    protected void doListBefore(AttendanceRecordEntity query, Map<String, Object> model, Context context) throws AppException {
        if (query.getClassId() != null) {
            query.setClassId(query.getClassId());
        }
        if (query.getGroupId() != null) {
            query.setAttendanceGroupId(query.getGroupId());
        }

        if (ObjectUtils.isEmpty(query.getAttendanceDateStart())) {
            query.setAttendanceDateStart(DateUtil.today());
            query.setAttendanceDateEnd(DateUtil.today());
        }

        if (ObjectUtils.isEmpty(query.getOrderColList())) {
            query.setOrderColList(Arrays.asList(new OrderCol("attendanceDate", OrderCol.DESCENDING), new OrderCol("staffName", OrderCol.DESCENDING)));
        } else {
            query.getOrderColList().add(new OrderCol("attendanceDate", OrderCol.DESCENDING));
            // query.getOrderColList().add(new OrderCol("staffName", OrderCol.DESCENDING));
        }

        super.doListBefore(query, model, context);
    }


    static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
        Map<Object,Boolean> seen = new ConcurrentHashMap<>();
        //putIfAbsent方法添加键值对，如果map集合中没有该key对应的值，则直接添加，并返回null，如果已经存在对应的值，则依旧为原来的值。
        //如果返回null表示添加数据成功(不重复)，不重复(null==null :TRUE)
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    @Override
    protected int doListAfter(AttendanceRecordEntity query, Map<String, Object> model, Context context) throws AppException {

        List<AttendanceRecordEntity> list = (List<AttendanceRecordEntity>) model.get(KEY_RESULT_DATA);
        //求出列表中最多detail 的 添加动态列
        List<AttendanceRecordDetailEntity> collect = list.parallelStream()
                .flatMap(item -> item.getAttendanceRecordDetailList().stream())
                .filter(distinctByKey(f->f.getOrderNum()))
                .sorted(Comparator.comparingInt(AttendanceRecordDetailEntity::getOrderNum))
                .collect(Collectors.toList());

        //list.parallelStream().collect(Collectors.groupingBy())
        List<AttDsyncColumn> dsyncColumns = new ArrayList<>();


   /*     for (int i = 1; i <= collect.size(); i++) {
            AttDsyncColumn attDsyncColumn = new AttDsyncColumn();
            attDsyncColumn.setName(String.format("上班%s打卡时间", i));
            attDsyncColumn.setProperty("goWorkDate");
            attDsyncColumn.setOrderNum(i);
            dsyncColumns.add(attDsyncColumn);

            attDsyncColumn = new AttDsyncColumn();
            attDsyncColumn.setName(String.format("上班%s打卡结果", i));
            attDsyncColumn.setProperty("goWorkResult");
            attDsyncColumn.setOrderNum(i);
            dsyncColumns.add(attDsyncColumn);

            attDsyncColumn = new AttDsyncColumn();
            attDsyncColumn.setName(String.format("下班%s打卡时间", i));
            attDsyncColumn.setProperty("offWorkDate");
            attDsyncColumn.setOrderNum(i);
            dsyncColumns.add(attDsyncColumn);

            attDsyncColumn = new AttDsyncColumn();
            attDsyncColumn.setName(String.format("下班%s打卡结果",i));
            attDsyncColumn.setProperty("offWorkResult");
            attDsyncColumn.setOrderNum(i);
            dsyncColumns.add(attDsyncColumn);
        }*/

        collect.forEach(item -> {
            AttDsyncColumn attDsyncColumn = new AttDsyncColumn();
            attDsyncColumn.setName(String.format("上班%s打卡时间", item.getOrderNum()));
            attDsyncColumn.setProperty("goWorkDate");
            attDsyncColumn.setOrderNum(item.getOrderNum());
            dsyncColumns.add(attDsyncColumn);

            attDsyncColumn = new AttDsyncColumn();
            attDsyncColumn.setName(String.format("上班%s打卡结果", item.getOrderNum()));
            attDsyncColumn.setProperty("goWorkResult");
            attDsyncColumn.setOrderNum(item.getOrderNum());
            dsyncColumns.add(attDsyncColumn);

            attDsyncColumn = new AttDsyncColumn();
            attDsyncColumn.setName(String.format("下班%s打卡时间", item.getOrderNum()));
            attDsyncColumn.setProperty("offWorkDate");
            attDsyncColumn.setOrderNum(item.getOrderNum());
            dsyncColumns.add(attDsyncColumn);

            attDsyncColumn = new AttDsyncColumn();
            attDsyncColumn.setName(String.format("下班%s打卡结果", item.getOrderNum()));
            attDsyncColumn.setProperty("offWorkResult");
            attDsyncColumn.setOrderNum(item.getOrderNum());
            dsyncColumns.add(attDsyncColumn);
        });
        Set<Integer> dsyncSet = dsyncColumns.stream().map(i -> i.getOrderNum()).collect(Collectors.toSet());
        //填充空的detail 对象
        Iterator<AttendanceRecordEntity> iterator = list.iterator();
        while (iterator.hasNext()) {
            AttendanceRecordEntity recordEntity = iterator.next();

            Set<Integer> existSet = recordEntity.getAttendanceRecordDetailList().stream().map(i -> i.getOrderNum()).collect(Collectors.toSet());
            //判断详细列表中是否存在顺序 没有自动填充空对象数据
            List<AttendanceRecordDetailEntity> addDetailList = dsyncSet.stream().map(i -> {
                if (!existSet.contains(i)) {
                    AttendanceRecordDetailEntity recordDetailEntity = new AttendanceRecordDetailEntity();
                    recordDetailEntity.initAttrValue();
                    recordDetailEntity.setOffWorkDate(null);
                    recordDetailEntity.setGoWorkDate(null);
                    recordDetailEntity.setOrderNum(i);
                    return recordDetailEntity;
                }
                return null;
            }).filter(f -> f != null).collect(Collectors.toList());

            if (!ObjectUtils.isEmpty(addDetailList)) {
                recordEntity.getAttendanceRecordDetailList().addAll(addDetailList);
                List<AttendanceRecordDetailEntity> orderList = recordEntity.getAttendanceRecordDetailList().stream().sorted(Comparator.comparingInt(AttendanceRecordDetailEntity::getOrderNum))
                        .collect(Collectors.toList());
                recordEntity.setAttendanceRecordDetailList(orderList);
            }
        }
        model.put("dyncColumns", dsyncColumns);
        return super.doListAfter(query, model, context);
    }


    /**
     * 每日统计
     */
    @PostMapping(value = "stat")
    public String statByDay() {
        JSONObject jsonObject = new JSONObject();
        String busiDesc = this.getModuleDesc() + "统计";
        try {
            // Rest<AttendStatInfo> rest = this.service.statByDay(getContext());
//            if (rest.getCode() == YesNoEnum.NO.getValue()) {
//                throw new AppException("统计异常！");
//            } else {
//                jsonObject.put(KEY_RESULT_DATA, rest.getData());
//            }
            recordSysLog(request, busiDesc + " 【成功】");
            jsonObject.put(KEY_RESULT_CODE, VALUE_RESULT_SUCCESS);
        } catch (Exception e) {
            log.error("申诉审核", e);
            jsonObject.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
            jsonObject.put(KEY_RESULT_MSG, super.convertException(e));
        }
        return jsonObject.toJSONString();
    }
}
