package cn.newfeifan.mall.module.distri.service.shopsettlement;

import cn.newfeifan.mall.module.api.shop.ShopApi;
import cn.newfeifan.mall.module.api.shop.dto.ShopDTO;
import cn.newfeifan.mall.module.distri.constant.DistriConstants;
import cn.newfeifan.mall.module.distri.controller.admin.ptprofit.vo.PtProfitSaveReqVO;
import cn.newfeifan.mall.module.distri.controller.admin.ptprofitlog.vo.PtProfitLogSaveReqVO;
import cn.newfeifan.mall.module.distri.controller.admin.ptsettlement.vo.TransferTemplateRespVO;
import cn.newfeifan.mall.module.distri.dal.dataobject.orderitemsettlement.OrderItemSettlementDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.ordersettlement.OrderSettlementDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.ptprofit.PtProfitDO;
import cn.newfeifan.mall.module.distri.dal.dataobject.ptsettlement.PtSettlementDO;
import cn.newfeifan.mall.module.distri.dal.mysql.dailybill.DailyBillMapper;
import cn.newfeifan.mall.module.distri.dal.mysql.orderitemsettlement.OrderItemSettlementMapper;
import cn.newfeifan.mall.module.distri.dal.mysql.ordersettlement.OrderSettlementMapper;
import cn.newfeifan.mall.module.distri.dal.mysql.ptsettlement.PtSettlementMapper;
import cn.newfeifan.mall.module.distri.service.dailybill.dto.MerchantDTO;
import cn.newfeifan.mall.module.distri.service.dailybill.dto.OrderDTO;
import cn.newfeifan.mall.module.distri.service.dailybill.dto.OrderItemDTO;
import cn.newfeifan.mall.module.distri.service.ptprofit.PtProfitService;
import cn.newfeifan.mall.module.distri.service.ptprofitlog.PtProfitLogService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;

import cn.newfeifan.mall.module.distri.controller.admin.shopsettlement.vo.*;
import cn.newfeifan.mall.module.distri.dal.dataobject.shopsettlement.ShopSettlementDO;
import cn.newfeifan.mall.framework.common.pojo.PageResult;
import cn.newfeifan.mall.framework.common.util.object.BeanUtils;

import cn.newfeifan.mall.module.distri.dal.mysql.shopsettlement.ShopSettlementMapper;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cn.newfeifan.mall.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.newfeifan.mall.module.distri.enums.CaclEnum.ORDER_SETTLEMENT;
import static cn.newfeifan.mall.module.distri.enums.ErrorCodeConstants.SHOP_SETTLEMENT_NOT_EXISTS;

/**
 * 订单结算信息表，记录店铺的结算信息 Service 实现类
 *
 * @author 非繁人
 */
@Service
@Validated
public class ShopSettlementServiceImpl implements ShopSettlementService {

    @Resource
    private ShopSettlementMapper shopSettlementMapper;

    @Resource
    private DailyBillMapper dailyBillMapper;

    @Resource
    private OrderSettlementMapper orderSettlementMapper;

    @Resource
    private OrderItemSettlementMapper orderItemSettlementMapper;

    @Resource
    private PtSettlementMapper ptSettlementMapper;

    @Resource
    private ShopApi shopApi;

    @Resource
    private PtProfitLogService ptProfitLogService;

    @Resource
    private PtProfitService ptProfitService;

    @Override
    public Long createShopSettlement(ShopSettlementSaveReqVO createReqVO) {
        // 插入
        ShopSettlementDO shopSettlement = BeanUtils.toBean(createReqVO, ShopSettlementDO.class);
        shopSettlementMapper.insert(shopSettlement);
        // 返回
        return shopSettlement.getId();
    }

    @Override
    public void updateShopSettlement(ShopSettlementSaveReqVO updateReqVO) {
        // 校验存在
        validateShopSettlementExists(updateReqVO.getId());
        // 更新
        ShopSettlementDO updateObj = BeanUtils.toBean(updateReqVO, ShopSettlementDO.class);
        shopSettlementMapper.updateById(updateObj);
    }

    @Override
    public void deleteShopSettlement(Long id) {
        // 校验存在
        validateShopSettlementExists(id);
        // 删除
        shopSettlementMapper.deleteById(id);
    }

    private void validateShopSettlementExists(Long id) {
        if (shopSettlementMapper.selectById(id) == null) {
            throw exception(SHOP_SETTLEMENT_NOT_EXISTS);
        }
    }

    @Override
    public ShopSettlementDO getShopSettlement(Long id) {
        return shopSettlementMapper.selectById(id);
    }

    @Override
    public PageResult<ShopSettlementRespVO> getShopSettlementPage(ShopSettlementPageReqVO pageReqVO) {
        PageResult<ShopSettlementDO> shopSettlementDOPageResult = shopSettlementMapper.selectPage(pageReqVO);
        PageResult<ShopSettlementRespVO> result = BeanUtils.toBean(shopSettlementDOPageResult, ShopSettlementRespVO.class);
        for (ShopSettlementRespVO shopSettlementRespVO : result.getList()) {
            shopSettlementRespVO.setShopName(shopSettlementMapper.getShopName(shopSettlementRespVO.getShopId()));
        }
        return result;
    }

    @Override
    public List<TransferTemplateRespVO> excelShopSettlementPage(ShopSettlementPageReqVO pageReqVO) {
        PageResult<ShopSettlementDO> shopSettlementDOPageResult = shopSettlementMapper.selectPage(pageReqVO);

        PageResult<ShopSettlementRespVO> result = BeanUtils.toBean(shopSettlementDOPageResult, ShopSettlementRespVO.class);

        List<TransferTemplateRespVO> list = new ArrayList<>();

        for (ShopSettlementRespVO respVO : result.getList()) {
            ShopDTO shop = shopApi.getShop(respVO.getShopId());

            // 定义日期时间格式
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

            list.add(TransferTemplateRespVO.builder()
                    .payeeName(shop.getAccountName())
                    .payeeAccount(shop.getAccountNumber())
                    .payeeBankName(shop.getBankName())
                    .money(respVO.getShopTotalHighAmount().divide(BigDecimal.valueOf(DistriConstants.ONE_HUNDRED), 2, RoundingMode.FLOOR).toString())
                    .mark("账单日期: " + formatter.format(respVO.getCreateTime().minusDays(1)))
                    .build());
        }
        return list;
    }

    /**
     * 把数据库的积分转换为数据库的钱
     *
     * @param integral 积分
     * @return 钱
     */
    public static Integer integral2Money(Long integral) {
        long l = (integral / DistriConstants.MONEY2INTEGRAL);
        return (int) l;
    }

    @Override
    public void settlementOrder() {
        //先获取所用的商户，再获取旗下的店铺，再获取店铺的订单，再计算
        List<MerchantDTO> merchantDTOS = dailyBillMapper.getMerchantDTO();

        // 订单项ids
        Map<Long, List<Long>> shopOrderIds = new HashMap<>();
        Map<Long, List<Long>> shopOrderItemIds = new HashMap<>();
        List<ShopSettlementDO> shopSettlementDOS = new ArrayList<>();

        Integer ptPlatformCollectionAmount = 0;
        // 代收积分
        Long ptPayIntegral = 0L;
        // 代收消费分
        Long ptPayConsumptionPoints = 0L;
        // 店铺应得金额（成本 * 数量）
        int ptShopAmount = 0;
        BigDecimal ptShopHighAmount = BigDecimal.ZERO;

        for (MerchantDTO merchant : merchantDTOS) {
            for (Long shopId : merchant.getShopIds()) {
                // 后面补加的，记录结算时的账号，而不是实时的
                ShopDTO shop = shopApi.getShop(shopId);
                List<OrderDTO> orders = shopSettlementMapper.getOrderByMerchantIdWithsShopId(shopId, merchant.getMerchantId());
                // 当前店铺没有订单，则跳过
                if (orders.isEmpty()) {
                    continue;
                }

                List<Long> orderIds = new ArrayList<>();
                List<Long> orderItemIds = new ArrayList<>();

                // pt代收金额
                Integer platformCollectionAmount = 0;
                // 代收积分
                Long payIntegral = 0L;
                // 代收消费分
                Long payConsumptionPoints = 0L;

                // 店铺应得金额（成本 * 数量）
                int shopAmount = 0;
                BigDecimal shopHighAmount = BigDecimal.ZERO;

                //获取该店铺下的订单
                for (OrderDTO order : orders) {
                    // 获取所有的订单id
                    orderIds.add(order.getId());

                    // 记录每笔订单的利润
                    BigDecimal profit = BigDecimal.ZERO;

                    for (OrderItemDTO item : order.getItems()) {
                        //拿到该店铺下的所有订单项id
                        orderItemIds.add(item.getId());
                        //计算订单的收入金额(pt)
                        platformCollectionAmount += item.getPayRmb();
                        ptPlatformCollectionAmount += item.getPayRmb();
                        //计算订单的收入积分（pt）
                        payIntegral += item.getPayIntegral();
                        payConsumptionPoints += item.getPayConsumptionPoints();     //消费分
                        ptPayIntegral += item.getPayIntegral();
                        ptPayConsumptionPoints += item.getPayConsumptionPoints();   //消费分
                        // 商品的成本价（成本 * 数量）
                        shopAmount += item.getSettlementPrice() * item.getCount();
                        ptShopAmount += item.getSettlementPrice() * item.getCount();
                        // 商品的高精度成本价（成本 * 数量）
                        shopHighAmount = shopHighAmount.add(BigDecimal.valueOf(item.getHighPrecisionSettlementPrice().doubleValue() * item.getCount()));
                        ptShopHighAmount = ptShopHighAmount.add(BigDecimal.valueOf(item.getHighPrecisionSettlementPrice().doubleValue() * item.getCount()));

                        // 如果商品成本不为0，则计算利润
                        if(!item.getSettlementPrice().equals(0)){
                            profit = profit.add(BigDecimal.valueOf(item.getCostPrice() - item.getSettlementPrice()));
                        } else if(item.getHighPrecisionSettlementPrice().compareTo(new BigDecimal("0")) == 0){
                            // 商品的高精度成本价
                            profit = profit.add(item.getHighPrecisionPrice().subtract(item.getHighPrecisionSettlementPrice()));
                        }
                    }

                    // 订单利润大于0的时候才操作
                    if(profit.compareTo(BigDecimal.ZERO) > 0){
                        PtProfitDO ptProfit = ptProfitService.getPtProfit();

                        // 金额转积分 放大一万倍
                        long value = profit.multiply(BigDecimal.valueOf(DistriConstants.PERCENT)).longValue();
                        ptProfit.setProfit(ptProfit.getProfit() + value);
                        ptProfit.setPtTotalAdd(ptProfit.getPtTotalAdd() + value);

                        ptProfitService.updatePtProfit(BeanUtils.toBean(ptProfit, PtProfitSaveReqVO.class));

                        // 记录平台钱包的变动日志
                        PtProfitLogSaveReqVO ptProfitLog = PtProfitLogSaveReqVO.builder()
                                .amount(value)
                                .afterAmount(ptProfit.getPtTotalAdd())
                                .profitStatus(ORDER_SETTLEMENT.getType())
                                .orderId(order.getId())
                                .orderNo(order.getNo())
                                .userId(order.getUserId())
                                .build();
                        ptProfitLogService.createPtProfitLog(ptProfitLog);
                    }
                }

                shopOrderIds.put(shopId, orderIds);
                shopOrderItemIds.put(shopId, orderItemIds);

                /*
                  积分也是pt代收
                  积分要转换才能变为钱
                 */
                platformCollectionAmount += integral2Money(payIntegral);
                platformCollectionAmount += integral2Money(payConsumptionPoints);


                // 店铺共计成本金额
                BigDecimal totalHighAmount = shopHighAmount.add(BigDecimal.valueOf(shopAmount));

                // 插入店铺结算信息
                ShopSettlementDO shopSett = ShopSettlementDO.builder()
                        .shopId(shopId)
                        .merchantId(merchant.getMerchantId())
                        .platformCollectionAmount(platformCollectionAmount)
                        .shopAmount(payIntegral)
                        .shopConsumptionPoints(payConsumptionPoints)
                        .shopTotalHighAmount(totalHighAmount.setScale(6, RoundingMode.HALF_UP))
                        .accountName(shop.getAccountName())
                        .accountNumber(shop.getAccountNumber())
                        .build();
                shopSettlementDOS.add(shopSett);
            }
        }

        ptPlatformCollectionAmount += integral2Money(ptPayIntegral);
        ptPlatformCollectionAmount += integral2Money(ptPayConsumptionPoints);
        // 平台共计成本金额
        BigDecimal totalHighAmount = ptShopHighAmount.add(BigDecimal.valueOf(ptShopAmount));

        // 平台每日记录
        PtSettlementDO ptSettlementDO = PtSettlementDO.builder()
                .platformCollectionAmount(ptPlatformCollectionAmount)
                .shopAmount(ptPayIntegral)
                .consumptionPoints(ptPayConsumptionPoints)
                .shopTotalHighAmount(totalHighAmount.setScale(6, RoundingMode.HALF_UP))
                .build();
        ptSettlementMapper.insert(ptSettlementDO);

        shopSettlementDOS.forEach(shopSett -> {
            shopSett.setPtSettlementId(ptSettlementDO.getId());
            shopSettlementMapper.insert(shopSett);

            // 插入订单结算信息（订单二级）
            List<Long> orderIds = shopOrderIds.get(shopSett.getShopId());
            orderIds.forEach(orderId -> orderSettlementMapper.insert(OrderSettlementDO.builder()
                    .tradeShopSettlementId(shopSett.getId())
                    .tradeOrderId(orderId)
                    .build()));
            // 更新订单的状态,将所有的订单状态更新为已完成
            shopSettlementMapper.updateOrderById(orderIds);

            // 插入订单项结算信息（订单项二级）
            List<Long> orderItemIds = shopOrderItemIds.get(shopSett.getShopId());
            orderItemIds.forEach(orderItemId -> orderItemSettlementMapper.insert(OrderItemSettlementDO.builder()
                    .tradeShopSettlementId(shopSett.getId())
                    .tradeOrderItemId(orderItemId)
                    .build()));
            // 更新订单项的结算时间
            shopSettlementMapper.setOrderItemById(orderItemIds);
        });

    }

    @Override
    public List<ShopSettlementDO> selectListByPtId(Long id) {
        return shopSettlementMapper.selectList(ShopSettlementDO::getPtSettlementId, id);
    }
}