package jnpf.attendance.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import io.seata.common.util.StringUtils;
import jnpf.attendance.mapper.AttendanceConfirmSettingMapper;
import jnpf.attendance.service.AttendanceConfirmService;
import jnpf.attendance.service.AttendanceConfirmSettingService;
import jnpf.base.service.SuperServiceImpl;
import jnpf.entity.attendance.AttendanceConfirmSetting;
import jnpf.model.attendance.dto.ConfirmSettingSubmitDto;
import jnpf.model.attendance.vo.attendance.ConfirmSettingInfoVo;
import jnpf.util.FtbUtil;
import jnpf.util.ServiceException;
import jnpf.util.UserProvider;
import jnpf.util.context.ThreadContext;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 考勤确认设置
 *
 * @author shitou
 * @email shitou@niujiekeji.com
 * @date 2024-11-07 09:33:43
 */
@Slf4j
@Service
public class AttendanceConfirmSettingServiceImpl extends SuperServiceImpl<AttendanceConfirmSettingMapper, AttendanceConfirmSetting> implements AttendanceConfirmSettingService {
    @Resource(name = "ioIntensiveThreadPool")
    private ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private TransactionTemplate transactionTemplate;
    @Resource
    private AttendanceConfirmService attendanceConfirmService;

    @Override
    public ConfirmSettingInfoVo getConfirmSetting(Boolean isNew) {
        List<AttendanceConfirmSetting> confirmSettingList = this.lambdaQuery().list();
        ConfirmSettingInfoVo build = ConfirmSettingInfoVo.builder()
                .confirmMonth(2)
                .allowConfirm(1)
                .confirmDuration(7)
                .slippageResult(1)
                .build();
        if (CollUtil.isEmpty(confirmSettingList)) {
            return build;
        }
        AttendanceConfirmSetting confirmSetting = confirmSettingList.size() == 2 ?
                confirmSettingList.stream().filter(setting -> isNew && setting.getNextData().equals(1)).findFirst().orElse(null) :
                confirmSettingList.stream().findFirst().orElse(null);
        if (Objects.isNull(confirmSetting)) {
            return build;
        }
        return ConfirmSettingInfoVo.builder()
                .id(confirmSetting.getId())
                .confirmMonth(confirmSetting.getConfirmMonth())
                .allowConfirm(confirmSetting.getAllowConfirm())
                .confirmDuration(confirmSetting.getConfirmDuration())
                .slippageResult(confirmSetting.getSlippageResult())
                .build();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean confirmSettingSubmit(ConfirmSettingSubmitDto dto) {
        //新增、修改或者删除考勤确认设置
        saveOrUpdateOrDelete(dto);
        //下一次应用
        if (dto.getOptMethod().equals(1)) {
            return Boolean.TRUE;
        }
        Assert.isFalse(dto.getSlippageResult().equals(2), "不能设置逾期不可确认");
        //处理考勤确认数据
        YearMonth yearMonth = YearMonth.of(LocalDate.now().getYear(), LocalDate.now().getMonth().getValue());
        Date startDate = Date.from(yearMonth.atDay(dto.getAllowConfirm()).atStartOfDay(ZoneId.systemDefault()).toInstant());
        int currentYear = yearMonth.getYear(), currentMonth = yearMonth.getMonth().getValue();
        if (Objects.equals(dto.getConfirmMonth(), 2)) {
            yearMonth = yearMonth.minusMonths(1);
            currentYear = yearMonth.getYear();
            currentMonth = yearMonth.getMonth().getValue();
        }
        Date expectedTime = DateUtil.offsetDay(startDate, dto.getConfirmDuration());
        //异步生成考勤确认数据
        int finalCurrentMonth = currentMonth;
        int finalCurrentYear = currentYear;
        CompletableFuture.runAsync(ThreadContext.wrap(() -> {
                    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
                        @Override
                        protected void doInTransactionWithoutResult(@NotNull TransactionStatus status) {
                            try {
                                attendanceConfirmService.createConfirm(finalCurrentYear, finalCurrentMonth, expectedTime, startDate, dto.getSlippageResult());
                            } catch (Exception e) {
                                log.error("异步生成考勤确认数据失败，事务回滚", e);
                                status.setRollbackOnly();
                                throw e;
                            }
                        }
                    });
                }), threadPoolExecutor)
                .exceptionally(ex -> {
                    log.error("生成考勤确认数据异常", ex);
                    return null;
                });
        return Boolean.TRUE;
    }

    /**
     * 新增、修改或者删除考勤确认设置
     *
     * @param dto 实体参数类
     */
    private void saveOrUpdateOrDelete(ConfirmSettingSubmitDto dto) {
        if (StringUtils.isNotEmpty(dto.getId())) {
            AttendanceConfirmSetting confirmSetting = this.getById(dto.getId());
            ServiceException.notNull(confirmSetting, "未查询到考勤确认设置");
            if (confirmSetting.getNextData().equals(1) && dto.getOptMethod().equals(2)) {
                LambdaQueryWrapper<AttendanceConfirmSetting> queryWrapper = Wrappers.lambdaQuery();
                queryWrapper.eq(AttendanceConfirmSetting::getNextData, Boolean.FALSE);
                this.baseMapper.delete(queryWrapper);
                confirmSetting.setNextData(0);
                BeanUtils.copyProperties(dto, confirmSetting);
                confirmSetting.setLastModifyTime(new Date());
                confirmSetting.setLastModifyUserId(UserProvider.getUser().getUserId());
                this.updateById(confirmSetting);
            } else if (confirmSetting.getNextData().equals(0) && dto.getOptMethod().equals(1)) {
                AttendanceConfirmSetting nextConfirmSetting = AttendanceConfirmSetting.builder()
                        .id(FtbUtil.getId())
                        .confirmMonth(dto.getConfirmMonth())
                        .allowConfirm(dto.getAllowConfirm())
                        .confirmDuration(dto.getConfirmDuration())
                        .slippageResult(dto.getSlippageResult())
                        .nextData(1)
                        .creatorTime(new Date())
                        .creatorUserId(UserProvider.getUser().getUserId())
                        .build();
                this.save(nextConfirmSetting);
            } else {
                BeanUtils.copyProperties(dto, confirmSetting);
                confirmSetting.setLastModifyTime(new Date());
                confirmSetting.setLastModifyUserId(UserProvider.getUser().getUserId());
                this.updateById(confirmSetting);
            }
        } else {
            this.save(AttendanceConfirmSetting.builder()
                    .id(FtbUtil.getId())
                    .confirmMonth(dto.getConfirmMonth())
                    .allowConfirm(dto.getAllowConfirm())
                    .confirmDuration(dto.getConfirmDuration())
                    .slippageResult(dto.getSlippageResult())
                    .nextData(0)
                    .creatorTime(new Date())
                    .creatorUserId(UserProvider.getUser().getUserId())
                    .build());
        }
    }
}
