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

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.mortals.framework.annotation.UnAuth;
import com.mortals.framework.common.Rest;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.model.Context;
import com.mortals.framework.model.OrderCol;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.util.StringUtils;
import com.mortals.framework.util.ThreadPool;
import com.mortals.framework.utils.ServletUtils;
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.key.RedisKey;
import com.mortals.xhx.common.keys.RedisCacheKeys;
import com.mortals.xhx.common.utils.AddAttendThread;
import com.mortals.xhx.module.attendance.model.*;
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.attendance.service.AttendanceRecordHikService;
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.door.model.DoorEntity;
import com.mortals.xhx.module.door.model.DoorQuery;
import com.mortals.xhx.module.door.service.DoorService;
import com.mortals.xhx.module.hik.door.model.req.door.HikDoorEventReq;
import com.mortals.xhx.module.staff.model.StaffEntity;
import com.mortals.xhx.module.staff.model.StaffQuery;
import com.mortals.xhx.module.staff.service.StaffService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StopWatch;
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 org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import static com.mortals.xhx.common.key.RedisKey.KEY_ATTENDANCE_STAT_LOCK_CACHE;

/**
 * 海康考勤打卡记录汇总信息
 *
 * @author zxfei
 * @date 2023-04-08
 */
@RestController
@RequestMapping("attendance/record/hik")
@Slf4j
public class AttendanceRecordHikController extends BaseCRUDJsonBodyMappingController<AttendanceRecordHikService, AttendanceRecordHikEntity, Long> {

    @Autowired
    private ParamService paramService;
    @Autowired
    private DeptService deptService;
    @Autowired
    private StaffService staffService;
    @Autowired
    private UploadService uploadService;
    @Autowired
    private AttendanceExportRecordService attendanceExportRecordService;
    @Autowired
    private AttendanceRecordHikService hikService;
    @Autowired
    private AttendanceGroupService groupService;
    @Autowired
    private AttendanceClassService classService;
    @Autowired
    private ICacheService cacheService;
    @Value("${hik.host:}")
    protected String hikhost;

    @Autowired
    private DoorService doorService;

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

    @Override
    protected void init(Map<String, Object> model, Context context) {
        super.init(model, context);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doImportDataBefore(List<AttendanceRecordHikEntity> list, boolean updateSupport, Context context) throws AppException {
        list.forEach(item -> {
            String orgName = item.getOrgName();
            //todo split orgName to deptId and deptName
            String[] split = orgName.split("/");
            String deptName = split[split.length - 1];
            DeptQuery deptQuery = new DeptQuery();
            deptQuery.setDeptName(deptName);
            //通过分解的deptName去获取部门信息
            List<DeptEntity> deptEntities = deptService.find(deptQuery);
            Long deptId = 0L;
            //判断查出来的部门信息是否为一个,是的话直接获取id,不是的话获取部门的父级id,通过父级id和deptName去查部门id
            if (StringUtils.isNotNull(deptEntities) && deptEntities.size() == 1) {
                deptId = deptEntities.get(0).getId();
            } else {
                String parentDeptName = split[split.length - 2];
                deptQuery.setDeptName(parentDeptName);
                DeptEntity deptEntity = deptService.selectOne(deptQuery);
                deptQuery.setDeptName(deptName);
                deptQuery.setParentId(deptEntity.getParentId());
                DeptEntity dept = deptService.selectOne(deptQuery);
                deptId = dept.getId();
            }
            //通过工号去查询员工信息
            String workNum = item.getWorkNum();
            String replace = StrUtil.replace(workNum, "\t", "");
            StaffEntity staffEntity = staffService.selectOne(new StaffQuery().workNum(replace));
            item.setWorkNum(replace);
            item.setStaffId(staffEntity.getId());
            item.setDeptId(deptId);
            item.setDeptName(deptName);
            item.setSalaId(staffEntity.getSalaId());
            item.setSalaName(staffEntity.getSalaName());
            item.setPositionId(staffEntity.getPositionId());
            item.setPositionName(staffEntity.getPositionName());
        });
        //存入海康打卡原始数据 mortals_xhx_attendance_record_hik
        super.doImportDataBefore(list, updateSupport, context);
    }

    @Override
    protected int batchSaveAfter(List<AttendanceRecordHikEntity> list, Map<String, Object> model, Context context) throws AppException {
        return super.batchSaveAfter(list, model, context);
    }

    @PostMapping(value = "/addAttendanceRecord")
    @UnAuth
    public Rest<String> addAttendanceRecord(@RequestBody AttendanceRecordHikEntity hikEntity) {
        Rest<String> ret = new Rest();
        Map<String, Object> model = new HashMap();
        Context context = this.getContext();
        String busiDesc = "查询" + this.getModuleDesc();
        int code = 1;
        try {
            if (ObjectUtils.isEmpty(hikEntity.getAttendanceDateStart())) {
                throw new AppException("请选择开始日期");
            }
            AddAttendThread addAttendThread = new AddAttendThread(hikService, hikEntity, getContext());
            ThreadPool.getInstance().execute(addAttendThread);
            model.put("message_info", busiDesc + "成功");
            this.recordSysLog(this.request, busiDesc + " 【成功】");
        } catch (Exception e) {
            code = -1;
            this.doException(this.request, busiDesc, model, e);
            model.put("message_info", e.getMessage());
        }

        this.init(model, context);
        ret.setCode(code);
        ret.setMsg(model.get("message_info") == null ? "" : model.remove("message_info").toString());
        return ret;

    }

    @PostMapping(value = "/addAttendanceRecordCustom")
    @UnAuth
    public Rest<String> addAttendanceRecordCustom(@RequestBody AttendanceRecordHikQuery hikEntity) {
        Rest<String> ret = new Rest();
        Map<String, Object> model = new HashMap();
        Context context = this.getContext();
        String busiDesc = "查询" + this.getModuleDesc();
        int code = 1;
        try {
            //天数区间分段计算
            DateTime attendStart = DateUtil.parseDate(hikEntity.getAttendanceDateStart());
            DateTime attendEnd = DateUtil.parseDate(hikEntity.getAttendanceDateEnd());
            Long compare = DateUtil.between(attendEnd, attendStart, DateUnit.DAY);
            StopWatch stopWatch = new StopWatch("stopwatch attend1");
            log.info("考勤计算天数区间：{}", compare);
            for (int i = 0; i <= compare.intValue(); i++) {
                DateTime curDate = DateUtil.offsetDay(attendStart, i);
                log.info("考勤虚增记录日期：{}", curDate.toDateStr());
                stopWatch.start("执行本地方法");
                hikEntity.setAttendanceDateStart(curDate.toDateStr());
                hikEntity.setAttendanceDateEnd(curDate.toDateStr());
                hikService.buildCustomHikRecord(hikEntity, context);
                stopWatch.stop();
                log.info("考勤添加虚增日期：{} 完成,耗时：{}ms", curDate.toDateStr(), stopWatch.getLastTaskTimeMillis());
            }
            model.put("message_info", busiDesc + "成功");
            this.recordSysLog(this.request, busiDesc + " 【成功】");
        } catch (Exception e) {
            code = -1;
            this.doException(this.request, busiDesc, model, e);
            model.put("message_info", e.getMessage());
        }

        this.init(model, context);
        ret.setCode(code);
        ret.setMsg(model.get("message_info") == null ? "" : model.remove("message_info").toString());
        return ret;
    }


    @PostMapping(value = "/addAttendanceHikRecordCustom")
    @UnAuth
    public Rest<String> addAttendanceHikRecordCustom(@RequestBody AttendanceRecordHikQuery hikEntity) {
        Rest<String> ret = new Rest();
        Map<String, Object> model = new HashMap();
        Context context = this.getContext();
        String busiDesc = "查询" + this.getModuleDesc();
        int code = 1;
        try {
            //天数区间分段计算
            DateTime attendStart = DateUtil.parseDate(hikEntity.getAttendanceDateStart());
            DateTime attendEnd = DateUtil.parseDate(hikEntity.getAttendanceDateEnd());
            Long compare = DateUtil.between(attendEnd, attendStart, DateUnit.DAY);
            StopWatch stopWatch = new StopWatch("stopwatch attend1");
            log.info("考勤计算天数区间：{}", compare);
            for (int i = 0; i <= compare.intValue(); i++) {
                DateTime curDate = DateUtil.offsetDay(attendStart, i);
                log.info("考勤计算日期：{}", curDate.toDateStr());
                stopWatch.start("执行本地方法");
                hikEntity.setAttendanceDateStart(curDate.toDateStr());
                hikEntity.setAttendanceDateEnd(curDate.toDateStr());
                if (!ObjectUtils.isEmpty(hikhost)) {
                    hikService.syncDoorEvents(hikEntity);
                    hikService.deletFakeRecord(hikEntity, context);
                } else {
                    HikDoorEventReq hikDoorEventReq = new HikDoorEventReq();
                    hikDoorEventReq.setStartTime(DateUtil.parseDate(curDate.toDateStr()).toJdkDate());
                    hikDoorEventReq.setEndTime(DateUtil.endOfDay(new Date()));
                    log.info("同步设备上门禁事件！req==>{}", JSON.toJSONString(hikDoorEventReq));
                    List<DoorEntity> doorEntities = doorService.find(new DoorQuery());
                    for (DoorEntity doorEntity : doorEntities) {
                        doorService.syncDoorDeviceEvents(doorEntity, hikDoorEventReq);
                    }
                    this.service.deletFakeRecord(hikEntity, null);
                }
                stopWatch.stop();
                log.info("考勤计算日期：{} 完成,耗时：{}ms", curDate.toDateStr(), stopWatch.getLastTaskTimeMillis());
            }
            model.put("message_info", busiDesc + "成功");
            this.recordSysLog(this.request, busiDesc + " 【成功】");
        } catch (Exception e) {
            code = -1;
            this.doException(this.request, busiDesc, model, e);
            model.put("message_info", e.getMessage());
        }

        this.init(model, context);
        ret.setCode(code);
        ret.setMsg(model.get("message_info") == null ? "" : model.remove("message_info").toString());
        return ret;

    }


    @Override
    protected void doListBefore(AttendanceRecordHikEntity query, Map<String, Object> model, Context context) throws AppException {
        if (query.getStaffName() != null && !query.getStaffName().equals("")) {
            String staffName = "%" + query.getStaffName() + "%";
            query.setStaffName(staffName);
        }
        if (ObjectUtils.isEmpty(query.getOrderColList())) {
            query.setOrderColList(Arrays.asList(new OrderCol("attendanceDate", OrderCol.DESCENDING)));
        } else {
            query.getOrderColList().add(new OrderCol("attendanceDate", OrderCol.DESCENDING));
        }
        super.doListBefore(query, model, context);
    }


    @Override
    public void doExportBefore(Context context, AttendanceRecordHikEntity query, List<String> properties) throws AppException {
        if (ObjectUtils.isEmpty(query.getAttendanceDateStart())) {
            query.setAttendanceDateStart(DateUtil.beginOfMonth(new Date()).toDateStr());
            query.setAttendanceDateEnd(DateUtil.today());
        }

        if (ObjectUtils.isEmpty(query.getOrderColList())) {
            query.setOrderColList(Arrays.asList(new OrderCol("attendanceDate", OrderCol.DESCENDING)));
        } else {
            query.getOrderColList().add(new OrderCol("attendanceDate", OrderCol.DESCENDING));
        }
        String bool = cacheService.get(RedisCacheKeys.getHikExportLockKey());
        if(!ObjectUtils.isEmpty(bool)){
            throw new AppException("考勤数据正在导出中，请稍后再试！");
        }
        cacheService.set(RedisCacheKeys.getHikExportLockKey(), "111", 120L);
    }

    @Override
    public void doExportFileAfter(byte[] data, AttendanceRecordHikEntity 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.getShiftsId())) {
            AttendanceClassEntity attendanceClassEntity = classService.get(query.getShiftsId(), context);
            attendanceExportRecordEntity.setClassName(attendanceClassEntity == null ? "" : attendanceClassEntity.getClassName());
        }

        attendanceExportRecordEntity.setCreateUserId(context.getUser().getId());
        attendanceExportRecordEntity.setCreateTime(new Date());
        attendanceExportRecordEntity.setType(1);
        attendanceExportRecordEntity.setExportOrImport(1);
        attendanceExportRecordService.save(attendanceExportRecordEntity);
        cacheService.del(RedisCacheKeys.getHikExportLockKey());

    }

}
