package com.yc.ship.module.product.service.voyagestockdetail;

import cn.hutool.core.util.IdUtil;
import com.yc.ship.framework.common.util.collection.CollectionUtils;
import com.yc.ship.module.product.controller.admin.voyagestockdetail.vo.VoyageStockDetailBatchSaveReqVO;
import com.yc.ship.module.product.controller.admin.voyagestockdetail.vo.VoyageStockDetailRespNewVO;
import com.yc.ship.module.product.controller.admin.voyagestockdetail.vo.VoyageStockDetailSaveReqVO;
import com.yc.ship.module.product.dal.dataobject.voyage.VoyageDO;
import com.yc.ship.module.product.dal.dataobject.voyagestock.VoyageStockDO;
import com.yc.ship.module.product.dal.dataobject.voyagestockdetail.VoyageStockDetailDO;
import com.yc.ship.module.product.dal.dataobject.voyagestockdetail.VoyageStockDetailLogDO;
import com.yc.ship.module.product.dal.dataobject.voyagestockdistribute.VoyageStockDistributeNewDO;
import com.yc.ship.module.product.dal.mysql.voyagestock.VoyageStockMapper;
import com.yc.ship.module.product.dal.mysql.voyagestockdistribute.VoyageStockDistributeNewMapper;
import com.yc.ship.module.product.enums.ProductStockStaticsName;
import com.yc.ship.module.product.framework.lock.ProductRedisKeyConstants;
import com.yc.ship.module.resource.api.ship.ShipApi;
import com.yc.ship.module.resource.api.ship.dto.RoomModelFloorNumDTO;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import com.yc.ship.framework.common.util.object.BeanUtils;

import com.yc.ship.module.product.dal.mysql.voyagestockdetail.VoyageStockDetailMapper;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 航次库存 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
@Slf4j
public class VoyageStockDetailServiceImpl implements VoyageStockDetailService {

    @Resource
    private VoyageStockDetailMapper voyageStockDetailMapper;

    @Resource
    private VoyageStockMapper voyageStockMapper;

    @Resource
    private VoyageStockDistributeNewMapper voyageStockDistributeNewMapper;
    @Resource
    private RedissonClient redissonClient;

    @Resource
    private ShipApi shipApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean createVoyageStock(List<VoyageStockDetailSaveReqVO> createReqVO) {
        if(createReqVO.isEmpty()) {
            return null;
        }
        Long voyageId = createReqVO.get(0).getVoyageId();
        //现在给整个航次的库存加锁
        String lockKey = String.format(ProductRedisKeyConstants.STOCK_REDIS_KEY_PREFIX, voyageId);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            lock.lock(60, TimeUnit.SECONDS);
            voyageStockDetailMapper.deleteByVoyageId(voyageId);
            List<VoyageStockDetailDO> list = BeanUtils.toBean(createReqVO, VoyageStockDetailDO.class);
            List<VoyageStockDetailDO> insertList = new ArrayList<>();
            list.stream().forEach(item -> {
                if(ProductStockStaticsName.TOTAL.equals(item.getRoomModelName())) {
                    VoyageStockDO stockDO = BeanUtils.toBean(item, VoyageStockDO.class);
                    voyageStockMapper.updateById(stockDO);
                    BigDecimal distributeNum = voyageStockDistributeNewMapper.selectTotalDistributeNum(voyageId);
                    handleShareNum(voyageId, distributeNum, null);
                }else {
                    insertList.add(item);
                }
            });
            return voyageStockDetailMapper.insertBatch(insertList);
        }finally {
            if(lock.isHeldByCurrentThread() && lock.isLocked()){
                lock.unlock();
            }
        }
    }

    /**
     * 处理共享库存
     * @param voyageId
     * @param distributeNum 已分配给分销商
     * stockDistributeNewList 分销商库存详情
     */
    @Override
    public void handleShareNum(Long voyageId,  BigDecimal distributeNum, List<VoyageStockDistributeNewDO> stockDistributeNewList) {
        VoyageStockDO stockDO = voyageStockMapper.selectById(voyageId);
        if(distributeNum == null) {
            distributeNum = BigDecimal.ZERO;
        }
        stockDO.setShareNum(stockDO.getCanSellNum().subtract(distributeNum));
        voyageStockMapper.updateById(stockDO);
        if(stockDistributeNewList != null && !stockDistributeNewList.isEmpty()) {
            Map<String, BigDecimal> numMap = stockDistributeNewList.stream().collect(Collectors.groupingBy(item -> item.getRoomModelId() + "_" + item.getFloor(), Collectors.mapping(VoyageStockDistributeNewDO::getNum, Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));
            List<VoyageStockDetailDO> list = voyageStockDetailMapper.selectListByVoyageId(voyageId);
            list.stream().forEach(item -> {
                BigDecimal num = numMap.get(item.getRoomModelId() + "_" + item.getFloor());
                if(num != null) {
                    //共享库存应该加上上次分出去的再减去这次分出去的
                    item.setShareNum(item.getCanSellNum().subtract(num));
                }
            });
            voyageStockDetailMapper.updateBatch(list);
        }
    }

    @Override
    public List<VoyageStockDetailRespNewVO> getInitByShip(Long shipId) {
        List<VoyageStockDetailDO> result = new ArrayList<>();
        List<RoomModelFloorNumDTO> list = shipApi.queryRoomModelFloorToNum(shipId);
        list.stream().forEach(item -> {
            VoyageStockDetailDO stockDetailDO = new VoyageStockDetailDO();
            stockDetailDO.setRoomModelId(item.getRoomModelId());
            stockDetailDO.setRoomModelName(item.getRoomModelName());
            stockDetailDO.setFloor(item.getFloor());
            stockDetailDO.setTotalNum(BigDecimal.valueOf(item.getNum()));
            stockDetailDO.setRealTotalNum(BigDecimal.valueOf(item.getNum()));
            stockDetailDO.setShelfNum(BigDecimal.ZERO);
            stockDetailDO.setNotShelfNum(BigDecimal.valueOf(item.getNum()));
            stockDetailDO.setShelfedNum(BigDecimal.ZERO);
            stockDetailDO.setBookNum(BigDecimal.ZERO);
            stockDetailDO.setCanSellNum(BigDecimal.ZERO);
            stockDetailDO.setOversoldNum(BigDecimal.ZERO);
            stockDetailDO.setVirtualNum(BigDecimal.ZERO);
            stockDetailDO.setSurplusNum(BigDecimal.valueOf(item.getNum()));
            result.add(stockDetailDO);
        });
        int intSum = list.stream().mapToInt(RoomModelFloorNumDTO::getNum).sum();
        BigDecimal sum = BigDecimal.valueOf(intSum);
        VoyageStockDetailDO voyageStockDO = new VoyageStockDetailDO();
        voyageStockDO.setTotalNum(sum);
        voyageStockDO.setRealTotalNum(sum);
        voyageStockDO.setNotShelfNum(sum);
        voyageStockDO.setShelfedNum(BigDecimal.ZERO);
        voyageStockDO.setBookNum(BigDecimal.ZERO);
        voyageStockDO.setCanSellNum(BigDecimal.ZERO);
        voyageStockDO.setVirtualNum(BigDecimal.ZERO);
        voyageStockDO.setOversoldNum(BigDecimal.ZERO);
        voyageStockDO.setSurplusNum(sum);
        voyageStockDO.setRoomModelName("合计");
        voyageStockDO.setFloor(0);
        result.add(voyageStockDO);
        return BeanUtils.toBean(result, VoyageStockDetailRespNewVO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean batchCreateVoyageStock(VoyageStockDetailBatchSaveReqVO createReqVO) {
        if(createReqVO.getList().isEmpty()) {
            return true;
        }
        Long shipId = createReqVO.getShipId();
        Integer type = createReqVO.getType();
        //TODO: 该处type不应使用魔法值，后续优化
        // 批量初始化库存,1表示该邮轮的全部航次，2表示指定航次
        if(type == 1) {
            //查询该邮轮未设置库存的航次
            List<Long> voyageIds = voyageStockMapper.selectNoShelfdNumVoyageIdsByShipId(shipId);
            handleBatchCreateVoyageStock(voyageIds, createReqVO);
        }else if(type == 2){
            List<Long> voyageIds = createReqVO.getVoyageIds();
            List<Long> voyageIdsNew = voyageStockMapper.selectNoShelfdNumVoyageIdsByVoyageIds(shipId,voyageIds);
            handleBatchCreateVoyageStock(voyageIdsNew, createReqVO);
        }
        return true;
    }

    private Boolean handleBatchCreateVoyageStock(List<Long> voyageIds, VoyageStockDetailBatchSaveReqVO createReqVO) {
        if(voyageIds.isEmpty()) {
            return true;
        }
        List<VoyageStockDetailDO> list = BeanUtils.toBean(createReqVO.getList(), VoyageStockDetailDO.class);
        List<VoyageStockDetailDO> insertList = new ArrayList<>();
        voyageStockDetailMapper.deleteByVoyageIds(voyageIds);
        voyageIds.stream().forEach(voyageId -> {
            list.stream().forEach(item -> {

                item.setVoyageId(voyageId);
                if(ProductStockStaticsName.TOTAL.equals(item.getRoomModelName())) {
                    VoyageStockDO stockDO = BeanUtils.toBean(item, VoyageStockDO.class);
                    stockDO.setShareNum(stockDO.getCanSellNum());
                    voyageStockMapper.updateById(stockDO);
                }else {
                    VoyageStockDetailDO voyageStockDetailDO = new VoyageStockDetailDO();
                    voyageStockDetailDO.setVoyageId(voyageId)
                            .setId(IdUtil.getSnowflakeNextId())
                            .setFloor(item.getFloor())
                            .setVirtualNum(item.getVirtualNum())
                            .setRoomModelId(item.getRoomModelId())
                            .setRoomModelName(item.getRoomModelName())
                            .setTotalNum(item.getTotalNum())
                            .setShelfNum(item.getShelfNum())
                            .setNotShelfNum(item.getNotShelfNum())
                            .setShelfedNum(item.getShelfedNum())
                            .setBookNum(item.getBookNum())
                            .setCanSellNum(item.getCanSellNum())
                            .setOversoldNum(item.getOversoldNum())
                            .setSurplusNum(item.getSurplusNum())
                            .setRealTotalNum(item.getRealTotalNum());
                    insertList.add(voyageStockDetailDO);
                }
            });
        });
        return voyageStockDetailMapper.insertBatch(insertList);
    }

    @Override
    public void updateVoyageStock(VoyageStockDetailSaveReqVO updateReqVO) {
        // 更新
        VoyageStockDetailDO updateObj = BeanUtils.toBean(updateReqVO, VoyageStockDetailDO.class);
        voyageStockDetailMapper.updateById(updateObj);
    }

    @Override
    public void deleteVoyageStock(Long id) {
        // 删除
        voyageStockDetailMapper.deleteById(id);
    }


    @Override
    public VoyageStockDetailDO getVoyageStock(Long id) {
        return voyageStockDetailMapper.selectById(id);
    }

    @Override
    public List<VoyageStockDetailDO> getListByVoyageId(Long voyageId) {
        return voyageStockDetailMapper.selectListByVoyageId(voyageId);
    }

    @Override
    public VoyageStockDetailDO getVoyageStockTotalRow(Long voyageId) {
        // 查询航次实际总库存数
        BigDecimal realTotalNum = Optional.ofNullable(voyageStockDetailMapper.selectRealTotalNumFromDistData(voyageId)).orElse(BigDecimal.ZERO);
        // 查询已分配房间数（领队数 + 陪同数）/ 2
        Double allocatedRoomCount = voyageStockDetailMapper.selectAllocatedRoomCountByVoyageId(voyageId);
        BigDecimal allocatedRoomNum = BigDecimal.valueOf(allocatedRoomCount != null ? allocatedRoomCount : 0);
        // 计算剩余库存 = 实际总库存 - 已分配房间数
        BigDecimal surplusNum = realTotalNum.subtract(allocatedRoomNum);
        // 构建汇总行数据
        VoyageStockDetailDO voyageStockDetailDO = new VoyageStockDetailDO();
        voyageStockDetailDO.setRoomModelName("内舱房");
        voyageStockDetailDO.setFloor(0);
        voyageStockDetailDO.setShelfNum(BigDecimal.ZERO);
        voyageStockDetailDO.setRealTotalNum(realTotalNum);
        voyageStockDetailDO.setBookNum(allocatedRoomNum);
        voyageStockDetailDO.setSurplusNum(surplusNum);
        return voyageStockDetailDO;
    }

}