package com.gree.mall.manager.logic.material.stock;

import cn.hutool.core.collection.CollectionUtil;
import com.gree.mall.manager.bean.material.stock.WebsitStockDTO;
import com.gree.mall.manager.bean.material.stock.WorkerStockDTO;
import com.gree.mall.manager.commonmapper.MaterialGoodsStockCMapper;
import com.gree.mall.manager.constant.Constant;
import com.gree.mall.manager.enums.material.DirectFlagEnum;
import com.gree.mall.manager.enums.material.WebsitGoodsTypeEnum;
import com.gree.mall.manager.exception.RemoteServiceException;
import com.gree.mall.manager.plus.entity.*;
import com.gree.mall.manager.plus.service.*;
import com.gree.mall.manager.utils.ValidateUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import javax.validation.ValidationException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class MaterialGoodsStockLogic {

    private final RedisLockRegistry redisLockRegistry;
    private final MaterialGoodsStockCMapper materialGoodsStockCMapper;
    private final WebsitStockService websitStockService;
    private final WebsitStockAccService websitStockAccService;
    private final WorkerStockService workerStockService;
    private final WorkerStockAccService workerStockAccService;
    private final WebsitPurchaseService websitPurchaseService;
    private final WebsitPartsSalesItemService websitPartsSalesItemService;

    /**
     * 单个网点库存处理
     * @param stockList
     * @throws Exception
     */
    public void handleWebsitStock(List<WebsitStockDTO> stockList) throws Exception {
        if(!TransactionSynchronizationManager.isSynchronizationActive()) {
            throw new RemoteServiceException("请先开启事务");
        }
//        System.out.println("程序入口：" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        if (CollectionUtil.isEmpty(stockList)) {
            return;
        }
//        System.out.println("检查传入的参数入口：" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        // 检查传入的参数
        for (int i = 0; i < stockList.size(); i++) {
            WebsitStockDTO websitStockDTO = stockList.get(i);
            try {
                ValidateUtil.validate(websitStockDTO);
                if (websitStockDTO.getChangeQty().compareTo(BigDecimal.ZERO) <= 0) {
                    throw new RuntimeException("变动数量不能少于0");
                }
            } catch (ValidationException ve) {
                throw new RemoteServiceException("执行数据第" + (i+1) + "行" + ve.getMessage());
            }
        }
//        System.out.println("检查传入的参数出口：" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        // 锁定网点库存
//        System.out.println("锁定网点库存入口：" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        WebsitStockDTO websitStockDTO = stockList.get(0);
        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.MATERIAL_STOCK_LOCK +
                websitStockDTO.getCompanyWechatId() + ":" +
                websitStockDTO.getWebsitId() + ":" +
                websitStockDTO.getGoodsType() + ":" +
                websitStockDTO.getStorageId());
        if(!obtain.tryLock(10, TimeUnit.SECONDS)){
            throw new RemoteServiceException("库存已冻结:" + websitStockDTO.getWebsitName());
        }
//        System.out.println("锁定网点库存出口：" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        // 处理网点库存
        try{
            this.saveWebsitStock(websitStockDTO, stockList);

        } finally {
            this.txCallUnlock(obtain);
//            System.out.println("程序出口：" + DateUtil.format(DateUtil.date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        }
    }

    /**
     * 单个师傅库存处理
     * @param stockList
     * @throws Exception
     */
    public void handleWorkerStock(List<WorkerStockDTO> stockList) throws Exception {
        if(!TransactionSynchronizationManager.isSynchronizationActive()) {
            throw new RemoteServiceException("请先开启事务");
        }
        if (CollectionUtil.isEmpty(stockList)) {
            return;
        }
        // 检查传入的参数
        for (int i = 0; i < stockList.size(); i++) {
            WorkerStockDTO workerStockDTO = stockList.get(i);
            try {
                ValidateUtil.validate(workerStockDTO);
                if ("销售订单".equals(workerStockDTO.getRefType()) && StringUtils.isBlank(workerStockDTO.getWebsitId())) {
                    throw new RemoteServiceException("销售网点编号不能空");
                }
                if (workerStockDTO.getChangeQty().compareTo(BigDecimal.ZERO) <= 0) {
                    throw new RuntimeException("变动数量不能少于0");
                }
            } catch (ValidationException ve) {
                throw new RemoteServiceException("第" + (i+1) + "行" + ve.getMessage());
            }
        }
        // 锁定师傅配件库存
        WorkerStockDTO workerStockDTO = stockList.get(0);
        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.MATERIAL_STOCK_LOCK +
                workerStockDTO.getCompanyWechatId() + ":" +
                workerStockDTO.getWorkerId() + ":" +
                workerStockDTO.getGoodsType());
        if(!obtain.tryLock(10, TimeUnit.SECONDS)){
            throw new RemoteServiceException("库存已冻结:" + workerStockDTO.getWorkerName());
        }
        // 处理师傅库存
        try{
            this.saveWorkerStock(workerStockDTO, stockList);
        } finally {
            this.txCallUnlockWorker(obtain);
        }
    }

    private void saveWorkerStock(WorkerStockDTO workerStockDTO, List<WorkerStockDTO> stockList) throws Exception {
        // 查询存在的商品库存
        List<WorkerStock> existWorkerStockList = materialGoodsStockCMapper.queryExistWorkerStockList(workerStockDTO.getCompanyWechatId(),
                workerStockDTO.getWorkerId(), stockList);
        // 全部商品库存
        Map<String, WorkerStock> existWorkerStockMap = this.createNotExistWorkerStock(workerStockDTO.getGoodsType(), stockList, existWorkerStockList);
        // 计算商品库存
        List<WorkerStockAcc> workerStockAccList = new ArrayList<>();
        this.computeWorkerStock(stockList, existWorkerStockMap, workerStockAccList);

        if (CollectionUtil.isEmpty(workerStockAccList)) {
            throw new RemoteServiceException("师傅库存处理失败");
        }

        workerStockService.saveOrUpdateBatch(existWorkerStockMap.values());
        workerStockAccService.saveBatch(workerStockAccList);
    }

    private void computeWorkerStock(List<WorkerStockDTO> stockList, Map<String, WorkerStock> existWorkerStockMap,
                                    List<WorkerStockAcc> workerStockAccList) throws Exception {
        for (WorkerStockDTO workerStockDTO : stockList) {
            WorkerStock workerStock = existWorkerStockMap.get(workerStockDTO.getGoodsId());
            if (workerStockDTO.getGoodsType().equals(WebsitGoodsTypeEnum.P.getKey())) {
                workerStock = existWorkerStockMap.get(workerStockDTO.getCompanyWechatId() + workerStockDTO.getWebsitId() + workerStockDTO.getPartsWebsitId() +  workerStockDTO.getGoodsId());
            }
            WorkerStockAcc workerStockAcc = new WorkerStockAcc();
            workerStockDTO.computeWorkerStock(workerStock, workerStockAcc);
            workerStockAccList.add(workerStockAcc);
        }
    }

    private Map<String, WorkerStock> createNotExistWorkerStock(String goodsType, List<WorkerStockDTO> stockList, List<WorkerStock> existWorkerStockList) {
        // 已存在的商品库存转成Map
        Map<String, WorkerStock> existShopStockMap;

        if (goodsType.equals(WebsitGoodsTypeEnum.M.getKey())) {
            existShopStockMap = existWorkerStockList.stream()
                    .collect(Collectors.toMap(WorkerStock::getGoodsId, Function.identity()));
        } else {
            existShopStockMap = existWorkerStockList.stream()
                    .collect(Collectors.toMap(o -> o.getCompanyWechatId() + o.getWebsitId() + o.getPartsWebsitId() +  o.getGoodsId(), Function.identity()));
        }

        // 找出不存在的商品库存并存入新商品库存List
        for (WorkerStockDTO workerStockDTO : stockList) {
            WorkerStock workerStock = existShopStockMap.get(workerStockDTO.getGoodsId());
            if (goodsType.equals(WebsitGoodsTypeEnum.P.getKey())) {
                workerStock = existShopStockMap.get(workerStockDTO.getCompanyWechatId() + workerStockDTO.getWebsitId() + workerStockDTO.getPartsWebsitId() +  workerStockDTO.getGoodsId());
            }

            if (Objects.isNull(workerStock)) {
                workerStock = workerStockDTO.createWorkerStock();
                if (goodsType.equals(WebsitGoodsTypeEnum.M.getKey())) {
                    existShopStockMap.put(workerStockDTO.getGoodsId(), workerStock);
                } else {
                    existShopStockMap.put(workerStockDTO.getCompanyWechatId() + workerStockDTO.getWebsitId() + workerStockDTO.getPartsWebsitId() +  workerStockDTO.getGoodsId(), workerStock);
                }
            }
        }
        return existShopStockMap;
    }

    private void saveWebsitStock(WebsitStockDTO websitStockDTO, List<WebsitStockDTO> stockList) {
        // 查询存在的配件库存
        List<WebsitStock> existWebsitStockList = materialGoodsStockCMapper.queryExistWebsitStockList(websitStockDTO.getCompanyWechatId(),
                websitStockDTO.getWebsitId(), websitStockDTO.getStorageId(), stockList);

        // 全部商品库存
        Map<String, WebsitStock> existWebsitStockMap = this.createNotExistStock(stockList, existWebsitStockList);
        // 计算商品库存
        List<WebsitStockAcc> shopStockAccList = new ArrayList<>();
        this.computeWebsitStock(stockList, existWebsitStockMap, null, shopStockAccList);

        if (CollectionUtil.isEmpty(shopStockAccList)) {
            throw new RemoteServiceException("商品库存处理失败");
        }

        websitStockService.saveOrUpdateBatch(existWebsitStockMap.values());
        websitStockAccService.saveBatch(shopStockAccList);
    }

    private Map<String, WebsitStock> createNotExistStock(List<WebsitStockDTO> stockList, List<WebsitStock> existWebsitStockList) {
        // 已存在的配件库存转成Map
        Map<String, WebsitStock> existStockMap = existWebsitStockList.stream()
                .collect(Collectors.toMap(WebsitStock::getGoodsId, Function.identity()));

        // 找出不存在的配件库存并存入新配件库存List
        for (WebsitStockDTO websitStockDTO : stockList) {
            WebsitStock shopStock = existStockMap.get(websitStockDTO.getGoodsId());
            if (Objects.isNull(shopStock)) {
                shopStock = websitStockDTO.createPartsStock();
                existStockMap.put(websitStockDTO.getGoodsId(), shopStock);
            }
            if (websitStockDTO.getRefType().equals("采购入库")) {
                shopStock.setLastCost(websitStockDTO.getPrice());
            }
            if (websitStockDTO.getRefType().equals("采购调整")
                    && websitStockDTO.getDirectFlag().equals(DirectFlagEnum.ADD.getKey())) {
                // 如果是采购调整并且方向是增加检查是否需要注入最后一次采购价
                final WebsitPurchase purchase = websitPurchaseService.lambdaQuery()
                        .select(WebsitPurchase::getPurchaseId)
                        .orderByDesc(WebsitPurchase::getConfirmTime)
                        .last("limit 1")
                        .one();
                if (purchase.getPurchaseId().equals(websitStockDTO.getPurchaseId())) {
                    shopStock.setLastCost(websitStockDTO.getPrice());
                }
            }
        }
        return existStockMap;
    }

    private void computeWebsitStock(List<WebsitStockDTO> stockList, Map<String, WebsitStock> existShopStockMap, Object o, List<WebsitStockAcc> shopStockAccList) {
        for (WebsitStockDTO shopStockDTO : stockList) {
            WebsitStock shopStock = existShopStockMap.get(shopStockDTO.getGoodsId());
            WebsitStockAcc shopStockAcc = new WebsitStockAcc();
            shopStockDTO.computeWebsitStock(shopStock, shopStockAcc);
            shopStockAccList.add(shopStockAcc);
        }
    }

    private void txCallUnlock(Lock obtain){
        //事务提交完成后才释放锁,此方法禁止执行mysql相关操作,否则将导致重大问题。
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCompletion(int status) {

                    if(TransactionSynchronization.STATUS_COMMITTED == status){
                        log.info("=========【网点商品库存】======事务提交==============");
                    }else if(TransactionSynchronization.STATUS_ROLLED_BACK == status){
                        log.info("========【网点商品库存】=========事务回滚============");
                    }

                    obtain.unlock();
                }
            });
        } else {
            log.info("【网点商品库存】====没有事务===释放锁");
            obtain.unlock();
        }
    }

    public void txCallUnlockWorker(Lock obtain){
        //事务提交完成后才释放锁,此方法禁止执行mysql相关操作,否则将导致重大问题。
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCompletion(int status) {

                    if(TransactionSynchronization.STATUS_COMMITTED == status){
                        log.info("=========【师傅库存】======事务提交==============");
                    }else if(TransactionSynchronization.STATUS_ROLLED_BACK == status){
                        log.info("========【师傅库存】=========事务回滚============");
                    }

                    obtain.unlock();
                }
            });
        } else {
            log.info("【师傅库存】====没有事务===释放锁");
            obtain.unlock();
        }
    }

    /**
     * 恢复新件销售数量
     * @param
     */
    public void revertNewPartsSalesQty(String companyId, String websitId, String partsWebsitId, String identity,
                                       List<WebsitPartsNewRefundManageRecord> partsNewRefundManageRecordList) throws Exception {
        // 锁定销售单明细
        String lockName = websitId + "|" + partsWebsitId + "|" + identity;
        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.WRITE_SALES_ITEM + lockName);
        if(!obtain.tryLock(10, TimeUnit.SECONDS)){
            throw new RemoteServiceException("销售明细已冻结:" + lockName);
        }
        // 处理销售单明细
        try{
            // 查询销售的明细
            List<WebsitPartsSalesItem> salesOrderItems = materialGoodsStockCMapper.querySalesItemByItemId(companyId, websitId, partsWebsitId,
                    identity, partsNewRefundManageRecordList.stream()
                            .map(WebsitPartsNewRefundManageRecord::getSalesItemId)
                            .collect(Collectors.toList()));

            Map<String, WebsitPartsSalesItem> salesOrderItemMap = salesOrderItems.stream()
                    .collect(Collectors.toMap(WebsitPartsSalesItem::getId, Function.identity()));
            for (WebsitPartsNewRefundManageRecord partsNewRefundManageRecord : partsNewRefundManageRecordList) {
                WebsitPartsSalesItem partsSalesOrderItem = salesOrderItemMap.get(partsNewRefundManageRecord.getSalesItemId());
                partsSalesOrderItem.setNewRefundQty(partsSalesOrderItem.getNewRefundQty().subtract(partsNewRefundManageRecord.getQty()));
                partsSalesOrderItem.setQty(partsSalesOrderItem.getQty().add(partsNewRefundManageRecord.getQty()));
            }

            // 更新销售明细数据
            websitPartsSalesItemService.saveOrUpdateBatch(salesOrderItemMap.values());

        } finally {
            this.txCallUnlockSalesItem(obtain);
        }
    }

    private void txCallUnlockSalesItem(Lock obtain) {
        //事务提交完成后才释放锁,此方法禁止执行mysql相关操作,否则将导致重大问题。
        if (TransactionSynchronizationManager.isActualTransactionActive()) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCompletion(int status) {

                    if(TransactionSynchronization.STATUS_COMMITTED == status){
                        log.info("=========【销售单明细】======事务提交==============");
                    }else if(TransactionSynchronization.STATUS_ROLLED_BACK == status){
                        log.info("========【销售单明细】=========事务回滚============");
                    }

                    obtain.unlock();
                }
            });
        } else {
            log.info("【销售单明细】====没有事务===释放锁");
            obtain.unlock();
        }
    }
}
