package com.yc.ship.module.trade.service.bill;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yc.ship.framework.common.exception.ServiceException;
import com.yc.ship.framework.common.lang.Asserts;
import com.yc.ship.framework.common.pojo.PageResult;
import com.yc.ship.framework.common.util.date.DateUtils;
import com.yc.ship.framework.common.util.object.BeanUtils;
import com.yc.ship.framework.common.util.object.ObjectUtils;
import com.yc.ship.framework.security.core.LoginUser;
import com.yc.ship.framework.tenant.core.aop.TenantIgnore;
import com.yc.ship.framework.tenant.core.context.TenantContextHolder;
import com.yc.ship.framework.tenant.core.util.TenantUtils;
import com.yc.ship.module.ota.api.dto.*;
import com.yc.ship.module.ota.controller.admin.distributor.vo.DistributorRespVO;
import com.yc.ship.module.ota.dal.mysql.distributor.DistributorMapper;
import com.yc.ship.module.ota.enums.DistributorTradeTypeEnum;
import com.yc.ship.module.product.api.dto.PlatProductRespDTO;
import com.yc.ship.module.product.api.dto.ProductGroupRespDTO;
import com.yc.ship.module.product.api.dto.ProductSpuRespDTO;
import com.yc.ship.module.system.api.user.AdminUserApi;
import com.yc.ship.module.system.api.user.dto.AdminUserRespDTO;
import com.yc.ship.module.trade.controller.admin.bill.vo.BillPageReqVO;
import com.yc.ship.module.trade.controller.admin.bill.vo.BillRespVO;
import com.yc.ship.module.trade.controller.admin.bill.vo.BillSaveReqVO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.TradeDetailRespVO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.TradeOrderPageReqVO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.TradeOrderRespVO;
import com.yc.ship.module.trade.dal.dataobject.bill.BillDO;
import com.yc.ship.module.trade.dal.dataobject.bill.BillStoreDO;
import com.yc.ship.module.trade.dal.dataobject.billorder.BillOrderDO;
import com.yc.ship.module.trade.dal.mysql.bill.BillMapper;
import com.yc.ship.module.trade.dal.mysql.bill.BillStoreMapper;
import com.yc.ship.module.trade.dal.mysql.billorder.BillOrderMapper;
import com.yc.ship.module.trade.utils.AgencyAuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
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.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception0;
import static com.yc.ship.framework.security.core.util.SecurityFrameworkUtils.getLoginUser;
import static com.yc.ship.module.ota.enums.ErrorCodeConstants.BULL_NOT_FAIL;
import static com.yc.ship.module.ota.enums.ErrorCodeConstants.ORDER_PAY_AUTO_REFUND;


/**
 * 旅行社账单 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
@Slf4j
public class BillServiceImpl implements BillService {
    /**
     * 扎帐存锁
     */
    String BILL_LOCK = "bill:lock:%d";
    String BILL_STORE_LOCK = "bill:store:lock:%d";
    String BILL_UPDATE_LOCK = "bill:update:lock:%d";
    @Resource
    private BillMapper billMapper;
    @Resource
    private BillStoreMapper billStoreMapper;

    @Resource
    private BillOrderMapper billOrderMapper;
    @Resource
    private DistributorMapper distributorMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private AgencyAuthUtils agencyAuthUtils;
    @Override
    @Transactional
    public Long createBill(BillSaveReqVO vo) {
        //旅行社账单
        BillDO billDO = new BillDO();
        //门店账单
        List<BillStoreDO> billStoreDO = new ArrayList<>();
        String detailKey = String.format(BILL_LOCK, vo.getAgencyId());
        RLock lock = redissonClient.getLock(detailKey);
        try {
            if (lock.tryLock(30, TimeUnit.SECONDS)) {
                Long id = IdWorker.getId(billDO);
                //查询分销商信息
                DistributorRespVO distributor = distributorMapper.getDistributorById(vo.getAgencyId());
                Asserts.isTrue(distributor != null ,"旅行社不存在账单生成失败！");
                //通过时间查询所有订单
                List<TradeOrderRespVO> result = billMapper.getOrderList(vo);
                if(!result.isEmpty()){
                    //判断订单是否满足条件
                    Iterator<TradeOrderRespVO> it = result.iterator();
                    while (it.hasNext()) {
                        TradeOrderRespVO order = it.next();
                        TradeOrderRespVO tradeOrderRespVO = billMapper.getOrderById(order.getId());
                        order.setPayTypeAmount(tradeOrderRespVO.getPayTypeAmount());

                        List<TradeDetailRespVO> tradeDetailList = billMapper.getOrderIdDetail(order.getId());
                        for(TradeDetailRespVO tradeDetail : tradeDetailList){
                            //判断状态 1待使用，2取票，3已核销，4退票中，5已退款,6逾期核销，7已过期 8部分核销 9部分退
                            if(tradeDetail.getVoucherStatus() != 3 && tradeDetail.getVoucherStatus() != 5 && tradeDetail.getVoucherStatus() != 6
                                    && tradeDetail.getVoucherStatus() != 7 && tradeDetail.getVoucherStatus() != 8
                                    && !(tradeDetail.getVoucherStatus()==9 && ObjectUtil.equal(tradeDetail.getCheckStatus(),1))){
                                it.remove();
                                break;
                            }
                        }
                    }
                    // 数据统计
                    statisticalAutoData(result,billDO);
                    //判断是OTA
                    if(distributor.getType() == 2){
                        // 门店账单数据
                        statisticalStoreAutoData(result,billStoreDO);
                    }
                    //保存旅行社账单
                    billDO.setId(id);
                    billDO.setProhibitType(vo.getType());
                    billDO.setAgencyId(distributor.getId());
                    billDO.setAgencyName(distributor.getName());
                    billDO.setPayDeadline(vo.getPayDeadline());
                    billDO.setOtaTenantId(distributor.getAccountTenantId());
                    billDO.setOrderStratTime(vo.getOrderStratTime());
                    billDO.setOrderEndTime(vo.getOrderEndTime());
                    billDO.setTenantId(distributor.getTenantId());
                    billMapper.insert(billDO);

                    //判断是否OTA
                    if(distributor.getType() == 2){
                        //保存门店账单
                        DistributorRespVO finalDistributor = distributor;
                        billStoreDO.forEach(billStore -> {
                            billStore.setBillId(id);
                            billStore.setAgencyId(finalDistributor.getId());
                            billStore.setAgencyName(finalDistributor.getName());
                            billStore.setPayDeadline(vo.getPayDeadline());
                            billStore.setOtaTenantId(finalDistributor.getAccountTenantId());
                            billStore.setTenantId(finalDistributor.getTenantId());
                            billStoreMapper.insert(billStore);
                        });
                    }

                    // 遍历订单
                    Iterator<TradeOrderRespVO> storeIterator = result.iterator();
                    while (storeIterator.hasNext()) {
                        BillOrderDO bill = new BillOrderDO();
                        TradeOrderRespVO order = storeIterator.next();
                        bill.setId(IdWorker.getId());
                        bill.setExternalOrderId(order.getExternalOrderId());
                        bill.setBillId(id);
                        bill.setBillStoreId(order.getBillStoreId());
                        bill.setOrderId(order.getId());
                        bill.setTenantId(distributor.getTenantId());
                        billOrderMapper.insert(bill);
                        //修改订单已使用
                        billMapper.updateOrderIsBill(order.getId(),2);
                    }
                }
            } else {
                throw exception(BULL_NOT_FAIL);
            }
        } catch (Exception ex) {
            throw exception(BULL_NOT_FAIL);
        }finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
        return billDO.getId();
    }

    @Override
    @Transactional
    public Long createBillOrder(BillSaveReqVO createReqVO) {
        //旅行社账单
        BillDO billDO = BeanUtils.toBean(createReqVO, BillDO.class);
        String detailKey = String.format(BILL_LOCK, createReqVO.getAgencyId());
        RLock lock = redissonClient.getLock(detailKey);
        try {
            if (lock.tryLock(30, TimeUnit.SECONDS)) {
                for (TradeOrderRespVO order : createReqVO.getOrders()){
                    //通过订单id查询订单
                    TradeOrderRespVO tradeOrder = billMapper.getOrderById(order.getId());
                    Asserts.isTrue(tradeOrder.getIsBill() != 2 ,"账单生成失败，存在已生成账单的订单！");

                    //查询订单详情
                    List<TradeDetailRespVO> tradeDetailList = billMapper.getOrderIdDetail(order.getId());
                    Date now = new Date();
                    for(TradeDetailRespVO tradeDetail : tradeDetailList){
                        //判断状态 1待使用，2取票，3已核销，4退票中，5已退款,6逾期核销，7已过期
                        if(tradeDetail.getVoucherStatus() != 3 && tradeDetail.getVoucherStatus() != 5 && tradeDetail.getVoucherStatus() != 6
                                && tradeDetail.getVoucherStatus() != 7 && tradeDetail.getVoucherStatus() != 8
//                                && !(tradeDetail.getVoucherStatus()==9 && ObjectUtil.equal(tradeDetail.getCheckStatus(),1))){
                                && tradeDetail.getVoucherStatus()!=9){
                            Asserts.isTrue(false ,"账单生成失败，存在未完成订单！");
                        }
                        //只能在有效期第二天产生账单
                        Asserts.isTrue(DateUtil.compare(DateUtils.of(tradeDetail.getValidityEndDate()),now)<0
                                ,"账单只能在有效期结束后第二天创建");
                    }
                }
                //门店账单
                List<BillStoreDO> billStoreDO = new ArrayList<>();
                //订单出账状态(2全部出账，1部分出账)
                Integer isBill = 2;
                //账单生成方（1：平台，2：旅行社，3:门店）
                Integer billGenerator;
                //获取登录租户
                Long tenantId = TenantContextHolder.getTenantId();
                //查询是否门店登录
                LoginUser loginUser = agencyAuthUtils.getAgencyLoginUser();
                //是否旅行社登录
                DistributorRespVO distributor = new DistributorRespVO();
                if(loginUser.getDistributorId() != null){
                    distributor = distributorMapper.getById(loginUser.getDistributorId());
                    billGenerator = 3;
                }else{
                    distributor = distributorMapper.getById(createReqVO.getDistributorId());
                    billGenerator = 1;
                }

                //判断是否部分生成账单
                if(createReqVO.getOrders()!=null && createReqVO.getOrders().size() == 1){
                    TradeOrderRespVO order = createReqVO.getOrders().get(0);
                    //判断金额
                    if(order.getActualPayAmount().compareTo(createReqVO.getSellAmount()) > 0){
                        billDO.setSellAmount(createReqVO.getSellAmount());
                        isBill = 1;
                    }
                }
                Long id = IdWorker.getId(billDO);
                //获取登录用户
                LoginUser user = getLoginUser();
                if(createReqVO.getOrders()!=null && createReqVO.getOrders().size() > 0){
                    // 数据统计
                    statisticalData(user,createReqVO.getOrders(),billDO,isBill);
                    //判断是OTA
                    if(distributor.getType() == 2){
                        // 门店账单数据
                        statisticalStoreData(user,createReqVO.getOrders(),billStoreDO,isBill);
                    }

                    //保存旅行社账单
                    billDO.setId(id);
                    billDO.setProhibitType(createReqVO.getType());
                    billDO.setBillBuildType(2);
                    billDO.setBillGenerator(billGenerator);
                    billDO.setAgencyId(createReqVO.getDistributorId());
                    billDO.setAgencyName(distributor.getName());
                    billDO.setPayDeadline(createReqVO.getPayDeadline());
                    billDO.setOtaTenantId(distributor.getAccountTenantId());
                    billMapper.insert(billDO);
                    //判断是否OTA
                    if(distributor.getType() == 2){
                        //保存门店账单
                        DistributorRespVO finalDistributor = distributor;
                        billStoreDO.forEach(billStore -> {
                            billStore.setBillId(id);
                            billStore.setBillBuildType(2);
                            billStore.setBillGenerator(billGenerator);
                            //billStore.setStoreId(createReqVO.getStoreId());
                            //billStore.setStoreName(agencyName);
                            billStore.setAgencyId(createReqVO.getDistributorId());
                            billStore.setAgencyName(finalDistributor.getName());
                            billStore.setPayDeadline(createReqVO.getPayDeadline());
                            billStore.setOtaTenantId(finalDistributor.getAccountTenantId());
                            billStoreMapper.insert(billStore);
                        });
                    }

                    // 遍历订单
                    Iterator<TradeOrderRespVO> storeIterator = createReqVO.getOrders().iterator();
                    while (storeIterator.hasNext()) {
                        BillOrderDO bill = new BillOrderDO();
                        TradeOrderRespVO order = storeIterator.next();
                        bill.setId(IdWorker.getId());
                        bill.setExternalOrderId(order.getExternalOrderId());
                        bill.setBillId(id);
                        bill.setBillStoreId(order.getBillStoreId());
                        bill.setOrderId(order.getId());
                        billOrderMapper.insert(bill);
                        //修改订单已使用
                        billMapper.updateOrderIsBill(order.getId(),isBill);
                    }
                }

            } else {
                throw exception(BULL_NOT_FAIL);
            }
        } catch (Exception ex) {
            throw exception0(40_102,ex.getMessage());
        }finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
        return billDO.getId();
    }

    /**
     * 将订单根据门店分类
     * @param storeMap 门店 map
     */
    public void setSiteMap(List<TradeOrderRespVO> list, Map<Long, List<TradeOrderRespVO>> storeMap) {
        for (TradeOrderRespVO map : list) {
            Long storeId = map.getStoreId();
            List<TradeOrderRespVO> orderList = storeMap.get(storeId);
            if (orderList == null) {
                orderList = new ArrayList<>();
            }
            orderList.add(map);
            storeMap.put(storeId, orderList);
        }
    }

    void OrderSplit(List<TradeOrderRespVO> orders, List<TradeOrderRespVO> selfOrders, List<TradeOrderRespVO> platOrders){
        orders.forEach(order -> {
            //获取登录租户
            Long tenantId = TenantContextHolder.getTenantId();
            Long id = order.getProductId();
            //查询是否直接分销产品
            ProductSpuRespDTO spuRespDTO = billOrderMapper.getProductSpuPlat(id, tenantId);
            //查询规格
            if (spuRespDTO == null) {
                //不是直接分销产品查询自建产品
                spuRespDTO = billOrderMapper.getProductSpu(id);
            }
            if (spuRespDTO != null) {
                ProductSpuRespDTO finalSpuRespDTO = spuRespDTO;
                TenantUtils.execute(spuRespDTO.getTenantId(), () -> {
                    //产品规格信息
                    List<ProductGroupRespDTO> list = billOrderMapper.getProductSupInfo(id);
                    //平台产品信息
                    List<PlatProductRespDTO> platList = billOrderMapper.getPlatProductSupInfo(id);
                    // 查询分销产品规格信息
                    platList.forEach(item -> {
                        List<ProductGroupRespDTO> groupList;
                        groupList = billOrderMapper.getProductSupInfo(item.getPlatProductId());
                        item.setProductGroupList(groupList);
                    });
                    finalSpuRespDTO.setProductGroupList(list);
                    finalSpuRespDTO.setPlatProductList(platList);
                });
            }
            //统计数据
            List<ProductGroupRespDTO> list = spuRespDTO.getProductGroupList();
            List<PlatProductRespDTO> platList = spuRespDTO.getPlatProductList();
            if(list != null && list.size()>0){
                //自建产品
                for (ProductGroupRespDTO group : list){
                    //计算当前订单自建价格
                    BigDecimal pay = new BigDecimal(0);
                    pay = pay.add(pay);
                    order.setAmount(pay);
                }
                selfOrders.add(order);
            }
            if(platList != null && platList.size()>0){
                //分销产品
                for (PlatProductRespDTO plat : platList){
                    for(ProductGroupRespDTO group : plat.getProductGroupList()){
                        //计算当前订单自建价格
                        BigDecimal pay = new BigDecimal(0);
                        pay = pay.add(pay);
                        order.setAmount(pay);
                    }
                }

                platOrders.add(order);
            }

        });
    }

    /**
     * 自动统计旅行社数据
     * @param orders 订单列表
     * @param bill 账单
     */
    public void statisticalAutoData(List<TradeOrderRespVO> orders, BillDO bill){
        bill.setOrderNum(orders.size());
        bill.setOperatorName("自动生成账单");
        bill.setBillStatus(0);
        bill.setBillGenerator(-1);
        bill.setBillBuildType(1);
        BigDecimal pay = new BigDecimal(0);
        bill.setCreditPay(pay);
        bill.setSellAmount(pay);
        for (TradeOrderRespVO order : orders) {
            bill.setAgencyName(order.getSourceName());
            //通过订单ID查询退票金额
            BigDecimal refundAmount =  billOrderMapper.getRefundAmount(order.getId());
            BigDecimal priceType = order.getPayTypeAmount().subtract(refundAmount);
            bill.setCreditPay(bill.getCreditPay().add(priceType));
            bill.setSellAmount(bill.getSellAmount().add(priceType));
        }
    }

    /**
     * 自动统计旅行社数据
     * @param orders 订单列表
     * @param billStoresList 账单
     */
    public void statisticalStoreAutoData(List<TradeOrderRespVO> orders, List<BillStoreDO> billStoresList){
        //门店分组
        Map<Long, List<TradeOrderRespVO>> storeMap = new HashMap<>(16);
        setSiteMap(orders, storeMap);
        //循环门店
        Set<Long> storeSet = storeMap.keySet();
        Iterator<Long> storeIterator = storeSet.iterator();
        while (storeIterator.hasNext()) {
            BillStoreDO billStoreDO = new BillStoreDO();
            //获取门店订单
            Long storeId = storeIterator.next();
            List<TradeOrderRespVO> orderList =  storeMap.get(storeId);
            Long id = IdWorker.getId(billStoreDO);
            billStoreDO.setId(id);
            billStoreDO.setOrderNum(orderList.size());
            billStoreDO.setOperatorName("自动生成账单");
            billStoreDO.setBillStatus(0);
            billStoreDO.setBillGenerator(-1);
            billStoreDO.setBillBuildType(1);
            billStoreDO.setStoreId(storeId);
            List<TradeOrderRespVO> storeOrders = storeMap.get(storeId);
            BigDecimal pay = new BigDecimal(0);
            billStoreDO.setCreditPay(pay);
            billStoreDO.setSellAmount(pay);
            for(TradeOrderRespVO order : storeOrders){
                order.setBillStoreId(id);
                billStoreDO.setStoreId(order.getStoreId());
                billStoreDO.setStoreName(order.getStoreName());
                //通过订单ID查询退票金额
                BigDecimal refundAmount =  billOrderMapper.getRefundAmount(order.getId());
                BigDecimal priceType = order.getPayTypeAmount().subtract(refundAmount);
                billStoreDO.setCreditPay(billStoreDO.getCreditPay().add(priceType));
                billStoreDO.setSellAmount(billStoreDO.getSellAmount().add(priceType));
            }
            // 数据统计
            billStoresList.add(billStoreDO);
        }
    }

    /**
     * 统计当前旅行社数据
     * @param orders 订单列表
     * @param bill 账单
     */
    public void statisticalData(LoginUser user, List<TradeOrderRespVO> orders, BillDO bill,Integer isBill){
        bill.setOrderNum(orders.size());
        bill.setOperatorId(user.getId());
        AdminUserRespDTO adminUser = adminUserApi.getUser(user.getId());
        if (adminUser != null) {
            bill.setOperatorName(adminUser.getNickname());
        }
        bill.setBillStatus(0);
        //判断是否部分出账
        if(isBill != 1){
            BigDecimal pay = new BigDecimal(0);
            bill.setCreditPay(pay);
            bill.setSellAmount(pay);
            for (TradeOrderRespVO order : orders) {
                bill.setAgencyName(order.getSourceName());
                //通过订单ID查询退票金额
                BigDecimal refundAmount =  billOrderMapper.getRefundAmount(order.getId());
                BigDecimal priceType = order.getPayTypeAmount().subtract(refundAmount);
                bill.setCreditPay(bill.getCreditPay().add(priceType));
                bill.setSellAmount(bill.getSellAmount().add(priceType));
            }
        }else{
            bill.setCreditPay(bill.getSellAmount());
            bill.setSellAmount(orders.get(0).getPayAmount());
        }

    }

    /**
     * 统计当前门店数据
     * @param orders 订单列表
     * @param billStoresList 账单
     */
    public void statisticalStoreData(LoginUser user, List<TradeOrderRespVO> orders, List<BillStoreDO> billStoresList,Integer isBill){
        //门店分组
        Map<Long, List<TradeOrderRespVO>> storeMap = new HashMap<>(16);
        if(isBill != 1){
            setSiteMap(orders, storeMap);
            //循环门店
            Set<Long> storeSet = storeMap.keySet();
            Iterator<Long> storeIterator = storeSet.iterator();
            while (storeIterator.hasNext()) {
                BillStoreDO billStoreDO = new BillStoreDO();
                //获取门店订单
                Long storeId = storeIterator.next();
                List<TradeOrderRespVO> orderList =  storeMap.get(storeId);
                Long id = IdWorker.getId(billStoreDO);
                billStoreDO.setId(id);
                billStoreDO.setOrderNum(orderList.size());
                billStoreDO.setOperatorId(user.getId());
                AdminUserRespDTO adminUser = adminUserApi.getUser(user.getId());
                if (adminUser != null) {
                    billStoreDO.setOperatorName(adminUser.getNickname());
                }
                billStoreDO.setBillStatus(0);
                billStoreDO.setStoreId(storeId);
                List<TradeOrderRespVO> storeOrders = storeMap.get(storeId);
                BigDecimal pay = new BigDecimal(0);
                billStoreDO.setCreditPay(pay);
                billStoreDO.setSellAmount(pay);
                for(TradeOrderRespVO order : storeOrders){
                    order.setBillStoreId(id);
                    billStoreDO.setStoreId(order.getStoreId());
                    billStoreDO.setStoreName(order.getStoreName());
                    //通过订单ID查询退票金额
                    BigDecimal refundAmount =  billOrderMapper.getRefundAmount(order.getId());
                    BigDecimal priceType = order.getPayTypeAmount().subtract(refundAmount);
                    billStoreDO.setCreditPay(billStoreDO.getCreditPay().add(priceType));
                    billStoreDO.setSellAmount(billStoreDO.getSellAmount().add(priceType));
                }
                // 数据统计
                billStoresList.add(billStoreDO);
            }
        }else{
            BillStoreDO billStoreDO = new BillStoreDO();
            //获取门店订单
            billStoreDO.setId(IdWorker.getId());
            billStoreDO.setOrderNum(orders.size());
            billStoreDO.setOperatorId(user.getId());
            AdminUserRespDTO adminUser = adminUserApi.getUser(user.getId());
            if (adminUser != null) {
                billStoreDO.setOperatorName(adminUser.getNickname());
            }
            billStoreDO.setBillStatus(0);
            billStoreDO.setStoreId(orders.get(0).getStoreId());
            // 数据统计
            billStoresList.add(billStoreDO);
        }

    }

    @Override
    @TenantIgnore
    public void updateBill(BillSaveReqVO updateReqVO) {
        // 校验存在 门店、旅行社修改账单都是同一个
        BillDO billDO =  billMapper.selectById(updateReqVO.getId());
        if(billDO != null){
            // 更新主账单
            BillDO updateObj = BeanUtils.toBean(updateReqVO, BillDO.class);
            if(billDO.getBillStatus() == 4){
                updateObj.setBillStatus(0);
            }else if(billDO.getBillStatus() == 3){
                updateObj.setBillStatus(2);
            }
            billMapper.updateById(updateObj);
        }else {
            // 更新子帐单
            BillStoreDO billStoreDO = billStoreMapper.selectById(updateReqVO.getId());
            BillStoreDO update = BeanUtils.toBean(updateReqVO, BillStoreDO.class);
            if(billStoreDO!=null){
                if(billStoreDO.getBillStatus() == 4){
                    update.setBillStatus(0);
                }
                billStoreMapper.updateById(update);
            }
        }
    }

    @Override
    @Transactional
    @TenantIgnore
    public void deleteBill(Long id) {
        List<RLock> rLockList = batchLock(id);
        try {
            BillDO billDO = billMapper.selectById(id);
            Asserts.isTrue(billDO!=null,"账单不存在");
            //出账、逾期
            Asserts.isTrue(ObjectUtils.equalsAny(billDO.getBillStatus(),0,4),"账单状态不能被删除");
            List<BillStoreDO> list = billStoreMapper.getStoreBillById(id,null);
            for (BillStoreDO billStoreDO : list) {
               Asserts.isTrue(ObjectUtils.equalsAny(billStoreDO.getBillStatus(),0,4),"账单存在不能被删除的门店账单");
            }
            // 删除
            billMapper.updateOrderByBillId(id);
            billMapper.deleteStoreBillByBillId(id);
            billMapper.deleteBillByBillId(id);
            billMapper.deleteBillOrderByBillId(id);

        }finally {
           unLockBatch(rLockList);
        }
    }

    private List<RLock> batchLock(Long id){
        List<BillStoreDO> list = billStoreMapper.getStoreBillById(id,null);
        List<RLock> locks = new ArrayList<>(list.size());
        try {
            String key = String.format("billPay:lock:%d", id);
            RLock billLock = redissonClient.getLock(key);
            billLock.lock();
            locks.add(billLock);

            for (BillStoreDO billStoreDO : list) {
                String detailKey = String.format("billPay:lock:%d", billStoreDO.getId());
                RLock lock = redissonClient.getLock(detailKey);
                lock.lock();
                locks.add(lock);
            }
        }catch (Exception e){
            log.error("batchLock error", e);
            unLockBatch(locks);
            throw new ServiceException(500,"上锁失败");
        }
        return locks;
    }

    private void unLockBatch(List<RLock> locks){
        if(CollUtil.isEmpty(locks)){
            return;
        }
        for (RLock lock : locks) {
            if(lock.isLocked() && lock.isHeldByCurrentThread()){
                lock.unlock();
            }
        }
    }

    private BillDO validateBillExists(Long id) {
        BillDO billDO = billMapper.selectById(id);
        Asserts.isTrue(billDO!=null,"账单不存在");
        return billDO;
    }

    @Override
    public BillDO getBill(Long id) {
        return billMapper.selectById(id);
    }

    @Override
    public PageResult<BillRespVO> getBillPage(BillPageReqVO pageReqVO) {
        IPage<BillRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        //获取登录租户
        Long tenantId = TenantContextHolder.getTenantId();
//        pageReqVO.setOtaTenantId(tenantId);
        //查询当前旅行社门店
        LoginUser loginUser = agencyAuthUtils.getAgencyLoginUser();
        Long storeId = loginUser.getStoreId();
        Long distributorId = loginUser.getDistributorId();
        if(storeId == null){
            //获取旅行社
            if(distributorId!=null) {
                DistributorRespVO distributor = distributorMapper.getDistributorById(distributorId);
                //旅行社
                if (distributor != null) {
                    pageReqVO.setAgencyId(distributor.getId());
                    IPage<BillRespVO> iPage = billMapper.getBillPage(page, pageReqVO);
                    return new PageResult<>(iPage.getRecords(), iPage.getTotal());
                }
            }
            ///获取平台所有旅行社租户
            List<Long> agencyIds = distributorMapper.getIds();
            IPage<BillRespVO> iPage =  billMapper.getPage(page,pageReqVO,agencyIds);
            return new PageResult<>(iPage.getRecords(), iPage.getTotal());
        }
        //门店
        else{
            pageReqVO.setStoreId(storeId);
            IPage<BillRespVO> iPage = billMapper.getBillStorePage(page,pageReqVO);
            return new PageResult<>(iPage.getRecords(), iPage.getTotal());
        }
    }

    @Override
    public PageResult<BillStoreDO> getBillStorePage(BillPageReqVO pageReqVO) {
        IPage<BillStoreDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        IPage<BillStoreDO> iPage =  billStoreMapper.getBillStorePage(page,pageReqVO);
        return new PageResult<>(iPage.getRecords(), iPage.getTotal());
    }

    @Override
    @TenantIgnore
    public List<TradeOrderRespVO> getBillOrderList(Long id, Integer type) {
        List<TradeOrderRespVO> list = new ArrayList<>();
        List<BillOrderDO> billOrders = new ArrayList<>();
        if(type == 2){
            billOrders = billOrderMapper.selectList("bill_store_id",id);
        }else{
            billOrders = billOrderMapper.selectList("bill_id",id);
        }
        billOrders.forEach(billOrder -> {
            //获取订单详情
            TradeOrderRespVO order = billMapper.getOrderById(billOrder.getOrderId());
            list.add(order);
        });
        return list;
    }

    @Override
    public PageResult<TradeOrderRespVO> getOrderPage(TradeOrderPageReqVO pageReqVO) {
        //返回参数
        AtomicReference<IPage<TradeOrderRespVO>> iPage = new AtomicReference<>(new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()));
        //查询选中的分销商订单
        IPage<TradeOrderRespVO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
        iPage.set(billMapper.getOrderPage(page, pageReqVO));
        //查询账单号
        iPage.get().getRecords().forEach(order -> {
            if(pageReqVO.getType() == 2){
                BillOrderDO bill = billOrderMapper.getBillOrderId(order.getId());
                if(bill != null){
                    order.setBillId(bill.getBillId());
                }
            }
            TradeOrderRespVO tradeOrderRespVO;
            if(ObjectUtil.equal(order.getIsChangeOrder(),1)){
                tradeOrderRespVO = billMapper.getPayOrderWithChangeData(order.getId());
            }else{
                tradeOrderRespVO = billMapper.getPayOrderData(order.getId());
            }
            order.setPayTypeAmount(tradeOrderRespVO.getPayTypeAmount());
            order.setPayTypeRefundAmount(tradeOrderRespVO.getPayTypeRefundAmount());
            order.setActualPayAmount(tradeOrderRespVO.getActualPayAmount());
        });


        iPage.get().getRecords().forEach(billOrder -> billOrder.setDistributorName(billOrder.getSourceName()));
        return new PageResult<>(iPage.get().getRecords(), iPage.get().getTotal());
    }

    @Override
    public List<BillStoreDO> getStoreBillById(Long id) {
        List<BillStoreDO> list = billStoreMapper.getStoreBillById(id,null);
         return list;
    }

    @Override
    @TenantIgnore
    public List<DistributorRechargeReqDTO> billPayNotice(BillPayReqDTO payReqDTO) {
        List<DistributorRechargeReqDTO> req = new ArrayList<>();
        String detailKey = String.format("billPay:lock:%d", payReqDTO.getOrderId());
        RLock lock = redissonClient.getLock(detailKey);
        try {
            if (lock.tryLock(30, TimeUnit.SECONDS)) {
                if(payReqDTO.getPayStatus() == 0){
                    throw exception0(400, "支付失败！");
                }
                //查询当前账单类型
                BillDO billDO = billMapper.selectById(payReqDTO.getOrderId());
                AdminUserRespDTO adminUser = adminUserApi.getUser(payReqDTO.getPayUserId());
                //判断主账单还是门店帐单
                if(billDO == null){
                    BillStoreDO billStoreDO = billStoreMapper.selectById(payReqDTO.getOrderId());
                    if(billStoreDO != null && billStoreDO.getBillStatus() != 1){
                        //待还金额
                        BigDecimal sellAmount = billStoreDO.getSellAmount();
                        //修改门店账单状态
                        updateStoreBill(billStoreDO, payReqDTO,adminUser);
                        addRecharge(req, null, billStoreDO,sellAmount);
                        //查询门店旅行社账单
                        billDO = billMapper.selectById(billStoreDO.getBillId());
                        //判断门店账单是否唯一账单未还
                        if(billStoreMapper.getStoreBillById(billDO.getId(), Arrays.asList(0,4)).isEmpty()){
                            //门店还款
                            billDO.setBillStatus(1);
                            billDO.setPayTime(LocalDateTime.now());
                            if(billDO.getPayAmount() != null && !billDO.getPayAmount().equals(BigDecimal.ZERO)){
                                billDO.setPayAmount(billDO.getPayAmount().add(billStoreDO.getPayAmount()));
                            }else{
                                billDO.setPayAmount(billStoreDO.getPayAmount());
                            }
                            billDO.setPayType(payReqDTO.getPayType());
                            billDO.setPayOperatorId(payReqDTO.getPayUserId());
                            billDO.setSellAmount(billDO.getCreditPay().subtract(billDO.getPayAmount()));
                            if (adminUser != null) {
                                billDO.setPayOperatorName(adminUser.getNickname());
                            }
                            billMapper.updateById(billDO);
                            //addRecharge(req, billDO, null,sellAmount);
                        }else{
                            billDO.setBillStatus(2);
                            billDO.setPayTime(LocalDateTime.now());
                            if(billDO.getPayAmount() != null && !billDO.getPayAmount().equals(BigDecimal.ZERO)){
                                billDO.setPayAmount(billDO.getPayAmount().add(billStoreDO.getPayAmount()));
                            }else{
                                billDO.setPayAmount(billStoreDO.getPayAmount());
                            }
                            billDO.setPayType(payReqDTO.getPayType());
                            billDO.setPayOperatorId(payReqDTO.getPayUserId());
                            billDO.setSellAmount(billDO.getCreditPay().subtract(billDO.getPayAmount()));
                            if (adminUser != null) {
                                billDO.setPayOperatorName(adminUser.getNickname());
                            }
                            billMapper.updateById(billDO);
                            //addRecharge(req, billDO, null, sellAmount);
                        }
                    }else{
                        throw exception0(ORDER_PAY_AUTO_REFUND.getCode(), "子帐单已经支付！");
                    }
                }else if(billDO.getBillStatus() != 1){
                    //修改旅行社账单状态
                    //addRecharge(req, billDO, null,billDO.getSellAmount());
                    updateBill(billDO, payReqDTO,adminUser);
                    //查询所有门店账单帐单
                    List<BillStoreDO> list = billStoreMapper.getStoreBillById(billDO.getId(),Arrays.asList(0,4));
                    for (BillStoreDO vo : list){
                        //修改门店账单
                        addRecharge(req, null, vo, vo.getSellAmount());
                        updateStoreBill(vo, payReqDTO,adminUser);
                    }
                }else{
                    throw exception0(ORDER_PAY_AUTO_REFUND.getCode(), "主订单已经支付！");
                }
            } else {
                throw exception0(60_101, "账单还款失败");
            }
        } catch (Exception ex) {
            if(ex instanceof ServiceException){
                throw (ServiceException) ex;
            }
            throw exception0(60_102, "账单还款获取锁失败!");
        }finally {
            if (lock.isHeldByCurrentThread() && lock.isLocked()) {
                lock.unlock();
            }
        }
        return req;
    }

    @Override
    public BillOrderVerifyRespDTO billOrderVerify(BillOrderVerifyReqDTO billOrderVerifyReqDTO) {
        BillOrderVerifyRespDTO list = null;
        //查询旅行社账单
        List<BillDO> billDO = billMapper.getBillAgencyById(billOrderVerifyReqDTO.getAgencyId());
        if(!billDO.isEmpty()){
          for(BillDO bill : billDO){
              //判断是否旅行社不让下单
              if(bill.getProhibitType() == 1){
                  //判断截至时间
                  if(bill.getPayDeadline().isBefore(LocalDateTime.now())){
                      list = new BillOrderVerifyRespDTO();
                      list.setBillId(bill.getId());
                      list.setType(1);
                      list.setProhibitType(1);
                      return list;
                  }
              }else if(bill.getProhibitType() == 2){
                  //查询子账单
                  List<BillStoreDO> billStoreDO = billStoreMapper.getStoreBillById(bill.getId(),null);
                  for(BillStoreDO billStore : billStoreDO){
                      if(billStore.getBillStatus() == 0){
                          //判断时间
                          if(billStore.getPayDeadline().isBefore(LocalDateTime.now())){
                              list = new BillOrderVerifyRespDTO();
                              list.setBillId(billStore.getId());
                              list.setType(2);
                              list.setProhibitType(2);
                              return list;
                          }
                      }
                  }
              }
          }
        }
        return list;
    }

    @Override
    @TenantIgnore
    public void billOver() {
        //查询所有逾期的账单
        List<BillDO> billList = billMapper.getBillOver();
        billList.forEach( billDO -> {
            if(billDO.getBillStatus() == 0){
                billDO.setBillStatus(4);
            }else if(billDO.getBillStatus() == 1){
                billDO.setBillStatus(3);
            }
            billMapper.updateById(billDO);
        });
        List<BillStoreDO> billStoreList = billMapper.getBillStoreOver();
        billStoreList.forEach( billStoreDO -> {
            billStoreDO.setBillStatus(4);
            billStoreMapper.updateById(billStoreDO);
        });
    }

    @Override
    public Boolean billPayVerification(Long id) {
        //查询当前账单信息
        BillDO billDO = billMapper.selectById(id);
        if(billDO != null){
            return ObjectUtil.notEqual(billDO.getBillStatus(),1);
        }
        BillStoreDO billStoreDO = billStoreMapper.selectById(id);
        if(billStoreDO != null){
            return ObjectUtil.notEqual(billStoreDO.getBillStatus(),1);
        }
        return true;
    }

    @Override
    public BillStoreDO getBillStoreById(Long id) {
        return  billStoreMapper.selectById(id);
    }

    @TenantIgnore
    private void updateBill(BillDO billDO, BillPayReqDTO payReqDTO,AdminUserRespDTO adminUser) {
        billDO.setBillStatus(1);
        billDO.setPayTime(LocalDateTime.now());
        if(billDO.getPayAmount() != null && !billDO.getPayAmount().equals(BigDecimal.ZERO)){
            billDO.setPayAmount(billDO.getPayAmount().add(billDO.getSellAmount()));
        }else{
            billDO.setPayAmount(billDO.getSellAmount());
        }
        billDO.setPayType(payReqDTO.getPayType());
        billDO.setPayOperatorId(payReqDTO.getPayUserId());
        billDO.setSellAmount(billDO.getCreditPay().subtract(billDO.getPayAmount()));
        if (adminUser != null) {
            billDO.setPayOperatorName(adminUser.getNickname());
        }
        billMapper.updateById(billDO);
    }
    @TenantIgnore
    private void updateStoreBill(BillStoreDO billStoreDO, BillPayReqDTO payReqDTO,AdminUserRespDTO adminUser) {
        billStoreDO.setBillStatus(1);
        billStoreDO.setPayTime(LocalDateTime.now());
        billStoreDO.setPayAmount(billStoreDO.getSellAmount());
        billStoreDO.setPayType(payReqDTO.getPayType());
        billStoreDO.setPayOperatorId(payReqDTO.getPayUserId());
        billStoreDO.setSellAmount(billStoreDO.getCreditPay().subtract(billStoreDO.getSellAmount()));
        if (adminUser != null) {
            billStoreDO.setPayOperatorName(adminUser.getNickname());
        }
        billStoreMapper.updateById(billStoreDO);
    }

    private void addRecharge(List<DistributorRechargeReqDTO> rechargeList, BillDO billDO,BillStoreDO billStoreDO,BigDecimal price) {
        DistributorRechargeReqDTO rechargeReqDTO = new DistributorRechargeReqDTO();
       if(billDO != null){
           rechargeReqDTO.setAccountId(String.valueOf(billDO.getAgencyId()));
           rechargeReqDTO.setRechargeType(DistributorTradeTypeEnum.BALANCE_RECHARGE.getType());//授信
           rechargeReqDTO.setTradeType(DistributorTradeTypeEnum.CREDIT_RECHARGE.getType());
           rechargeReqDTO.setOrderId(String.valueOf(billDO.getId()));
           rechargeReqDTO.setAmount(price);
           rechargeList.add(rechargeReqDTO);
       }else if(billStoreDO != null) {
           rechargeReqDTO.setAccountId(String.valueOf(billStoreDO.getAgencyId()));
           rechargeReqDTO.setStoreId(billStoreDO.getStoreId());
           rechargeReqDTO.setRechargeType(DistributorTradeTypeEnum.BALANCE_RECHARGE.getType());//授信
           rechargeReqDTO.setTradeType(DistributorTradeTypeEnum.CREDIT_RECHARGE.getType());
           rechargeReqDTO.setOrderId(String.valueOf(billStoreDO.getId()));
           rechargeReqDTO.setAmount(price);
           rechargeList.add(rechargeReqDTO);
       }

        //营销政策充值
        //CommonResult<DistributorRechargeRespDTO> result = otaDistributorApi.tradeRecharge(rechargeReqDTO);
    }

    @Override
    public void resetBillByMarketing(BillResetReqDTO reqDTO){
        BillOrderDO billOrder= billOrderMapper.getBillOrderId(reqDTO.getOrderId());
        String key = String.format(BILL_UPDATE_LOCK,billOrder.getBillId());
        RLock lock = redissonClient.getLock(key);
        lock.lock();
        try{
            BillDO billDO = billMapper.selectById(billOrder.getBillId());
            BigDecimal afterCreditPay = billDO.getCreditPay().subtract(reqDTO.getRefundAmount());
            BigDecimal afterSellAmount = billDO.getSellAmount().subtract(reqDTO.getRefundAmount());
            LambdaUpdateWrapper<BillDO> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(BillDO::getId,billDO.getId());
            updateWrapper.set(BillDO::getCreditPay,afterCreditPay);
            updateWrapper.set(BillDO::getSellAmount,afterSellAmount);
            billMapper.update(updateWrapper);
            log.error("营销政策重置账单金额，订单"+reqDTO.getOrderId()+",账单ID"+billDO.getId()+"账单前金额"+billDO.getCreditPay()+"改后金额"+afterCreditPay
            +"改前还款金额"+billDO.getSellAmount()+"改后金额"+afterSellAmount);
            if(billOrder.getBillStoreId()!=null){
                String storeKey = String.format(BILL_STORE_LOCK,billOrder.getBillStoreId());
                RLock storeLock = redissonClient.getLock(storeKey);
                storeLock.lock();
                try{
                    BillStoreDO billStoreDO = billStoreMapper.selectById(billOrder.getBillStoreId());
                    BigDecimal afterStoreCreditPay = billStoreDO.getCreditPay().subtract(reqDTO.getRefundAmount());
                    BigDecimal afterStoreSellAmount = billStoreDO.getSellAmount().subtract(reqDTO.getRefundAmount());

                    LambdaUpdateWrapper<BillStoreDO> updateStoreWrapper = new LambdaUpdateWrapper<>();
                    updateStoreWrapper.eq(BillStoreDO::getId,billStoreDO.getId());
                    updateStoreWrapper.set(BillStoreDO::getCreditPay,afterStoreCreditPay);
                    updateStoreWrapper.set(BillStoreDO::getSellAmount,afterStoreSellAmount);
                    billStoreMapper.update(updateStoreWrapper);
                    log.error("营销政策重置账单金额，门店账单ID"+billStoreDO.getId()+"账单前金额"+billStoreDO.getCreditPay()+"改后金额"+afterStoreCreditPay
                            +"改前还款金额"+billStoreDO.getSellAmount()+"改后金额"+afterStoreSellAmount);
                } finally {
                    if (storeLock.isLocked() && storeLock.isHeldByCurrentThread()) {
                        storeLock.unlock();
                    }
                }
            }

        } finally {
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}