package com.ruoyi.oa.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.oa.domain.OaSalaryItem;
import com.ruoyi.oa.domain.bo.SysOaAttendanceBo;
import com.ruoyi.oa.domain.vo.CalcResultVo;
import com.ruoyi.oa.domain.vo.SysOaAttendanceVo;
import com.ruoyi.oa.domain.vo.SysOaHolidayVo;
import com.ruoyi.oa.mapper.OaSalaryItemMapper;
import com.ruoyi.oa.service.ISysOaAttendanceService;
import com.ruoyi.oa.service.ISysOaHolidayService;
import com.ruoyi.oa.utils.OwnHttpUtils;
import com.ruoyi.system.mapper.SysUserRoleMapper;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.workflow.mapper.WfDeployFormMapper;
import com.ruoyi.workflow.service.IWfTaskService;
import lombok.RequiredArgsConstructor;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.Deployment;
import org.flowable.task.api.TaskQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.oa.domain.bo.OaSalaryBo;
import com.ruoyi.oa.domain.vo.OaSalaryVo;
import com.ruoyi.oa.domain.OaSalary;
import com.ruoyi.oa.mapper.OaSalaryMapper;
import com.ruoyi.oa.service.IOaSalaryService;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;

/**
 * 薪资管理Service业务层处理
 *
 * @author hdka
 * @date 2025-03-06
 */
@RequiredArgsConstructor
@Service
public class OaSalaryServiceImpl implements IOaSalaryService {

    private final OaSalaryMapper baseMapper;

    private final IWfTaskService wfTaskService;

    private final WfDeployFormMapper deployFormMapper;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private TaskService taskService;

    @Resource
    private HistoryService historyService;


    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysOaHolidayService holidayService;

    @Autowired
    private OaSalaryItemMapper salaryItemMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private ISysOaAttendanceService sysOaAttendanceService;

    @Autowired
    private ISysOaHolidayService sysOaHolidayService;

    /**
     * 查询薪资管理
     */
    @Override
    public OaSalaryVo queryById(Long salaryId,Date payTime){
        return baseMapper.selectVoAndItemVoById(salaryId,payTime);
    }

    /**
     * 查询薪资管理列表
     * 职工为一个月的固定工资 所以不需要计算日薪乘以工作日数量 所以区别于车间工人
     */
    @Override
    public TableDataInfo<OaSalaryVo> queryPageList(OaSalaryBo bo, PageQuery pageQuery) {

        List<Long> userIds = sysUserRoleMapper.selectUserIdsByNotRoleId(1852970465740505090L);
        QueryWrapper<OaSalary> lqw = new QueryWrapper<>();
        lqw.in("os.user_id",userIds);
        // 判断一下是否传参 如果没传参则默认查询当月记录，如果当月记录为空则创造当月的记录然后返回回去
        Date payTime = bo.getPayTime()!=null?bo.getPayTime():new Date();
        Page<OaSalaryVo> result = baseMapper.selectStaffVoPage(pageQuery.build(), lqw,payTime);
        if (result.getRecords().isEmpty()){
            // 判断是否有当前月的记录 没有则进行创造新的记录
            this.importSalaryUser(payTime);
            result = baseMapper.selectStaffVoPage(pageQuery.build(), lqw,payTime);
        }

        // 写入真实薪资 为日薪乘以工作日-罚金
        for (OaSalaryVo record : result.getRecords()) {
            record.setRealSalary(record.getBaseSalary()+record.getPunishSalary()-record.getInsure());
        }
        return TableDataInfo.build(result);
    }


    @Override
    public TableDataInfo<OaSalaryVo> listWorker(OaSalaryBo bo, PageQuery pageQuery) {
        // 还得计算一下这个月的工作日
        Date payTime = bo.getPayTime()!=null?bo.getPayTime():new Date();
        Long dayNum = sysOaHolidayService.queryWorkDayNumByMonth(payTime);
        List<Long> userIds = sysUserRoleMapper.selectUserIdsByRoleId(1852970465740505090L);
        QueryWrapper<OaSalary> lqw = new QueryWrapper<>();
        lqw.in("os.user_id",userIds);
        Page<OaSalaryVo> result = baseMapper.selectStaffVoPage(pageQuery.build(), lqw,payTime);
        if (result.getRecords().isEmpty()){
            // 判断是否有当前月的记录 没有则进行创造新的记录
            this.importSalaryUser(payTime);
            result = baseMapper.selectStaffVoPage(pageQuery.build(), lqw,payTime);
        }

        // 写入真实薪资 为日薪乘以工作日-罚金
        for (OaSalaryVo record : result.getRecords()) {
            record.setRealSalary(record.getBaseSalary()*dayNum+record.getPunishSalary()-record.getInsure());
        }
        return TableDataInfo.build(result);
    }

    @Override
    public R<Void> importSalaryUser(Date payTime) {
        List<SysUser> sysUsers = userService.selectUserList2();
        baseMapper.delByPayTime(payTime);
        for (SysUser user : sysUsers) {
            if(Objects.nonNull(user)){
                OaSalary oaSalary = new OaSalary();
                oaSalary.setUserId(user.getUserId());
                oaSalary.setInsure(user.getInsure());
                oaSalary.setBaseSalary(Double.valueOf(user.getLaborCost()));
                oaSalary.setPayTime(payTime);
                baseMapper.insert(oaSalary);
            }

        }
        return R.ok();
    }


    /**
     * 查询薪资管理列表
     */
    @Override
    public List<OaSalaryVo> queryList(OaSalaryBo bo) {
        LambdaQueryWrapper<OaSalary> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<OaSalary> buildQueryWrapper(OaSalaryBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<OaSalary> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getUserId() != null, OaSalary::getUserId, bo.getUserId());
        return lqw;
    }

    /**
     * 新增薪资管理
     */
    @Override
    public Boolean insertByBo(OaSalaryBo bo) {
        OaSalary add = BeanUtil.toBean(bo, OaSalary.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setSalaryId(add.getSalaryId());
        }
        return flag;
    }

    /**
     * 修改薪资管理
     */
    @Override
    public Boolean updateByBo(OaSalaryBo bo) {
        OaSalary update = BeanUtil.toBean(bo, OaSalary.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(OaSalary entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除薪资管理
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 计算薪资
     *
     * @param bo
     * @return
     */
    @Override
    public String calcSalary(OaSalaryBo bo) {
        // 1先获取对应的json路径  "/home/ubuntu/lyq/fad_oa_kqdeal/202411.json"
        String jsonPath = OwnHttpUtils.getJsonName(bo.getMonthStr(), bo.getFilePath());
        // 2拿到所有的用户
        Date yearMonth = StringToDate(combineYearMonth(bo.getMonthStr()));
        // 添加去重思想 即拿到月份如果之前计算过，则将原来的全部删掉然后新增新的
        // baseMapper.delByPayTime(yearMonth);
        baseMapper.delItemByPayYearMonth(yearMonth);

        List<SysUser> sysUsers = userService.selectUserList2();
        // 3遍历user列表 拿出所有的nickName分别拿到他们的json 进行第二步分析
        for (SysUser user : sysUsers) {
            OaSalary salary = baseMapper.selectVoByUserId(user.getUserId(),yearMonth);
            if (salary == null) {
                return jsonPath;
            }
            // 3.1 拿到一个人打卡机的记录
            CalcResultVo res = OwnHttpUtils.getBaseCalc(jsonPath,user.getNickName());
            // 3.2 通过打卡机拿到的记录去和差旅表、请假申请表、假期表做比对
            HistoricProcessInstanceQuery historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery()
                    .startedBy(String.valueOf(user.getUserId()))
                    .orderByProcessInstanceStartTime()
                    .desc();
            // 3.3 查询到他所有的任务 其中包括了差旅申请和请假申请的事
            List<HistoricProcessInstance> list = historicProcessInstanceQuery.list();
            for (HistoricProcessInstance historicProcessInstance : list) {
                Deployment deployment = repositoryService.createDeploymentQuery()
                        .deploymentId(historicProcessInstance.getDeploymentId()).singleResult();
                // 这个deployment可以拿到他的分类信息 这里我需要的是 trip和absence
                if (deployment.getCategory()=="absence" || deployment.getCategory()=="trip") {
                    TaskQuery taskQuery = taskService.createTaskQuery()
                            .taskCategory(deployment.getCategory());
                    taskQuery.list().forEach(task -> {
                        Map<String, Object> processVariables = task.getProcessVariables();
                        String startTime = String.valueOf(processVariables.get("startTime"));
                        String endTime = String.valueOf(processVariables.get("endTime"));
                        List<String> timeRange =  getDateRange(startTime, endTime);
                        if (deployment.getCategory().equals("absence")){
                            // 这里的item为1时则为是上午  否则为下午
                            Long startTimeItem = (Long) processVariables.get("startTimeItem");
                            Long endTimeItem = (Long) processVariables.get("endTimeItem");
                        }
                        // 计算缺席的某天是否请假或者出差
                        List<String> realDates = new ArrayList<>();
                        List<String> real05Dates = new ArrayList<>();
                        for (String absentDate : res.getAbsentDates()) {
                            // 形如2024-11-01
                            String fullDate = combineYearMonthAndDay(bo.getMonthStr(), absentDate);
                            boolean contains = timeRange.contains(fullDate);
                            // 如果为false 则说明此天没有请假或者出差则将其记录
                            if (!contains) {
                                realDates.add(fullDate);
                            }
                        }
                        for (String real05Date : res.getAbsent05Dates()) {
                            String fullDate = combineYearMonthAndDay(bo.getMonthStr(), real05Date);
                            boolean contains = timeRange.contains(fullDate);
                            if (!contains) {
                                real05Dates.add(fullDate);
                            }
                        }
                        res.setAbsentDates(realDates);
                        res.setAbsent05Dates(real05Dates);
                        // 这次已经将缺勤拿出来了 现在还要判断今天是否为节假日
                        List<String> realDate2 =new ArrayList<>();
                        for (String realDate : res.getAbsentDates()) {
                            SysOaHolidayVo sysOaHolidayVo = holidayService.queryHolidayByDate(StringToDate(realDate));
                            if (sysOaHolidayVo != null && sysOaHolidayVo.getType()!=0 ) {
                                // 如果不为节假日则加入进去
                                realDate2.add(realDate);
                            }
                        }
                        List<String> real05Date2 =new ArrayList<>();
                        for (String real05Date : res.getAbsent05Dates()) {
                            SysOaHolidayVo sysOaHolidayVo = holidayService.queryHolidayByDate(StringToDate(real05Date));
                            if (sysOaHolidayVo != null && sysOaHolidayVo.getType()!=0 ) {
                                // 如果不为节假日则加入进去
                                real05Date2.add(real05Date);
                            }

                        }
                        res.setAbsentDates(realDate2);
                        res.setAbsent05Dates(real05Date2);

                    });
                }
            }
            List<String> days = getDays(bo, res.getAbsentDates());
            res.setAbsentDates(days);
            List<String> days05 = getDays(bo, res.getAbsent05Dates());
            res.setAbsentDates(days);
            res.setAbsent05Dates(days05);
            buildItemAbsent(res.getAbsentDates(), salary,1.0,yearMonth);
            buildItemAbsent(res.getAbsent05Dates(), salary,0.5,yearMonth);

            // 3.4创建其他的item包括了好多罚金
            // 3.4.1 迟到罚金
            OaSalaryItem oaSalaryItem = new OaSalaryItem();
            StringBuilder sb= new StringBuilder();
            if (!res.getLateDates().isEmpty()) {
                for (String lateDate : res.getLateDates()) {
                    sb.append(lateDate);
                    sb.append(",");
                }
                sb.append("迟到");

                oaSalaryItem.setSalaryId(salary.getSalaryId());
                oaSalaryItem.setPrice(Double.valueOf(res.getLateFee()));
                oaSalaryItem.setType(4L);
                oaSalaryItem.setFlag(0L);
                oaSalaryItem.setReason(sb.toString());
                oaSalaryItem.setSignTime(yearMonth);
                salaryItemMapper.insert(oaSalaryItem);
            }

            // 3.4.2 早退罚金
            sb= new StringBuilder();
            if (!res.getLeaveEarly10Dates().isEmpty()) {
                for (String lateDate : res.getLeaveEarly10Dates()) {
                    sb.append(lateDate);
                    sb.append(",");
                }
                sb.append("以上为早退十分钟日期");
            }

            if (!res.getLeaveEarly30Dates().isEmpty()) {
                for (String lateDate : res.getLeaveEarly30Dates()) {
                    sb.append(lateDate);
                    sb.append(",");
                }
                sb.append("以上为早退三十分钟日期");
            }

            if (!res.getLeaveEarly30Dates().isEmpty() || !res.getLeaveEarly10Dates().isEmpty()) {
                oaSalaryItem = new OaSalaryItem();
                oaSalaryItem.setSalaryId(salary.getSalaryId());
                oaSalaryItem.setPrice(Double.valueOf(res.getLeaveEarlyFee()));
                oaSalaryItem.setType(5L);
                oaSalaryItem.setFlag(0L);
                oaSalaryItem.setReason(sb.toString());
                oaSalaryItem.setSignTime(yearMonth);
                salaryItemMapper.insert(oaSalaryItem);
            }
            // 3.4.3 打卡罚金
            if (!res.getLateDates().isEmpty()){
                sb= new StringBuilder();
                for (String lateDate : res.getLateDates()) {
                    sb.append(lateDate);
                    sb.append(",");
                }
                sb.append("未打卡");
                oaSalaryItem = new OaSalaryItem();
                oaSalaryItem.setSalaryId(salary.getSalaryId());
                oaSalaryItem.setPrice(Double.valueOf(res.getNotAttendanceFee()));
                oaSalaryItem.setType(6L);
                oaSalaryItem.setFlag(0L);
                oaSalaryItem.setReason(sb.toString());
                oaSalaryItem.setSignTime(yearMonth);
                salaryItemMapper.insert(oaSalaryItem);
            }
        }

        return "计算成功";
    }

    private List<String> getDays(OaSalaryBo bo, List<String> res) {
        List<String> days = new ArrayList<>();
        for (String absentDate : res) {
            String dateString = combineYearMonthAndDay(bo.getMonthStr(), absentDate);
            Date date = StringToDate2(dateString);
            SysOaAttendanceBo sysOaAttendanceBo = new SysOaAttendanceBo();
            sysOaAttendanceBo.setCreateTime(date);
            SysOaAttendanceVo sysOaAttendanceVo = !sysOaAttendanceService.queryList(sysOaAttendanceBo).isEmpty() ?sysOaAttendanceService.queryList(sysOaAttendanceBo).get(0):null;
            if (sysOaAttendanceVo != null && sysOaAttendanceVo.getProjectId()!=0) {
                days.add(absentDate);
            }
        }
        return days;
    }


    private void buildItemAbsent(List<String> realDate2, OaSalary salary,Double flag,Date yearMonth) {
        // 最终这个为缺勤日期
        if (realDate2.isEmpty()) {
            return;
        }
        OaSalaryItem oaSalaryItem = new OaSalaryItem();
        StringBuilder sb = new StringBuilder();
        // 接下来将他拼接成一个字符串为扣款原因矿工问题
        for (String s : realDate2) {
            sb.append(s);
            sb.append(",");
        }
        if (flag>0.5){
            sb.append("旷工整天");
        }else{
            sb.append("旷工半天");
        }
        oaSalaryItem.setType(3L);
        oaSalaryItem.setReason(String.valueOf(sb));
        oaSalaryItem.setPrice(2* salary.getBaseSalary()* realDate2.size()*flag);
        oaSalaryItem.setFlag(0L);
        oaSalaryItem.setSignTime(yearMonth);
        oaSalaryItem.setSalaryId(salary.getSalaryId());
        salaryItemMapper.insert(oaSalaryItem);
    }


    /**
     * 获取从 startTime 到 endTime（含首尾）的所有日期列表
     *
     * @param startTime 字符串格式 "yyyy-MM-dd" 的开始日期
     * @param endTime   字符串格式 "yyyy-MM-dd" 的结束日期
     * @return 日期列表，每个元素形如 "yyyy-MM-dd"
     */
    public static List<String> getDateRange(String startTime, String endTime) {
        // 1. 定义解析与输出格式
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

        try {
            // 2. 解析成 LocalDate
            LocalDate startDate = LocalDate.parse(startTime, formatter);
            LocalDate endDate = LocalDate.parse(endTime, formatter);

            // 若开始日期大于结束日期，则抛异常（也可根据需求改成自动调换顺序）
            if (startDate.isAfter(endDate)) {
                throw new IllegalArgumentException("开始日期不得晚于结束日期: "
                        + startTime + " > " + endTime);
            }

            // 3. 循环遍历日期，加入列表
            List<String> dateList = new ArrayList<>();
            LocalDate currentDate = startDate;
            while (!currentDate.isAfter(endDate)) {
                // 每次加入 "yyyy-MM-dd" 格式字符串
                dateList.add(currentDate.format(formatter));
                // 加 1 天
                currentDate = currentDate.plusDays(1);
            }

            return dateList;
        } catch (DateTimeParseException e) {
            // 如果传入的日期字符串格式不对，抛出异常或自行处理
            throw new IllegalArgumentException("日期格式错误，要求 yyyy-MM-dd", e);
        }
    }


    /**
     * 将形如 "202411" (yyyyMM) 和 "11-04" (MM-dd) 拼装成 "2024-11-04" (yyyy-MM-dd)
     *
     * @param yearMonth 形如 "202411" 的字符串 (前4位为年份, 后2位为月份)
     * @param monthDay  形如 "11-04" 的字符串 (前2位为月份, 后2位为天数)
     * @return 拼装好的 "yyyy-MM-dd"
     */
    private String combineYearMonthAndDay(String yearMonth, String monthDay) {
        // 1. 拆解第一个参数: "202411" => 年 (2024), 月 (11)
        String year = yearMonth.substring(0, 4);   // "2024"
        String monthFromYearMonth = yearMonth.substring(4, 6); // "11"

        // 2. 拆解第二个参数: "11-04" => 月 (11), 日 (04)
        String[] parts = monthDay.split("-");
        if (parts.length != 2) {
            throw new IllegalArgumentException("传入的 monthDay 格式不正确, 应形如 'MM-dd'。实际传入: " + monthDay);
        }
        String monthFromMonthDay = parts[0]; // "11"
        String day = parts[1];              // "04"

        // 3. 校验两个“月”是否一致（可选）
        if (!monthFromYearMonth.equals(monthFromMonthDay)) {
            throw new IllegalArgumentException("yearMonth 与 monthDay 中的 '月' 不匹配: "
                    + monthFromYearMonth + " != " + monthFromMonthDay);
        }

        // 4. 组合成 yyyy-MM-dd 格式
        return year + "-" + monthFromYearMonth + "-" + day;
    }

    /**
     * 将形如 "202411" (yyyyMM) 和 "11-04" (MM-dd) 拼装成 "2024-11-04" (yyyy-MM-dd)
     *
     * @param yearMonth 形如 "202411" 的字符串 (前4位为年份, 后2位为月份)
     * @return 拼装好的 "yyyy-MM-dd"
     */
    private String combineYearMonth(String yearMonth) {
        // 1. 拆解第一个参数: "202411" => 年 (2024), 月 (11)
        String year = yearMonth.substring(0, 4);   // "2024"
        String monthFromYearMonth = yearMonth.substring(4, 6); // "11"
        // 4. 组合成 yyyy-MM-dd 格式
        return year + "-" + monthFromYearMonth;
    }

    private Date StringToDate(String dateStr) {

        // 1. 解析字符串到 YearMonth（只包含年和月）
        YearMonth yearMonth = YearMonth.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM"));

        // 2. 在 YearMonth 基础上指定“日”为 1
        LocalDate localDate = yearMonth.atDay(1);

        // 3. 转成 Instant
        Instant instant = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant();

        // 4. 转成旧版本 Date
        return Date.from(instant);
    }

    private Date StringToDate2(String dateStr) {
        System.out.println(dateStr);
        // 1. 解析字符串到 YearMonth（只包含年和月）
        YearMonth yearMonth = YearMonth.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        // 2. 在 YearMonth 基础上指定“日”为 1
        LocalDate localDate = yearMonth.atDay(1);

        // 3. 转成 Instant
        Instant instant = localDate.atStartOfDay(ZoneId.systemDefault()).toInstant();

        // 4. 转成旧版本 Date
        return Date.from(instant);
    }

    private String DateToString(Date date) {
        // 2. 构造解析器，指定格式。例如 "yyyy-MM-dd"
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 3. 调用 parse() 将字符串转为 Date

        return sdf.format(date);

    }
}
