package cn.iocoder.yudao.module.expense.service.expenseinfo;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.employee.api.EmployeeApi;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeRespDTO;
import cn.iocoder.yudao.module.employee.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.expense.api.expenseinfo.dto.ExpenseInfoCreateReqDTO;
import cn.iocoder.yudao.module.expense.api.expenseinfo.dto.ExpenseInfoObjCreateReqDTO;
import cn.iocoder.yudao.module.expense.controller.admin.expenseinfo.vo.ExpenseInfoPageReqVO;
import cn.iocoder.yudao.module.expense.controller.admin.expenseinfo.vo.ExpenseInfoRespVO;
import cn.iocoder.yudao.module.expense.controller.admin.expenseinfo.vo.ExpenseInfoSaveReqVO;
import cn.iocoder.yudao.module.expense.dal.dataobject.expenseinfo.ExpenseInfoDO;
import cn.iocoder.yudao.module.expense.dal.dataobject.expenseinfo.ExpenseInfoObjDO;
import cn.iocoder.yudao.module.expense.dal.mysql.expenseinfo.ExpenseInfoMapper;
import cn.iocoder.yudao.module.expense.dal.mysql.expenseinfo.ExpenseInfoObjMapper;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
import cn.iocoder.yudao.module.system.api.permission.dto.DeptDataPermissionRespDTO;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.expense.enums.ErrorCodeConstants.*;

/**
 * 报销信息 Service 实现类
 *
 * @author dp
 */
@Service
@Validated
public class ExpenseInfoServiceImpl implements ExpenseInfoService {

    @Resource
    private ExpenseInfoMapper infoMapper;
    @Resource
    private ExpenseInfoObjMapper expenseInfoObjMapper;

    @Resource
    private EmployeeApi employeeApi;

    @Resource
    private FileApi fileApi;

    @Resource
    private PermissionApi permissionApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createInfo(ExpenseInfoSaveReqVO createReqVO) {
        // 获取当前登录用户ID
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 通过用户ID获取登录员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        // 获取报销员工ID
        Long employeeId = createReqVO.getEmployeeId();
        // 通过员工ID获取员工详细信息
        EmployeeRespDTO employee = employeeApi.getEmployeeById(employeeId);
        // 校验员工信息是否存在
        if (Objects.isNull(loginEmployee) || Objects.isNull(employee)) {
            throw exception(ErrorCodeConstants.EMPLOYEE_INFO_NOT_EXISTS);
        }

        // 处理费用明细信息
        List<ExpenseInfoObjDO> oaExpenseObjs = createReqVO.getExpenseInfoObjs();
        for (ExpenseInfoObjDO oaExpenseObj : oaExpenseObjs) {
            // 设置费用明细的创建人
            // oaExpenseObj.setCreator(String.valueOf(loginEmployee.getId()));
            oaExpenseObj.setMoney(oaExpenseObj.getMoney().setScale(2, BigDecimal.ROUND_HALF_UP));
            oaExpenseObj.setExpenseObjUuid(IdUtil.fastSimpleUUID());

            // 校验费用项目是否为空
            if (oaExpenseObj.getExpenseItemId() == null) {
                throw exception(EXPENSE_INFO_OBJ_ITEM_BLANK);
            }
            // 校验费用金额是否有效
            if (oaExpenseObj.getMoney() == null || oaExpenseObj.getMoney().compareTo(BigDecimal.ZERO) < 1) {
                throw exception(EXPENSE_INFO_OBJ_MONEY_ZERO);
            }
            // 校验开始日期是否为空
            if (StrUtil.isBlank(oaExpenseObj.getStartDate())) {
                throw exception(EXPENSE_INFO_OBJ_START_DATE_NULL);
            }
            // 校验结束日期是否为空
            if (StrUtil.isBlank(oaExpenseObj.getEndDate())) {
                throw exception(EXPENSE_INFO_OBJ_END_DATE_NULL);
            }
        }

        // 准备插入费用主表数据
        ExpenseInfoDO info = BeanUtils.toBean(createReqVO, ExpenseInfoDO.class);
        // 获取所有费用信息的总金额
        BigDecimal totalMoney = info.getTotalMoney().setScale(2, BigDecimal.ROUND_HALF_UP);
        // 使用流操作累加oaExpenseObjs中所有ExpenseInfoObjDO对象的金额，并与0进行比较
        BigDecimal reduce = oaExpenseObjs.stream()
                .map(ExpenseInfoObjDO::getMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .setScale(2, BigDecimal.ROUND_HALF_UP);
        // 比较总金额与累加金额是否一致，如果不一致则抛出异常
        if (totalMoney.compareTo(reduce) != 0) {
            throw exception(EXPENSE_INFO_TOTAL_MONEY_ERROR);
        }
        info.setTotalMoney(info.getTotalMoney().setScale(2, BigDecimal.ROUND_HALF_UP));
        // 生成唯一标识符
        info.setExpenseUuid(IdUtil.fastSimpleUUID());
        // 设置部门ID、岗位ID、用户ID和创建人信息
        info.setDeptId(employee.getDeptId());
        info.setPostId(employee.getPostId());
        info.setStatus("0");
        info.setUserId(loginUserId);
        info.setCreateEmployeeId(loginEmployee.getId());
        // info.setCreator(String.valueOf(loginEmployee.getId()));
        // 插入费用主表数据
        infoMapper.insert(info);

        // 插入费用明细数据
        createExpenseInfoObjList(info.getId(), createReqVO.getExpenseInfoObjs());

        // 附件
        fileApi.updateFileBiz(createReqVO.getFileIdList(), info.getExpenseUuid());

        // 返回费用主表ID
        return info.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfo(ExpenseInfoSaveReqVO updateReqVO) {
        // 获取当前登录用户ID
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 通过用户ID获取登录员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        // 获取报销员工ID
        Long employeeId = updateReqVO.getEmployeeId();
        // 通过员工ID获取员工详细信息
        EmployeeRespDTO employee = employeeApi.getEmployeeById(employeeId);
        // 校验员工信息是否存在
        if (Objects.isNull(loginEmployee) || Objects.isNull(employee)) {
            throw exception(ErrorCodeConstants.EMPLOYEE_INFO_NOT_EXISTS);
        }

        // 处理费用明细信息
        List<ExpenseInfoObjDO> oaExpenseObjs = updateReqVO.getExpenseInfoObjs();
        for (ExpenseInfoObjDO oaExpenseObj : oaExpenseObjs) {
            // 设置费用明细的创建人
            // oaExpenseObj.setCreator(String.valueOf(loginEmployee.getId()));
            oaExpenseObj.setMoney(oaExpenseObj.getMoney().setScale(2, BigDecimal.ROUND_HALF_UP));
            oaExpenseObj.setExpenseObjUuid(IdUtil.fastSimpleUUID());

            // 校验费用项目是否为空
            if (oaExpenseObj.getExpenseItemId() == null) {
                throw exception(EXPENSE_INFO_OBJ_ITEM_BLANK);
            }
            // 校验费用金额是否有效
            if (oaExpenseObj.getMoney() == null || oaExpenseObj.getMoney().compareTo(BigDecimal.ZERO) < 1) {
                throw exception(EXPENSE_INFO_OBJ_MONEY_ZERO);
            }
            // 校验开始日期是否为空
            if (StrUtil.isBlank(oaExpenseObj.getStartDate())) {
                throw exception(EXPENSE_INFO_OBJ_START_DATE_NULL);
            }
            // 校验结束日期是否为空
            if (StrUtil.isBlank(oaExpenseObj.getEndDate())) {
                throw exception(EXPENSE_INFO_OBJ_END_DATE_NULL);
            }
        }

        // 校验存在
        ExpenseInfoDO expenseInfoBefore = validateInfoExists(updateReqVO.getId());
        // 更新
        ExpenseInfoDO updateObj = BeanUtils.toBean(updateReqVO, ExpenseInfoDO.class);

        // 获取所有费用信息的总金额
        BigDecimal totalMoney = updateObj.getTotalMoney().setScale(2, BigDecimal.ROUND_HALF_UP);
        // 使用流操作累加oaExpenseObjs中所有ExpenseInfoObjDO对象的金额，并与0进行比较
        BigDecimal reduce = oaExpenseObjs.stream()
                .map(ExpenseInfoObjDO::getMoney)
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .setScale(2, BigDecimal.ROUND_HALF_UP);
        // 比较总金额与累加金额是否一致，如果不一致则抛出异常
        if (totalMoney.compareTo(reduce) != 0) {
            throw exception(EXPENSE_INFO_TOTAL_MONEY_ERROR);
        }

        // 设置部门ID、岗位ID、用户ID和创建人信息
        updateObj.setDeptId(employee.getDeptId());
        updateObj.setPostId(employee.getPostId());
        updateObj.setStatus("0");

        infoMapper.updateById(updateObj);

        // 更新子表
        updateExpenseInfoObjList(updateReqVO.getId(), updateReqVO.getExpenseInfoObjs());

        // 附件
        fileApi.updateFileBiz(updateReqVO.getFileIdList(), expenseInfoBefore.getExpenseUuid());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteInfo(Long id) {
        // 校验存在
        validateInfoExists(id);
        // 删除
        infoMapper.deleteById(id);

        // 删除子表
        deleteExpenseInfoObjByExpenseId(id);
    }

    private ExpenseInfoDO validateInfoExists(Long id) {
        ExpenseInfoDO expenseInfoDO = infoMapper.selectById(id);
        if (expenseInfoDO == null) {
            throw exception(INFO_NOT_EXISTS);
        }
        return expenseInfoDO;
    }

    @Override
    public ExpenseInfoDO getInfo(Long id) {
        return infoMapper.selectById(id);
    }

    @Override
    public PageResult<ExpenseInfoRespVO> getInfoPage(ExpenseInfoPageReqVO pageReqVO) {
        // 数据权限
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        DeptDataPermissionRespDTO deptDataPermission = permissionApi.getDeptDataPermission(loginUserId);
        pageReqVO.setDeptDataPermission(deptDataPermission);
        pageReqVO.setUserId(loginUserId);

        pageReqVO.setPageNo((pageReqVO.getPageNo() - 1) * pageReqVO.getPageSize());

        long pageCount = 0;
        if (!Objects.equals(pageReqVO.getPageSize(), PageParam.PAGE_SIZE_NONE)) {
            pageCount = infoMapper.selectPageCount(pageReqVO);
        }

        List<ExpenseInfoRespVO> pageList = infoMapper.selectPageList(pageReqVO);

        for (ExpenseInfoRespVO expenseInfoRespVO : pageList) {
            String status = expenseInfoRespVO.getStatus();
            if (Objects.equals(status, "0")) {
                expenseInfoRespVO.setStatusDesc("已完成");
            } else if (Objects.equals(status, "1")) {
                expenseInfoRespVO.setStatusDesc("已作废");
            } else {
                expenseInfoRespVO.setStatusDesc("未知");
            }

            String infoSource = expenseInfoRespVO.getInfoSource();
            if (Objects.equals(infoSource, "0")) {
                expenseInfoRespVO.setInfoSourceDesc("流程添加");
            } else if (Objects.equals(infoSource, "1")) {
                expenseInfoRespVO.setInfoSourceDesc("手动添加");
            } else {
                expenseInfoRespVO.setInfoSourceDesc("未知来源");
            }
        }

        return new PageResult<>(pageList, pageCount);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createExpenseInfo(ExpenseInfoCreateReqDTO createReqDTO) {
        // 将创建请求DTO转换为ExpenseInfoDO对象
        ExpenseInfoDO expenseInfoDO = BeanUtils.toBean(createReqDTO, ExpenseInfoDO.class);
        // 插入ExpenseInfoDO对象到数据库
        infoMapper.insert(expenseInfoDO);

        // 获取与当前报销相关的报销信息对象列表
        List<ExpenseInfoObjCreateReqDTO> expenseInfoObjs = createReqDTO.getExpenseInfoObjs();
        // 如果报销信息对象列表不为空
        if (CollUtil.isNotEmpty(expenseInfoObjs)) {
            // 将报销信息对象列表转换为ExpenseInfoObjDO列表
            List<ExpenseInfoObjDO> expenseInfoObjDOList = BeanUtils.toBean(expenseInfoObjs, ExpenseInfoObjDO.class);
            // 创建报销信息对象列表与当前报销的关联
            createExpenseInfoObjList(expenseInfoDO.getId(), expenseInfoObjDOList);
        }

        // 返回新创建的报销ID
        return expenseInfoDO.getId();
    }

    // ==================== 子表（报销信息子） ====================

    @Override
    public List<ExpenseInfoObjDO> getExpenseInfoObjListByExpenseId(Long expenseId) {
        return expenseInfoObjMapper.selectListByExpenseId(expenseId);
    }

    private void createExpenseInfoObjList(Long expenseId, List<ExpenseInfoObjDO> list) {
        list.forEach(o -> o.setExpenseId(expenseId));
        expenseInfoObjMapper.insertBatch(list);
    }

    private void updateExpenseInfoObjList(Long expenseId, List<ExpenseInfoObjDO> list) {
        deleteExpenseInfoObjByExpenseId(expenseId);
		list.forEach(o -> o.setId(null).setUpdater(null).setUpdateTime(null).setCreateTime(null).setDeleted(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新
        createExpenseInfoObjList(expenseId, list);
    }

    private void deleteExpenseInfoObjByExpenseId(Long expenseId) {
        expenseInfoObjMapper.deleteByExpenseId(expenseId);
    }

}