package com.xjrsoft.module.teacher.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.xjrsoft.common.enums.AttendanceNodeEnum;
import com.xjrsoft.common.exception.MyException;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.module.organization.entity.Post;
import com.xjrsoft.module.organization.entity.UserPostRelation;
import com.xjrsoft.module.teacher.dto.GetCheckInDayDto;
import com.xjrsoft.module.teacher.entity.AttendanceConfig;
import com.xjrsoft.module.teacher.entity.AttendanceRecord;
import com.xjrsoft.module.teacher.entity.XjrUser;
import com.xjrsoft.module.teacher.mapper.AttendanceRecordMapper;
import com.xjrsoft.module.teacher.mapper.XjrUserMapper;
import com.xjrsoft.module.teacher.service.IAttendanceConfigService;
import com.xjrsoft.module.teacher.service.IAttendanceRecordService;
import com.xjrsoft.module.teacher.vo.AttendanceConfigPageVo;
import com.xjrsoft.module.teacher.vo.PresentUserCheckInfoVo;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
* @title: 考勤-记录
* @Author szs
* @Date: 2023-12-18
* @Version 1.0
*/
@Service
@AllArgsConstructor
public class AttendanceRecordServiceImpl extends MPJBaseServiceImpl<AttendanceRecordMapper, AttendanceRecord> implements IAttendanceRecordService {

    private final double EARTH_RADIUS = 6371; // 地球半径，单位为千米
    private final IAttendanceConfigService attendanceConfigService;

    private final AttendanceRecordMapper attendanceRecordMapper;

    private final XjrUserMapper xjrUserMapper;
    @Override
    @Transactional
    public Boolean addAttendanceRecordOfClockIn(AttendanceRecord attendanceRecord) {
        if(ObjectUtil.isNotNull(attendanceRecord.getLongitude())
                && ObjectUtil.isNotNull(attendanceRecord.getLatitude())
                && ObjectUtil.isNotNull(attendanceRecord.getAddress())
                ){//上班打卡
            Long userId = StpUtil.getLoginIdAsLong();
            attendanceRecord.setUserId(userId);
            long time = System.currentTimeMillis();
            Date date_now = new Date(time);
            attendanceRecord.setCheckTime(date_now);
            attendanceRecord.setLeaveReason(AttendanceNodeEnum.STARTWORK.getCode());

            //判断是否已经打卡，一天只能打卡一次
            //一天的开始，结果：2017-03-01 00:00:00
            Date beginOfDay = DateUtil.beginOfDay(attendanceRecord.getCheckTime());

            //一天的结束，结果：2017-03-01 23:59:59
            Date endOfDay = DateUtil.endOfDay(attendanceRecord.getCheckTime());
            LambdaQueryWrapper<AttendanceRecord> queryWrapper01 = new LambdaQueryWrapper<>();
            queryWrapper01
                    .eq(AttendanceRecord::getUserId,attendanceRecord.getUserId())
                    .eq(AttendanceRecord::getLeaveReason,AttendanceNodeEnum.STARTWORK.getCode())
                    .between(AttendanceRecord::getCheckTime,beginOfDay,endOfDay);
            List<AttendanceRecord> attendanceRecordList = this.list(queryWrapper01);
            if(ObjectUtil.isNotNull(attendanceRecordList) && attendanceRecordList.size() > 0){
                throw new MyException("今日已经上班打卡，请勿重复打卡");
            }

            //判断打卡距离范围
            LambdaQueryWrapper<AttendanceConfig> queryWrapper02 = new LambdaQueryWrapper<>();
            queryWrapper02
                    .orderByDesc(AttendanceConfig::getId)
                    .select(AttendanceConfig.class,x -> VoToColumnUtil.fieldsToColumns(AttendanceConfigPageVo.class).contains(x.getProperty()))
                    .eq(AttendanceConfig::getDeleteMark,0)
                    .eq(AttendanceConfig::getEnabledMark,1);
            List<AttendanceConfig> attendanceConfigList = attendanceConfigService.list(queryWrapper02);

            if (attendanceConfigList == null || !(attendanceConfigList.size() > 0)) {
                throw new MyException("打卡配置出现未知错误，请联系管理员！");
            }

            AttendanceConfig attendanceConfigCurrent = attendanceConfigList.get(0);
            //为当前记录添加打卡配置的打卡时间
            attendanceRecord.setAttendanceTime(attendanceConfigCurrent.getAttendanceTime());

            Double latitude = attendanceConfigCurrent.getLatitude();
            Double longitude = attendanceConfigCurrent.getLongitude();
            double distance = calculateDistance(latitude,longitude,attendanceRecord.getLatitude(), attendanceRecord.getLongitude());

            if(distance < attendanceConfigCurrent.getAttendanceRange()){//距离小于配置的打卡范围
                //更改打卡状态
                //打卡配置的每日打卡时间解析
                LocalDateTime localDateTime = LocalDateTime.of(LocalDate.now(),attendanceConfigCurrent.getAttendanceTime());
                Instant instant = localDateTime.atZone(ZoneId.systemDefault()).toInstant();
                Date attendanceConfigTinme = Date.from(instant);

                //添加打卡距离
                attendanceRecord.setDistance(distance);

                if(attendanceConfigTinme.compareTo(attendanceRecord.getCheckTime()) == 1){//当为1表示正常打卡
                    attendanceRecord.setStatus(1);
                }else {
                    attendanceRecord.setStatus(2);
                }
                this.save(attendanceRecord);
            }else{
                throw new MyException("未在打卡范围内，请进入打卡范围！");
            }
            return true;
        }
        return false;
    }

    @Override
    public List<String> getCheckInDay(GetCheckInDayDto dto) {
        if(ObjectUtil.isNotNull(dto.getTimestamp()) && dto.getTimestamp()>0){
            Date date = DateUtil.date(dto.getTimestamp());
            //一个月的开始，结果：2017-03-01 00:00:00
            Date beginOfMonth = DateUtil.beginOfMonth(date);
            //一个月的结束，结果：2017-03-31 23:59:59
            Date endOfMonth = DateUtil.endOfMonth(date);
            List<String> result = attendanceRecordMapper.getCheckInDay(beginOfMonth,endOfMonth);
            return result;
        }
        return new ArrayList<String>();
    }

    @Override
    public PresentUserCheckInfoVo getPresentUserCheckInfo() {
        AttendanceRecord dto = new AttendanceRecord();
        Long userId = StpUtil.getLoginIdAsLong();
        dto.setUserId(userId);
        long time = System.currentTimeMillis();
        Date date_now = new Date(time);

        //一天的开始，结果：2017-03-01 00:00:00
        Date beginOfDay = DateUtil.beginOfDay(date_now);

        //一天的结束，结果：2017-03-01 23:59:59
        Date endOfDay = DateUtil.endOfDay(date_now);

        //已经上班打卡
        MPJLambdaWrapper<AttendanceRecord> queryWrapper = new MPJLambdaWrapper<>();
        queryWrapper
                .disableSubLogicDel()
                .selectAs(XjrUser::getName, PresentUserCheckInfoVo::getUserName)
                .selectAs(Post::getName, PresentUserCheckInfoVo::getPostName)
                .select(AttendanceRecord.class, x -> VoToColumnUtil.fieldsToColumns(PresentUserCheckInfoVo.class).contains(x.getProperty()))
                .leftJoin(XjrUser.class, XjrUser::getId,AttendanceRecord::getUserId)
                .leftJoin(UserPostRelation.class, UserPostRelation::getUserId,AttendanceRecord::getUserId)
                .leftJoin(Post.class, Post::getId,UserPostRelation::getPostId)
                .eq(AttendanceRecord::getUserId,dto.getUserId())
                .eq(AttendanceRecord::getLeaveReason,AttendanceNodeEnum.STARTWORK.getCode())
                .between(AttendanceRecord::getCheckTime,beginOfDay,endOfDay);
        List<PresentUserCheckInfoVo> presentUserCheckInfoVoList = this.selectJoinList(PresentUserCheckInfoVo.class, queryWrapper);

        if(ObjectUtil.isNotNull(presentUserCheckInfoVoList) && presentUserCheckInfoVoList.size() > 0){
            return presentUserCheckInfoVoList.get(0);
        }

        //没有上班打卡
        MPJLambdaWrapper<XjrUser> queryWrapper3 = new MPJLambdaWrapper<>();
        queryWrapper3
                .selectAs(XjrUser::getName, PresentUserCheckInfoVo::getUserName)
                .selectAs(Post::getName, PresentUserCheckInfoVo::getPostName)
                .leftJoin(UserPostRelation.class, UserPostRelation::getUserId,XjrUser::getId)
                .leftJoin(Post.class, Post::getId,UserPostRelation::getPostId)
                .eq(XjrUser::getId,dto.getUserId());
        List<PresentUserCheckInfoVo> presentUserCheckInfoVoList1 = xjrUserMapper.selectJoinList(PresentUserCheckInfoVo.class, queryWrapper3);

        PresentUserCheckInfoVo presentUserCheckInfoVo = new PresentUserCheckInfoVo();
        if(ObjectUtil.isNotNull(presentUserCheckInfoVoList1) && presentUserCheckInfoVoList1.size() > 0){
            presentUserCheckInfoVo = presentUserCheckInfoVoList1.get(0);
        }else{
            LambdaQueryWrapper<XjrUser> queryWrapper4 = new LambdaQueryWrapper<>();
            queryWrapper4
                    .eq(XjrUser::getId,dto.getUserId());
            XjrUser xjrUser = xjrUserMapper.selectOne(queryWrapper4);
            presentUserCheckInfoVo.setUserName(xjrUser.getName());
        }

        LambdaQueryWrapper<AttendanceConfig> queryWrapper02 = new LambdaQueryWrapper<>();
        queryWrapper02
                .orderByDesc(AttendanceConfig::getId)
                .select(AttendanceConfig.class,x -> VoToColumnUtil.fieldsToColumns(AttendanceConfigPageVo.class).contains(x.getProperty()))
                .eq(AttendanceConfig::getDeleteMark,0)
                .eq(AttendanceConfig::getEnabledMark,1);
        List<AttendanceConfig> attendanceConfigList = attendanceConfigService.list(queryWrapper02);
        if (attendanceConfigList == null || !(attendanceConfigList.size() > 0)) {
            throw new MyException("打卡配置出现未知错误，请联系管理员！");
        }

        presentUserCheckInfoVo.setAttendanceTime(attendanceConfigList.get(0).getAttendanceTime());
        presentUserCheckInfoVo.setLeaveReason(AttendanceNodeEnum.STARTWORK.getCode());
        presentUserCheckInfoVo.setStatus(3);

        return presentUserCheckInfoVo;
    }

    //通过经纬度计算距离
    private double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        double dLat = Math.toRadians(lat2 - lat1);
        double dLon = Math.toRadians(lon2 - lon1);

        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
                + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
                * Math.sin(dLon / 2) * Math.sin(dLon / 2);

        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

        double distance = EARTH_RADIUS * c;
        return distance*1000;
    }
}
