package com.cpop.oam.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.cpop.api.tencent.wxWork.handler.WebHookSendHandler;
import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.core.base.entity.LoginUser;
import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.table.SysConfig;
import com.cpop.core.service.CoreService;
import com.cpop.core.service.RedisService;
import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.oam.business.bo.*;
import com.cpop.oam.business.entity.FinanceReimburse;
import com.cpop.oam.business.entity.FinanceReimburseStage;
import com.cpop.oam.business.mapper.FinanceReimburseMapper;
import com.cpop.oam.business.service.CommonService;
import com.cpop.oam.business.service.FinanceReimburseService;
import com.cpop.oam.business.service.FinanceReimburseStageService;
import com.cpop.oam.business.vo.FinanceReimburseAuditPageVo;
import com.cpop.oam.business.vo.FinanceReimbursePageVo;
import com.cpop.oam.business.vo.ReimbursePersonStatisticVo;
import com.cpop.oam.framework.constant.WebHookKeyConstant;
import com.cpop.oam.framework.enums.OamConfigEnum;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.cpop.core.base.table.table.SysUserTableDef.SYS_USER;
import static com.cpop.oam.business.entity.table.FinanceReimburseStageTableDef.FINANCE_REIMBURSE_STAGE;
import static com.cpop.oam.business.entity.table.FinanceReimburseTableDef.FINANCE_REIMBURSE;
import static com.cpop.oam.business.entity.table.FinanceReimburseTypeTableDef.FINANCE_REIMBURSE_TYPE;
import static com.cpop.oam.business.entity.table.StaffTableDef.STAFF;

/**
 * 报销记录表 服务层实现。
 *
 * @author DB
 * @since 2023-09-20
 */
@Service("financeReimburseService")
public class FinanceReimburseServiceImpl extends ServiceImpl<FinanceReimburseMapper, FinanceReimburse> implements FinanceReimburseService {

    /**
     * @Description: 财务报销模块-报销分页列表
     * @param bo 请求参数
     * @return: R<Page<FinanceReimbursePageVo>>
     * @Author: DB
     * @Date: 2023/5/30 15:44
     **/
    @Override
    public Page<FinanceReimbursePageVo> getFinanceReimbursePage(FinanceReimbursePageBo bo) {
        //获取当前登陆用户信息
        LoginUser loginUser = SecurityUtils.getInstance().getLoginUser();
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (bo.getStatus()!=null){
            if (bo.getStatus() == 0) {
                //状态
                queryWrapper.and(FINANCE_REIMBURSE.STATUS.in(0, -1));
            } else {
                //状态
                queryWrapper.and(FINANCE_REIMBURSE.STATUS.eq(bo.getStatus()));
            }
        }
        return this.mapper.paginateAs(pageDomain.getPageNum(), pageDomain.getPageSize(),
                queryWrapper.select(FINANCE_REIMBURSE.ALL_COLUMNS)
                        .select(STAFF.NAME.as(FinanceReimbursePageVo::getStaffName))
                        .select(FINANCE_REIMBURSE_TYPE.TYPE_NAME.as(FinanceReimbursePageVo::getTypeName))
                        .from(FINANCE_REIMBURSE)
                        .leftJoin(FINANCE_REIMBURSE_TYPE).on(FINANCE_REIMBURSE_TYPE.ID.eq(FINANCE_REIMBURSE.TYPE_ID))
                        .leftJoin(STAFF).on(STAFF.ID.eq(FINANCE_REIMBURSE.STAFF_ID))
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(STAFF.USER_ID))
                        //用户id
                        .where(SYS_USER.ID.eq(loginUser.getUserId()))
                        //类型
                        .and(FINANCE_REIMBURSE.TYPE_ID.eq(bo.getTypeId()))
                        .orderBy(FINANCE_REIMBURSE.UPDATE_TIME.desc()),
                FinanceReimbursePageVo.class);
    }

    /**
     * @Description: 财务报销模块-报销审核分页列表
     * @param bo 请求参数
     * @return: R<Page<FinanceReimbursePageVo>>
     * @Author: DB
     * @Date: 2023/5/30 15:44
     **/
    @Override
    public Page<FinanceReimburseAuditPageVo> getReimburseAuditPage(FinanceReimburseAuditPageBo bo) {
        PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
        return this.mapper.paginateAs(pageDomain.getPageNum(), pageDomain.getPageSize(),
                QueryWrapper.create().select(FINANCE_REIMBURSE.ALL_COLUMNS)
                        .select(STAFF.NAME.as(FinanceReimbursePageVo::getStaffName))
                        .select(FINANCE_REIMBURSE_TYPE.TYPE_NAME.as(FinanceReimbursePageVo::getTypeName))
                        .from(FINANCE_REIMBURSE)
                        .leftJoin(FINANCE_REIMBURSE_TYPE).on(FINANCE_REIMBURSE_TYPE.ID.eq(FINANCE_REIMBURSE.TYPE_ID))
                        .leftJoin(STAFF).on(STAFF.ID.eq(FINANCE_REIMBURSE.STAFF_ID))
                        .leftJoin(SYS_USER).on(SYS_USER.ID.eq(STAFF.USER_ID))
                        //类型
                        .and(FINANCE_REIMBURSE.TYPE_ID.eq(bo.getTypeId()))
                        //理由
                        .and(FINANCE_REIMBURSE.REMARKS.like(bo.getQuery()).or(STAFF.NAME.like(bo.getQuery())))
                        .and(FINANCE_REIMBURSE.STATUS.eq(bo.getStatus()))
                        .orderBy(FINANCE_REIMBURSE.CREATE_TIME.asc()),
                FinanceReimburseAuditPageVo.class);
    }

    /**
     * 修改报销状态
     * @param bo 请求参数
     * @author DB
     * @since : 2023/5/10 16:01
     **/
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateReimburseStatus(ReimburseStatusBo bo) {
        List<FinanceReimburse> list = this.list(QueryWrapper.create().where(FINANCE_REIMBURSE.ID.in(bo.getIds())));
        List<String> finishReimburseIds = list.stream().map(FinanceReimburse::getId).collect(Collectors.toList());
        LocalDateTime now = LocalDateTime.now();
        List<FinanceReimburseStage> financeReimburseStages = new ArrayList<>();
        //审批批量转下款
        if (list.get(0).getStatus() == 0) {
            Map<String, FinanceReimburse> idToPrice = list.stream().collect(Collectors.toMap(FinanceReimburse::getId, item->item));
            list.forEach(item -> {
                FinanceReimburseStage entity = new FinanceReimburseStage();
                entity.setFinanceReimburseId(item.getId()).setPaymentTime(now).setStageAmount(item.getPrice());
                financeReimburseStages.add(entity);
                //修改状态
                item.setStatus(2);
            });
        } else {
            //获取已有的报销阶段
            Map<String, List<FinanceReimburseStage>> stageMap = SpringUtils.getBean(FinanceReimburseStageService.class).queryChain()
                    .where(FINANCE_REIMBURSE_STAGE.FINANCE_REIMBURSE_ID.in(finishReimburseIds)).list()
                    .stream().collect(Collectors.groupingBy(FinanceReimburseStage::getFinanceReimburseId));
            list.forEach(item -> {
                FinanceReimburseStage entity = new FinanceReimburseStage();
                if (stageMap.get(item.getId()) != null) {
                    //减去已下发的
                    entity.setStageAmount(item.getPrice().subtract(stageMap.get(item.getId()).stream().map(FinanceReimburseStage::getStageAmount).reduce(BigDecimal.ZERO, BigDecimal::add)));
                } else {
                    entity.setFinanceReimburseId(item.getId()).setPaymentTime(now).setStageAmount(item.getPrice());
                }
                financeReimburseStages.add(entity);
                item.setStatus(2);
            });
        }
        //批量修改
        SpringUtils.getBean(FinanceReimburseStageService.class).saveBatch(financeReimburseStages);
        this.updateBatch(list);
    }

    /**
     * 新增报销申请
     * @author DB
     * @since  2023/09/21 15:55
     * @param bo 请求参数
     */
    @Override
    public void insertReimburseApplication(ReimburseApplicationBo bo) {
        //获取申请员工信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        FinanceReimburse financeReimburse = BeanUtils.mapToClass(bo, FinanceReimburse.class);
        financeReimburse.setStaffId(loginUserInfo.getString("id"))
                .setStatus(0);
        this.save(financeReimburse);
        //提醒审核员工
        try {
            List<String> phoneList = new ArrayList<>();
            phoneList.add(loginUserInfo.getString("phoneNumber"));
            // 获取审核管理员手机号
            RedisService redisService = SpringUtils.getBean(RedisService.class);
            CommonService commonService = SpringUtils.getBean(CommonService.class);
            String auditStaffPhone = redisService.getCacheObject(commonService.getCacheKey(OamConfigEnum.CANCEL_AFTER_VERIFICATION_STAFF_PHONE.getKey()));
            if (StringUtils.isBlank(auditStaffPhone)) {
                SysConfig sysConfig = SpringUtils.getBean(CoreService.class).selectConfigByKey(commonService.getCacheKey(OamConfigEnum.AUDIT_STAFF_PHONE.getKey()));
                auditStaffPhone = sysConfig.getConfigValue();
                redisService.setCacheObject(commonService.getCacheKey(OamConfigEnum.AUDIT_STAFF_PHONE.getKey()), auditStaffPhone);
            }
            phoneList.add(auditStaffPhone);
            SpringUtils.getBean(WebHookSendHandler.class).webHookSendText(WebHookKeyConstant.FINANCE_BOT, phoneList,
                    "您有一笔新的报销需要处理:\n",
                    false);
        } catch (IOException e) {
            throw new ServiceException("发送消息通知失败!");
        }
    }

    /**
     * 修改报销申请
     * @author DB
     * @since 2023/12/21
     * @param bo 请求
     */
    @Override
    public void updateReimburseApplication(ReimburseApplicationBo bo) {
        //获取申请员工信息
        FinanceReimburse financeReimburse = BeanUtils.mapToClass(bo, FinanceReimburse.class);
        financeReimburse.setStatus(0);
        this.updateById(financeReimburse);
    }

    /**
     * @Description: 财务报销模块-个人报销统计
     * @return: R<Page<FinanceReimbursePageVo>>
     * @Author: DB
     * @Date: 2023/5/30 15:44
     **/
    @Override
    public ReimbursePersonStatisticVo getPersonStatistic() {
        //读取员工信息
        JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
        List<FinanceReimburse> list = this.list(QueryWrapper.create()
                .where(FINANCE_REIMBURSE.STAFF_ID.eq(loginUserInfo.getString("id")))
                .and(FINANCE_REIMBURSE.STATUS.ne(-1)));
        //统计
        ReimbursePersonStatisticVo vo = new ReimbursePersonStatisticVo();
        vo.setUnPay(list.stream().filter(s -> s.getStatus() != 2).map(FinanceReimburse::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        vo.setHavePay(list.stream().filter(s -> s.getStatus() == 2).map(FinanceReimburse::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add));
        return vo;
    }

    /**
     * @descriptions 报销下款
     * @author DB
     * @date 2023/09/08 15:20
     * @param bo 请求参数
     * @return: com.jambox.core.base.R<java.lang.Void>
     */
    @Override
    public void reimbursePay(ReimbursePayBo bo) {
        //报销记录
        FinanceReimburse financeReimburse = this.getById(bo.getId());
        BigDecimal price = financeReimburse.getPrice();
        FinanceReimburseStageService stageService = SpringUtils.getBean(FinanceReimburseStageService.class);
        //获取已有的报销记录
        List<FinanceReimburseStage> financeReimburseStageList = stageService.list(QueryWrapper.create().where(FINANCE_REIMBURSE_STAGE.FINANCE_REIMBURSE_ID.eq(bo.getId())));
        //有下款记录
        if (!financeReimburseStageList.isEmpty()) {
            //已有金额
            BigDecimal amountAvailable = financeReimburseStageList.stream().map(FinanceReimburseStage::getStageAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            if (price.compareTo(amountAvailable.add(bo.getStageAmount())) < 0) {
                throw new ServiceException("当前下发金额总和已超过申请金额！");
            } else if (price.compareTo(amountAvailable.add(bo.getStageAmount())) == 0) {
                financeReimburse.setStatus(2);
            } else {
                //下款中
                financeReimburse.setStatus(1);
            }
        } else {
            //单次下发金额
            if (price.compareTo(bo.getStageAmount()) < 0) {
                throw new ServiceException("当前下发金额已超过申请金额！");
            } else if (price.compareTo(bo.getStageAmount()) == 0) {
                financeReimburse.setStatus(2);
            } else {
                //下款中
                financeReimburse.setStatus(1);
            }
        }
        FinanceReimburseStage entity = new FinanceReimburseStage();
        entity.setFinanceReimburseId(bo.getId()).setPaymentTime(bo.getPaymentTime()).setStageAmount(bo.getStageAmount());
        stageService.save(entity);
        //更新报销记录
        this.updateById(financeReimburse);
    }
}
