package com.ruoyi.oa.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.utils.StringUtils;
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.*;
import com.ruoyi.oa.domain.vo.EmployeeSalaryRecordVo;
import com.ruoyi.oa.domain.vo.OaBindingItemDetailVo;
import com.ruoyi.oa.mapper.*;
import com.ruoyi.system.mapper.SysDeptMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.oa.domain.bo.OaEmployeeTemplateBindingBo;
import com.ruoyi.oa.domain.vo.OaEmployeeTemplateBindingVo;
import com.ruoyi.oa.service.IOaEmployeeTemplateBindingService;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 员工模板绑定及月度发放记录Service业务层处理
 *
 * @author ruoyi
 * @date 2025-06-23
 */
@RequiredArgsConstructor
@Service
public class OaEmployeeTemplateBindingServiceImpl implements IOaEmployeeTemplateBindingService {
    @Autowired
    private final OaEmployeeTemplateBindingMapper baseMapper;
    @Autowired
    private final OaBindingItemDetailMapper bindingItemDetailMapper;
    @Autowired
    private final OaEmployeeMapper employeeMapper;
    @Autowired
    private final OaSalaryTemplateDetailMapper salaryDetailMapper;
    @Autowired
    private final OaInsuranceTemplateMapper insuranceTemplateMapper;
    @Autowired
    private final OaInsuranceTemplateDetailMapper insuranceDetailMapper;
    @Autowired
    private final SysDeptMapper deptMapper;


    /**
     * 查询员工模板绑定及月度发放记录
     */
    @Override
    public OaEmployeeTemplateBindingVo queryById(Long bindingId){
        return baseMapper.selectVoById(bindingId);
    }

    /**
     * 查询员工模板绑定及月度发放记录列表
     */
    @Override
    public TableDataInfo<OaEmployeeTemplateBindingVo> queryPageList(OaEmployeeTemplateBindingBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<OaEmployeeTemplateBinding> lqw = buildQueryWrapper(bo);
        Page<OaEmployeeTemplateBindingVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);

        if (CollectionUtils.isNotEmpty(result.getRecords())) {
            // 1. 批量查员工
            List<Long> employeeIds = result.getRecords().stream()
                    .map(OaEmployeeTemplateBindingVo::getEmployeeId)
                    .filter(Objects::nonNull)
                    .distinct()
                    .collect(Collectors.toList());
            Map<Long, OaEmployee> employeeMap = new HashMap<>();
            if (!employeeIds.isEmpty()) {
                List<OaEmployee> employees = employeeMapper.selectBatchIds(employeeIds);
                employeeMap = employees.stream().collect(Collectors.toMap(OaEmployee::getEmployeeId, e -> e));
            }

            // 2. 批量查部门
            Set<Long> deptIds = employeeMap.values().stream()
                    .map(OaEmployee::getDeptId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            Map<Long, SysDept> deptMap = new HashMap<>();
            if (!deptIds.isEmpty()) {
                List<SysDept> depts = deptMapper.selectBatchIds(deptIds);
                deptMap = depts.stream().collect(Collectors.toMap(SysDept::getDeptId, d -> d));
            }

            // 3. 封装到VO
            for (OaEmployeeTemplateBindingVo vo : result.getRecords()) {
                OaEmployee emp = employeeMap.get(vo.getEmployeeId());
                if (emp != null) {
                    vo.setCompany(emp.getCompany());
                    SysDept dept = deptMap.get(emp.getDeptId());
                    vo.setDeptName(dept != null ? dept.getDeptName() : null);
                }
            }
            // 提取所有bindingId
            List<Long> bindingIds = result.getRecords().stream()
                    .map(OaEmployeeTemplateBindingVo::getBindingId)
                    .collect(Collectors.toList());

            // 批量查询每个bindingId对应的各项金额
            Map<Long, SalaryCalculationResult> calculationMap = calculateSalaryDetailsByBindingIds(bindingIds);

            // 将计算结果设置到对应的VO对象中
            for (OaEmployeeTemplateBindingVo vo : result.getRecords()) {
                SalaryCalculationResult calc = calculationMap.getOrDefault(vo.getBindingId(), new SalaryCalculationResult());
                vo.setNetSalary(calc.getTotalSalary());           // 薪资总额
                vo.setTotalPersonalInsurance(calc.getTotalPersonalInsurance()); // 个人社保总额
                vo.setTotalCompanyInsurance(calc.getTotalCompanyInsurance());   // 企业社保总额
                vo.setTotalSalary(calc.getNetSalary());               // 实发工资
                vo.setTotalCompanyCost(calc.getTotalCompanyCost()); // 公司总成本
            }
        }

        return TableDataInfo.build(result);
    }

    /**
     * 薪资计算结果内部类
     */
    private static class SalaryCalculationResult {
        private BigDecimal totalSalary = BigDecimal.ZERO;           // 薪资总额
        private BigDecimal totalPersonalInsurance = BigDecimal.ZERO; // 个人社保总额
        private BigDecimal totalCompanyInsurance = BigDecimal.ZERO;  // 企业社保总额
        private BigDecimal netSalary = BigDecimal.ZERO;             // 实发工资
        private BigDecimal totalCompanyCost = BigDecimal.ZERO;      // 公司总成本

        // getters and setters
        public BigDecimal getTotalSalary() { return totalSalary; }
        public void setTotalSalary(BigDecimal totalSalary) { this.totalSalary = totalSalary; }

        public BigDecimal getTotalPersonalInsurance() { return totalPersonalInsurance; }
        public void setTotalPersonalInsurance(BigDecimal totalPersonalInsurance) { this.totalPersonalInsurance = totalPersonalInsurance; }

        public BigDecimal getTotalCompanyInsurance() { return totalCompanyInsurance; }
        public void setTotalCompanyInsurance(BigDecimal totalCompanyInsurance) { this.totalCompanyInsurance = totalCompanyInsurance; }

        public BigDecimal getNetSalary() { return netSalary; }
        public void setNetSalary(BigDecimal netSalary) { this.netSalary = netSalary; }

        public BigDecimal getTotalCompanyCost() { return totalCompanyCost; }
        public void setTotalCompanyCost(BigDecimal totalCompanyCost) { this.totalCompanyCost = totalCompanyCost; }
    }

    /**
     * 根据绑定记录ID集合批量计算薪资明细
     */
    private Map<Long, SalaryCalculationResult> calculateSalaryDetailsByBindingIds(List<Long> bindingIds) {
        if (CollectionUtils.isEmpty(bindingIds)) {
            return Collections.emptyMap();
        }

        Map<Long, SalaryCalculationResult> resultMap = new HashMap<>();

        // 查询所有明细记录
        LambdaQueryWrapper<OaBindingItemDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(OaBindingItemDetail::getBindingId, bindingIds)
                .eq(OaBindingItemDetail::getDelFlag, 0);

        List<OaBindingItemDetail> allDetails = bindingItemDetailMapper.selectList(queryWrapper);

        // 按bindingId分组处理
        Map<Long, List<OaBindingItemDetail>> detailsByBindingId = allDetails.stream()
                .collect(Collectors.groupingBy(OaBindingItemDetail::getBindingId));

        for (Long bindingId : bindingIds) {
            SalaryCalculationResult calc = new SalaryCalculationResult();
            List<OaBindingItemDetail> details = detailsByBindingId.getOrDefault(bindingId, Collections.emptyList());

            for (OaBindingItemDetail detail : details) {
                if (detail.getPaidAmount() != null) {
                    if ("salary".equals(detail.getTemplateType())) {
                        // 薪资项目
                        calc.setTotalSalary(calc.getTotalSalary().add(detail.getPaidAmount()));
                    } else if ("insurance".equals(detail.getTemplateType())) {
                        // 社保项目 - 需要区分个人还是企业
                        OaInsuranceTemplateDetail insuranceDetail = insuranceDetailMapper.selectById(detail.getItemDetailId());
                        if (insuranceDetail != null) {
                            OaInsuranceTemplate template = insuranceTemplateMapper.selectById(insuranceDetail.getInsuranceTemplateId());
                            if (template != null) {
                                if (template.getType() == 0) {
                                    // 个人社保
                                    calc.setTotalPersonalInsurance(calc.getTotalPersonalInsurance().add(detail.getPaidAmount()));
                                } else if (template.getType() == 1) {
                                    // 企业社保
                                    calc.setTotalCompanyInsurance(calc.getTotalCompanyInsurance().add(detail.getPaidAmount()));
                                }
                            }
                        }
                    }
                }

            }
            // 计算实发工资和公司总成本
            BigDecimal personalInsurance = calc.getTotalPersonalInsurance();
            if (personalInsurance.compareTo(BigDecimal.ZERO) < 0) {
                personalInsurance = personalInsurance.abs(); // 或直接设为0
            }
            calc.setNetSalary(calc.getTotalSalary().subtract(personalInsurance));
            calc.setTotalCompanyCost(calc.getTotalSalary().add(calc.getTotalCompanyInsurance()));
            resultMap.put(bindingId, calc);
        }

        return resultMap;
    }
    /**
     * 根据绑定记录ID集合批量计算薪资总额
     * @param bindingIds 绑定记录ID集合
     * @return 绑定记录ID与薪资总额的映射关系
     */
    private Map<Long, BigDecimal> calculateSalaryTotalByBindingIds(List<Long> bindingIds) {
        if (CollectionUtils.isEmpty(bindingIds)) {
            return Collections.emptyMap();
        }

        // 创建查询条件
        LambdaQueryWrapper<OaBindingItemDetail> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(OaBindingItemDetail::getBindingId, bindingIds)
                .eq(OaBindingItemDetail::getTemplateType, "salary") // 只查询薪资类型
                .eq(OaBindingItemDetail::getDelFlag, 0); // 只查询未删除的记录

        // 查询所有符合条件的明细记录
        List<OaBindingItemDetail> details = bindingItemDetailMapper.selectList(queryWrapper);

        // 使用流处理按bindingId分组并计算总额
        return details.stream()
                .collect(Collectors.groupingBy(
                        OaBindingItemDetail::getBindingId,
                        Collectors.reducing(
                                BigDecimal.ZERO,
                                OaBindingItemDetail::getPaidAmount,
                                BigDecimal::add
                        )
                ));
    }
    /**
     * 查询员工模板绑定及月度发放记录列表
     */
    @Override
    public List<OaEmployeeTemplateBindingVo> queryList(OaEmployeeTemplateBindingBo bo) {
        LambdaQueryWrapper<OaEmployeeTemplateBinding> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<OaEmployeeTemplateBinding> buildQueryWrapper(OaEmployeeTemplateBindingBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<OaEmployeeTemplateBinding> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getEmployeeId() != null, OaEmployeeTemplateBinding::getEmployeeId, bo.getEmployeeId());
        lqw.eq(bo.getPersonalInsuranceTemplateId() != null, OaEmployeeTemplateBinding::getPersonalInsuranceTemplateId, bo.getPersonalInsuranceTemplateId());
        lqw.eq(bo.getCompanyInsuranceTemplateId() != null, OaEmployeeTemplateBinding::getCompanyInsuranceTemplateId, bo.getCompanyInsuranceTemplateId());
        lqw.eq(bo.getSalaryTemplateId() != null, OaEmployeeTemplateBinding::getSalaryTemplateId, bo.getSalaryTemplateId());
        lqw.eq(bo.getPayYear() != null, OaEmployeeTemplateBinding::getPayYear, bo.getPayYear());
        lqw.eq(bo.getPayMonth() != null, OaEmployeeTemplateBinding::getPayMonth, bo.getPayMonth());
        lqw.eq(bo.getNetSalary() != null, OaEmployeeTemplateBinding::getNetSalary, bo.getNetSalary());
        lqw.eq(bo.getTotalCompanyCost() != null, OaEmployeeTemplateBinding::getTotalCompanyCost, bo.getTotalCompanyCost());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), OaEmployeeTemplateBinding::getStatus, bo.getStatus());
        return lqw;
    }

    /**
     * 新增员工模板绑定及月度发放记录
     */
    @Override
    public Boolean insertByBo(OaEmployeeTemplateBindingBo bo) {
        OaEmployeeTemplateBinding add = BeanUtil.toBean(bo, OaEmployeeTemplateBinding.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setBindingId(add.getBindingId());
        }
        return flag;
    }

    /**
     * 修改员工模板绑定及月度发放记录
     */
    @Override
    public Boolean updateByBo(OaEmployeeTemplateBindingBo bo) {
        OaEmployeeTemplateBinding update = BeanUtil.toBean(bo, OaEmployeeTemplateBinding.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

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

    /**
     * 批量删除员工模板绑定及月度发放记录
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public Boolean calculateSalary(List<Long> employeeIds, Long payYear, Long payMonth,
                                   Long defaultPersonalInsuranceTemplateId, Long defaultCompanyInsuranceTemplateId, Long defaultSalaryTemplateId) {
        // 参数校验
        if (defaultPersonalInsuranceTemplateId == null || defaultCompanyInsuranceTemplateId == null || defaultSalaryTemplateId == null) {
            throw new RuntimeException("请选择默认的薪资模板和社保模板");
        }

        for (Long employeeId : employeeIds) {
            // 1. 检查本月是否已存在记录
            OaEmployeeTemplateBinding exist = baseMapper.findByEmployeeAndMonth(employeeId, payYear, payMonth);
            if (exist != null) {
                // 已存在，跳过
                continue;
            }

            // 2. 获取上月记录
            Long lastMonth = (payMonth == 1) ? 12 : payMonth - 1;
            Long lastYear = (payMonth == 1) ? payYear - 1 : payYear;
            OaEmployeeTemplateBinding lastBinding = baseMapper.findByEmployeeAndMonth(employeeId, lastYear, lastMonth);

            // 3. 新建本月主表记录
            OaEmployeeTemplateBinding binding = new OaEmployeeTemplateBinding();
            binding.setEmployeeId(employeeId);
            binding.setPayYear(payYear);
            binding.setPayMonth(payMonth);
            binding.setStatus("待发");

            if (lastBinding != null) {
                // 存在上月记录，复制上月的模板ID
                binding.setPersonalInsuranceTemplateId(lastBinding.getPersonalInsuranceTemplateId());
                binding.setCompanyInsuranceTemplateId(lastBinding.getCompanyInsuranceTemplateId());
                binding.setSalaryTemplateId(lastBinding.getSalaryTemplateId());
            } else {
                // 首次配置，使用默认模板
                binding.setPersonalInsuranceTemplateId(defaultPersonalInsuranceTemplateId);
                binding.setCompanyInsuranceTemplateId(defaultCompanyInsuranceTemplateId);
                binding.setSalaryTemplateId(defaultSalaryTemplateId);
            }

            // 插入主表记录
            baseMapper.insert(binding);

            // 4. 生成明细快照
            if (lastBinding != null) {
                // 如果存在上月记录，复制上月的实际支付金额
                copyLastMonthDetails(lastBinding.getBindingId(), binding.getBindingId());
            } else {
                // 首次配置，使用模板默认金额
                createNewDetails(binding.getBindingId(), binding.getSalaryTemplateId(),
                        binding.getPersonalInsuranceTemplateId(), binding.getCompanyInsuranceTemplateId());
            }

            // 5. 更新主表的汇总金额
            updateBindingTotals(binding.getBindingId());
        }
        return true;
    }

    /**
     * 复制上月明细记录
     */
    private void copyLastMonthDetails(Long lastBindingId, Long newBindingId) {
        // 查询上月所有明细
        List<OaBindingItemDetail> lastDetails = bindingItemDetailMapper.findByBindingId(lastBindingId);

        for (OaBindingItemDetail lastDetail : lastDetails) {
            OaBindingItemDetail newDetail = new OaBindingItemDetail();
            newDetail.setBindingId(newBindingId);
            newDetail.setTemplateType(lastDetail.getTemplateType());
            newDetail.setItemDetailId(lastDetail.getItemDetailId());
            newDetail.setPaidAmount(lastDetail.getPaidAmount()); // 复制实际支付金额
            bindingItemDetailMapper.insert(newDetail);
        }
    }

    /**
     * 创建新的明细记录（使用模板默认值）
     */
    private void createNewDetails(Long bindingId, Long salaryTemplateId, Long personalInsuranceTemplateId, Long companyInsuranceTemplateId) {
        // 薪资明细
        List<OaSalaryTemplateDetail> salaryDetails = salaryDetailMapper.findByTemplateId(salaryTemplateId);
        for (OaSalaryTemplateDetail detail : salaryDetails) {
            OaBindingItemDetail item = new OaBindingItemDetail();
            item.setBindingId(bindingId);
            item.setTemplateType("salary");
            item.setItemDetailId(detail.getSalaryDetailId());
            item.setPaidAmount(detail.getAmount() != null ? detail.getAmount() : BigDecimal.ZERO);
            bindingItemDetailMapper.insert(item);
        }

        // 个人社保明细
        List<OaInsuranceTemplateDetail> personalDetails = insuranceDetailMapper.findByTemplateId(personalInsuranceTemplateId);
        for (OaInsuranceTemplateDetail detail : personalDetails) {
            // 只处理主表type=0的模板
            OaInsuranceTemplate template = insuranceTemplateMapper.selectById(detail.getInsuranceTemplateId());
            if (template != null && template.getType() == 0) {
                OaBindingItemDetail item = new OaBindingItemDetail();
                item.setBindingId(bindingId);
                item.setTemplateType("insurance");
                item.setItemDetailId(detail.getInsuranceDetailId());
                item.setPaidAmount(detail.getAmount() != null ? detail.getAmount() : BigDecimal.ZERO);
                bindingItemDetailMapper.insert(item);
            }
        }

        // 企业社保明细
        List<OaInsuranceTemplateDetail> companyDetails = insuranceDetailMapper.findByTemplateId(companyInsuranceTemplateId);
        for (OaInsuranceTemplateDetail detail : companyDetails) {
            // 只处理主表type=1的模板
            OaInsuranceTemplate template = insuranceTemplateMapper.selectById(detail.getInsuranceTemplateId());
            if (template != null && template.getType() == 1) {
                OaBindingItemDetail item = new OaBindingItemDetail();
                item.setBindingId(bindingId);
                item.setTemplateType("insurance");
                item.setItemDetailId(detail.getInsuranceDetailId());
                item.setPaidAmount(detail.getAmount() != null ? detail.getAmount() : BigDecimal.ZERO);
                bindingItemDetailMapper.insert(item);
            }
        }
    }

    private void updateBindingTotals(Long bindingId) {
        List<OaBindingItemDetail> details = bindingItemDetailMapper.findByBindingId(bindingId);

        BigDecimal totalSalary = BigDecimal.ZERO;
        BigDecimal totalPersonalInsurance = BigDecimal.ZERO;
        BigDecimal totalCompanyInsurance = BigDecimal.ZERO;

        for (OaBindingItemDetail detail : details) {
            if (detail.getPaidAmount() != null) {
                if ("salary".equals(detail.getTemplateType())) {
                    totalSalary = totalSalary.add(detail.getPaidAmount());
                } else if ("insurance".equals(detail.getTemplateType())) {
                    // 通过明细的 insuranceTemplateId 查主表 type
                    OaInsuranceTemplateDetail insuranceDetail = insuranceDetailMapper.selectById(detail.getItemDetailId());
                    if (insuranceDetail != null) {
                        OaInsuranceTemplate template = insuranceTemplateMapper.selectById(insuranceDetail.getInsuranceTemplateId());
                        if (template != null) {
                            // 强制社保金额为正数
                            BigDecimal paid = detail.getPaidAmount().abs();
                            if (template.getType() == 0) {
                                totalPersonalInsurance = totalPersonalInsurance.add(paid);
                            } else if (template.getType() == 1) {
                                totalCompanyInsurance = totalCompanyInsurance.add(paid);
                            }
                        }
                    }
                }
            }
        }

        // 实发工资 = 薪资合计 - 个人社保
        BigDecimal netSalary = totalSalary.subtract(totalPersonalInsurance);
        // 单位总支出 = 薪资合计 + 企业社保
        BigDecimal totalCompanyCost = totalSalary.add(totalCompanyInsurance);

        OaEmployeeTemplateBinding binding = new OaEmployeeTemplateBinding();
        binding.setBindingId(bindingId);
        binding.setNetSalary(netSalary);
        binding.setTotalCompanyCost(totalCompanyCost);
        baseMapper.updateById(binding);
    }


    @Override
    public Boolean finalizeSalary(List<String> bindingIds) {
        for (String id : bindingIds) {
            OaEmployeeTemplateBinding binding = baseMapper.selectById(id);
            if (binding != null && !"已发".equals(binding.getStatus())) {
                binding.setStatus("已发");
                //TODO 这里可以算到财务支出等逻辑
                baseMapper.updateById(binding);
            }
        }
        return true;
    }

    @Override
    public List<EmployeeSalaryRecordVo> querySalaryHistory(Long employeeId) {
        List<OaEmployeeTemplateBinding> list = baseMapper.selectList(
                new LambdaQueryWrapper<OaEmployeeTemplateBinding>().eq(OaEmployeeTemplateBinding::getEmployeeId, employeeId)
                        .orderByDesc(OaEmployeeTemplateBinding::getPayYear, OaEmployeeTemplateBinding::getPayMonth)
        );
        return list.stream().map(this::toVo).collect(Collectors.toList());
    }

    // 工具方法：主表转VO并查明细
    private EmployeeSalaryRecordVo toVo(OaEmployeeTemplateBinding binding) {
        if (binding == null) return null;
        EmployeeSalaryRecordVo vo = new EmployeeSalaryRecordVo();
        BeanUtils.copyProperties(binding, vo);
        OaEmployee emp = employeeMapper.selectById(binding.getEmployeeId());
        if (emp != null) {
            vo.setEmployeeName(emp.getEmployeeName());
            vo.setCompany(emp.getCompany());
        }
        List<OaBindingItemDetail> allDetails = bindingItemDetailMapper.selectList(
                new LambdaQueryWrapper<OaBindingItemDetail>().eq(OaBindingItemDetail::getBindingId, binding.getBindingId())
        );
        vo.setSalaryDetails(allDetails.stream().filter(d -> "salary".equals(d.getTemplateType()))
                .map(this::toDetailVo).collect(Collectors.toList()));
        vo.setInsuranceDetails(allDetails.stream().filter(d -> "insurance".equals(d.getTemplateType()))
                .map(this::toDetailVo).collect(Collectors.toList()));
        return vo;
    }

    private OaBindingItemDetailVo toDetailVo(OaBindingItemDetail detail) {
        OaBindingItemDetailVo vo = new OaBindingItemDetailVo();
        BeanUtils.copyProperties(detail, vo);
        // 可补充itemName等
        return vo;
    }
}

