package com.fjhx.account.service.account.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.account.entity.account.bo.AccountRunningWaterBo;
import com.fjhx.account.entity.account.dto.AccountRunningWaterDto;
import com.fjhx.account.entity.account.dto.AccountRunningWaterSelectDto;
import com.fjhx.account.entity.account.po.AccountManagement;
import com.fjhx.account.entity.account.po.AccountRemainder;
import com.fjhx.account.entity.account.po.AccountRunningWater;
import com.fjhx.account.entity.account.vo.AccountRunningWaterVo;
import com.fjhx.account.entity.tax.po.TaxRefundDetails;
import com.fjhx.account.mapper.account.AccountRunningWaterMapper;
import com.fjhx.account.service.account.AccountManagementService;
import com.fjhx.account.service.account.AccountRemainderService;
import com.fjhx.account.service.account.AccountRunningWaterService;
import com.fjhx.account.service.tax.TaxRefundDetailsService;
import com.fjhx.common.constant.SourceConstant;
import com.fjhx.common.entity.corporation.po.Corporation;
import com.fjhx.common.service.corporation.CorporationService;
import com.fjhx.common.utils.Assert;
import com.fjhx.common.utils.ExchangeRateUtil;
import com.fjhx.item.util.excel.util.ExcelUtil;
//import com.fjhx.socket.service.WebSocketServer;
import com.fjhx.tenant.entity.dict.dto.DictTenantDataSelectDto;
import com.fjhx.tenant.entity.dict.vo.DictTenantDataVo;
import com.fjhx.tenant.service.dict.DictTenantDataService;
import com.ruoyi.common.core.domain.BaseSelectDto;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * <p>
 * 账户资金流水表 服务实现类
 * </p>
 *
 * @author
 * @since 2023-04-06
 */
@Service
public class AccountRunningWaterServiceImpl extends ServiceImpl<AccountRunningWaterMapper, AccountRunningWater> implements AccountRunningWaterService {

    @Autowired
    private AccountRemainderService accountRemainderService;

    @Autowired
    private DictTenantDataService dictTenantDataService;

    @Autowired
    private CorporationService corporationService;

    @Autowired
    private AccountManagementService accountManagementService;
    @Autowired
    private TaxRefundDetailsService taxRefundDetailsService;

    @Override
    public Page<AccountRunningWaterVo> getPage(AccountRunningWaterSelectDto dto) {
        QueryWrapper<Object> wrapper = Wrappers.query();
        // 资金账户
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getAccountManagementId()), "arw.account_management_id", dto.getAccountManagementId());
        // 交易类型
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getStatus()), "arw.status", dto.getStatus());
        // 是否合同到账
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getReceived()), "arw.received", dto.getReceived());
        // 币种
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getCurrency()), "arw.currency", dto.getCurrency());
        //是否认领
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getIsClaim()), "arw.is_claim", dto.getIsClaim());
        //交易开始金额
        wrapper.ge(ObjectUtil.isNotEmpty(dto.getBeginAmount()), "arw.amount", dto.getBeginAmount());
        //交易结束金额
        wrapper.le(ObjectUtil.isNotEmpty(dto.getEndAmount()), "arw.amount", dto.getEndAmount());
        //交易开始时间
        wrapper.ge(ObjectUtil.isNotEmpty(dto.getStartTime()), "arw.transaction_time", dto.getStartTime());
        //交易结束时间
        wrapper.le(ObjectUtil.isNotEmpty(dto.getStopTime()), "arw.transaction_time", dto.getStopTime());
        //对方账户名称
        wrapper.like(ObjectUtil.isNotEmpty(dto.getName()), "arw.name", dto.getName());
        //摘要
        wrapper.like(ObjectUtil.isNotEmpty(dto.getRemarks()), "arw.remarks", dto.getRemarks());
        if (StringUtils.isNotEmpty(dto.getKeyword())) {
            wrapper.and(wrapper1 -> wrapper1.like("arw.remarks", dto.getKeyword()).or().like("arw.name", dto.getKeyword()));
        }
        if (ObjectUtil.isNotEmpty(dto.getDataType()) && dto.getDataType() == 1) {// 到账认领数据
            wrapper.eq("arw.received", 10);
            //到账认领状态使用自定义排序 0未认领 2部分认领 1已认领
            wrapper.orderByAsc("field(arw.is_claim,0,2,1)");
            //到账时间倒序
            wrapper.orderByDesc("arw.transaction_time");
        }
        // todo ERP-》资金管理-》资金流水分页 要求按交易时间排序，我也不懂上面if排序是在哪调用的，先在else里加吧
        else {
            wrapper.orderByDesc("arw.transaction_time", "arw.id");
        }

        Page<AccountRunningWaterVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);
        List<AccountRunningWaterVo> records = page.getRecords();
        //赋值归属公司名称
        corporationService.attributeAssign(records, AccountRunningWaterVo::getCorporationId, (item, corporation) -> {
            item.setCorporationName(corporation.getName());
        });
        return page;
    }

    /**
     * 账户流水分页
     */
    @Override
    public Page<AccountRunningWaterVo> getPage1(AccountRunningWaterSelectDto dto) {
        Assert.notEmpty(dto.getAccountManagementId(), "资金账户不能为空");
        Assert.notEmpty(dto.getCurrency(), "币种不能为空");
        // 获取账户余额
        AccountRemainder accountRemainder = accountRemainderService.getOne(q -> q
                .eq(AccountRemainder::getAccountManagementId, dto.getAccountManagementId())
                .eq(AccountRemainder::getCurrency, dto.getCurrency()));

        BigDecimal remainder;
        if (accountRemainder == null) {
            remainder = BigDecimal.ZERO;
        } else {
            remainder = accountRemainder.getRemainder();
        }

        QueryWrapper<Object> wrapper = Wrappers.query();
        // 资金账户
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getAccountManagementId()), "arw.account_management_id", dto.getAccountManagementId());
        // 币种
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getCurrency()), "arw.currency", dto.getCurrency());
        wrapper.orderByDesc("arw.transaction_time");

        //获取当前分页之前的数据先处理余额
        Integer size = (dto.getPageNum() - 1) * dto.getPageSize();
        List<AccountRunningWater> amounts = baseMapper.getAccountRunningWaterAmount(wrapper, size);
        //收入合
        BigDecimal incomeSum = amounts.stream().filter(item -> "10".equals(item.getStatus())).map(AccountRunningWater::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //支出合
        BigDecimal expenditureSum = amounts.stream().filter(item -> "20".equals(item.getStatus())).map(AccountRunningWater::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        remainder = remainder.subtract(incomeSum).add(expenditureSum);

        //获取分页数据
        Page<AccountRunningWaterVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);
        List<AccountRunningWaterVo> records = page.getRecords();
        for (AccountRunningWaterVo record : records) {
            record.setRemainder(remainder);
            //根据当前余额 反向计算 每条记录操作后的余额 收入减 支出加
            remainder = "10".equals(record.getStatus()) ? remainder.subtract(record.getAmount()) : remainder.add(record.getAmount());
        }
        //赋值归属公司名称
        corporationService.attributeAssign(records, AccountRunningWaterVo::getCorporationId, (item, corporation) -> {
            item.setCorporationName(corporation.getName());
        });
        return page;
    }

    @Override
    public AccountRunningWaterVo detail(Long id) {
        AccountRunningWaterVo result = baseMapper.getDetail(id);
        //赋值归属公司名称
        Corporation corporation = corporationService.getById(result.getCorporationId());
        if (ObjectUtil.isNotEmpty(corporation)) {
            result.setCorporationName(corporation.getName());
        }
        return result;
    }

    /**
     * 账户资金流水表新增
     */
    @Override
    @DSTransactional
    public void add(AccountRunningWaterDto dto) {
        if (StringUtils.isEmpty(dto.getCurrency())) {
            throw new ServiceException("币种不能为空");
        }
        dto.setRate(ExchangeRateUtil.getCnyToCodeRate(dto.getCurrency()));
        // 修改账户余额表的余额
        AccountRemainder accountRemainder = accountRemainderService.getOne(q -> q
                .eq(AccountRemainder::getAccountManagementId, dto.getAccountManagementId())
                .eq(AccountRemainder::getCurrency, dto.getCurrency())
        );

        if (ObjectUtil.isEmpty(accountRemainder)) {
            throw new ServiceException("该账户不存在此币种，请前往资金账户添加");
        }
        BigDecimal amount = dto.getAmount();
        String status = dto.getStatus();
        BigDecimal remainder = accountRemainder.getRemainder();

        accountRemainder.setStatus(status);
        accountRemainder.setChangeRemainder(amount);

        if (status.equals("10")) {
            accountRemainder.setRemainder(remainder.add(amount));
        } else if (status.equals("20")) {
            if (remainder.compareTo(amount) < 0) {
                throw new ServiceException("账户余额不足");
            }
            accountRemainder.setRemainder(remainder.subtract(amount));
        }
        accountRemainderService.updateById(accountRemainder);

        this.save(dto);

        // 发送消息推送
        sendSocket(dto);
    }


    private void sendSocket(AccountRunningWaterDto dto) {

        if (ObjectUtil.notEqual(dto.getStatus(), "10") || ObjectUtil.notEqual(dto.getReceived(), "10")) {
            return;
        }

        // 账户id
        Long accountManagementId = dto.getAccountManagementId();
        AccountManagement accountManagement = accountManagementService.getById(accountManagementId);
        if (accountManagement == null) {
            throw new ServiceException("没有找到公司账户");
        }

        // 归属公司
        Long corporationId = accountManagement.getCorporationId();
        if (corporationId == null) {
            throw new ServiceException("归属公司id为空");
        }
        Corporation corporation = corporationService.getById(corporationId);
        if (corporation == null) {
            throw new ServiceException("没有找到归属公司");
        }

        String msg = "{} - {}账户有一笔新的到账（金额：{} {}），请相应业务员进行认领。";

//        WebSocketServer.sendInfoGroup(0, StrUtil.format(
//                msg, corporation.getName(), accountManagement.getName(), dto.getCurrency(), dto.getAmount()));

    }


    /**
     * 账户资金流水表编辑
     */
    @Override
    @DSTransactional
    public void edit(AccountRunningWater accountRunningWaterDto) {
        //先回滚历史数据
        //获取历史的流水信息信息
        AccountRunningWater oldAccountRunningWater = getById(accountRunningWaterDto.getId());
        //获取余额
        AccountRemainder accountRemainder = accountRemainderService.getOne(q -> q
                .eq(AccountRemainder::getAccountManagementId, oldAccountRunningWater.getAccountManagementId())
                .eq(AccountRemainder::getCurrency, oldAccountRunningWater.getCurrency())
        );
        if (ObjectUtil.isEmpty(accountRemainder)) {
            throw new ServiceException("历史数据 该账户不存在此币种，请前往资金账户添加");
        }
        boolean update = false;
        if ("10".equals(oldAccountRunningWater.getStatus())) {
            BigDecimal subtract = accountRemainder.getRemainder().subtract(oldAccountRunningWater.getAmount());
            if (subtract.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("修改前数据 账户余额不足");
            }
            update = accountRemainderService.update(q -> q
                    .setSql("remainder = remainder - " + oldAccountRunningWater.getAmount())
                    .setSql("change_remainder = " + oldAccountRunningWater.getAmount())
                    .eq(AccountRemainder::getId, accountRemainder.getId())
                    .apply("remainder - {0} >= 0", oldAccountRunningWater.getAmount())
            );
        } else {
            update = accountRemainderService.update(q -> q
                    .setSql("remainder = remainder + " + oldAccountRunningWater.getAmount())
                    .setSql("change_remainder = " + oldAccountRunningWater.getAmount())
                    .eq(AccountRemainder::getId, accountRemainder.getId())
            );
        }
        Assert.eqTrue(update, "余额回滚操作失败");
        //再操作修改后数据
        //获取余额
        AccountRemainder accountRemainder1 = accountRemainderService.getOne(q -> q
                .eq(AccountRemainder::getAccountManagementId, accountRunningWaterDto.getAccountManagementId())
                .eq(AccountRemainder::getCurrency, accountRunningWaterDto.getCurrency())
        );
        if (ObjectUtil.isEmpty(accountRemainder)) {
            throw new ServiceException("修改后数据 该账户不存在此币种，请前往资金账户添加");
        }
        boolean update1 = false;
        if ("10".equals(accountRunningWaterDto.getStatus())) {
            update1 = accountRemainderService.update(q -> q
                    .setSql("remainder = remainder + " + accountRunningWaterDto.getAmount())
                    .setSql("change_remainder = " + accountRunningWaterDto.getAmount())
                    .eq(AccountRemainder::getId, accountRemainder1.getId())
            );
        } else {
            BigDecimal subtract = accountRemainder1.getRemainder().subtract(accountRunningWaterDto.getAmount());
            if (subtract.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("修改后数据 账户余额不足");
            }
            update1 = accountRemainderService.update(q -> q
                    .setSql("remainder = remainder - " + accountRunningWaterDto.getAmount())
                    .setSql("change_remainder = " + accountRunningWaterDto.getAmount())
                    .eq(AccountRemainder::getId, accountRemainder1.getId())
                    .apply("remainder - {0} >= 0", accountRunningWaterDto.getAmount())
            );
        }
        Assert.eqTrue(update1, "余额修改操作失败");

        this.updateById(accountRunningWaterDto);
    }

    @DSTransactional
    @Override
    public void delete(Long id) {
        AccountRunningWater accountRunningWater = getById(id);
        Assert.notEmpty(accountRunningWater, "获取不到该流水信息");


        // 修改账户余额表的余额
        AccountRemainder accountRemainder = accountRemainderService.getOne(q -> q
                .eq(AccountRemainder::getAccountManagementId, accountRunningWater.getAccountManagementId())
                .eq(AccountRemainder::getCurrency, accountRunningWater.getCurrency())
        );
        if (ObjectUtil.isEmpty(accountRemainder)) {
            throw new ServiceException("该账户不存在此币种，请前往资金账户添加");
        }
        BigDecimal amount = accountRunningWater.getAmount();
        String status = accountRunningWater.getStatus();
        BigDecimal remainder = accountRemainder.getRemainder();

        accountRemainder.setStatus(status);
        accountRemainder.setChangeRemainder(amount);

        boolean update = false;
        if ("10".equals(accountRunningWater.getStatus())) {
            BigDecimal subtract = remainder.subtract(amount);
            if (subtract.compareTo(BigDecimal.ZERO) < 0) {
                throw new ServiceException("账户余额不足");
            }

            update = accountRemainderService.update(q -> q
                    .setSql("remainder = remainder - " + accountRunningWater.getAmount())
                    .setSql("change_remainder = " + accountRunningWater.getAmount())
                    .eq(AccountRemainder::getId, accountRemainder.getId())
                    .apply("remainder - {0} >= 0", accountRunningWater.getAmount())
            );
        } else {
            update = accountRemainderService.update(q -> q
                    .setSql("remainder = remainder + " + accountRunningWater.getAmount())
                    .setSql("change_remainder = " + accountRunningWater.getAmount())
                    .eq(AccountRemainder::getId, accountRemainder.getId())
            );
        }
        Assert.eqTrue(update, "余额操作失败操作失败");

        this.removeById(id);
    }

    /**
     * 到账任领统计
     */
    @Override
    public List<AccountRunningWater> claimStatistics(BaseSelectDto dto) {
        QueryWrapper<AccountRunningWater> query = Wrappers.query();
        query.groupBy("is_claim");
        query.select("is_claim,count(*) count");
        //到账认领统计
        return baseMapper.selectList(query);

    }

    @Override
    public void exportExcel(AccountRunningWaterSelectDto dto, HttpServletResponse httpServletResponse) {
        QueryWrapper<Object> wrapper = Wrappers.query();
        //资金账户
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getAccountManagementId()), "arw.account_management_id", dto.getAccountManagementId());
        //交易类型
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getStatus()), "arw.status", dto.getStatus());
        //是否合同到账
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getReceived()), "arw.received", dto.getReceived());
        //币种
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getCurrency()), "arw.currency", dto.getCurrency());
        //是否认领
        wrapper.eq(ObjectUtil.isNotEmpty(dto.getIsClaim()), "arw.is_claim", dto.getIsClaim());
        //交易开始金额
        wrapper.ge(ObjectUtil.isNotEmpty(dto.getBeginAmount()), "arw.amount", dto.getBeginAmount());
        //交易结束金额
        wrapper.ge(ObjectUtil.isNotEmpty(dto.getEndAmount()), "arw.amount", dto.getEndAmount());
        //交易开始时间
        wrapper.ge(ObjectUtil.isNotEmpty(dto.getBeginTime()), "arw.create_time", dto.getBeginTime());
        //交易结束金额
        wrapper.ge(ObjectUtil.isNotEmpty(dto.getEndTime()), "arw.create_time", dto.getEndTime());
        //对方账户名称
        wrapper.ge(ObjectUtil.isNotEmpty(dto.getName()), "arw.name", dto.getName());
        //摘要
        wrapper.ge(ObjectUtil.isNotEmpty(dto.getRemarks()), "arw.name", dto.getRemarks());
        if (StringUtils.isNotEmpty(dto.getKeyword())) {
            wrapper.and(wrapper1 -> wrapper1.like("arw.remarks", dto.getKeyword()).or().like("arw.name", dto.getKeyword()));
        }
        if (ObjectUtil.isNotEmpty(dto.getDataType()) && dto.getDataType() == 1) {//到账认领数据
            wrapper.eq("arw.received", 10);
            wrapper.orderByAsc("arw.is_claim", "arw.create_time");
        }
        List<AccountRunningWaterVo> records = this.baseMapper.getList(wrapper);

        //赋值字典数据
        DynamicDataSourceContextHolder.push(SourceConstant.BASE);
        List<DictTenantDataVo> dictTenantDataVos = getDict("account_currency");
        if (dictTenantDataVos.size() == 0) {
            throw new ServiceException("数据有误：没有配置币种字典，请先配置");
        }
        Map<String, List<DictTenantDataVo>> dictTenantDataVoMap = dictTenantDataVos.stream()
                .collect(Collectors.groupingBy(DictTenantDataVo::getDictKey));
        DynamicDataSourceContextHolder.poll();
        for (AccountRunningWaterVo accountRunningWaterVo : records) {
            List<DictTenantDataVo> dictTenantDataVoList = dictTenantDataVoMap.get(accountRunningWaterVo.getCurrency());
            if (ObjectUtil.isNotEmpty(dictTenantDataVoList)) {
                accountRunningWaterVo.setCurrency(dictTenantDataVoList.get(0).getDictValue());
            }
        }

        //导出Excel
        List<AccountRunningWaterBo> accountRunningWaterBos = BeanUtil.copyToList(records, AccountRunningWaterBo.class);
        for (AccountRunningWaterBo accountRunningWaterBo : accountRunningWaterBos) {
            //重新赋值收支类型
            accountRunningWaterBo.setStatus("10".equals(accountRunningWaterBo.getStatus()) ? "收入" : "支出");
            //重新赋值是否合同到账
            accountRunningWaterBo.setReceived("10".equals(accountRunningWaterBo.getReceived()) ? "是" : "否");
        }
        ExcelUtil.export(httpServletResponse, accountRunningWaterBos, AccountRunningWaterBo.class);
    }

    // 根据字典编码获取字典的数据
    private List<DictTenantDataVo> getDict(String code) {
        DictTenantDataSelectDto dto = new DictTenantDataSelectDto();
        dto.setDictCode(code);
        List<DictTenantDataVo> dictTenantDataServiceList = dictTenantDataService.getList(dto);
        return dictTenantDataServiceList;
    }

    /**
     * 退税添加
     */
    @DSTransactional
    @Override
    public void taxRefund(AccountRunningWaterDto dto) {
        dto.setStatus("10");
        dto.setType("40");//退税
        add(dto);
        List<TaxRefundDetails> taxRefundDetailsList = dto.getTaxRefundDetailsList();
        taxRefundDetailsList.forEach(item -> item.setAccountRunningWaterId(dto.getId()));
        taxRefundDetailsService.saveBatch(taxRefundDetailsList);
    }

}
