package com.gree.mall.miniapp.logic.material;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gree.mall.miniapp.bean.material.MyDataCountBean;
import com.gree.mall.miniapp.bean.material.stock.WorkerStockAccBean;
import com.gree.mall.miniapp.bean.material.stock.WorkerStockBean;
import com.gree.mall.miniapp.bean.material.stock.WorkerStockDTO;
import com.gree.mall.miniapp.bean.material.stock.WorkerStockDetailBean;
import com.gree.mall.miniapp.bean.user.CurrentCompanyWechat;
import com.gree.mall.miniapp.commonmapper.stock.WorkerStockCMapper;
import com.gree.mall.miniapp.constant.Constant;
import com.gree.mall.miniapp.exception.RemoteServiceException;
import com.gree.mall.miniapp.logic.common.CommonLogic;
import com.gree.mall.miniapp.plus.entity.*;
import com.gree.mall.miniapp.plus.service.*;
import com.gree.mall.miniapp.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.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 师傅的辅材库存
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class WorkerStockLogic {

    private final CommonLogic commonLogic;
    private final WorkerStockService workerStockService;
    private final WorkerStockAccService workerStockAccService;
    private final WebsitGoodsCategoryService websitGoodsCategoryService;
    private final WorkerStockCMapper workerStockCMapper;
    private final WebsitPartsRetService websitPartsRetService;
    private final WebsitNormRecordService websitNormRecordService;
    private final WebsitGoodsService websitGoodsService;
    private final RedisLockRegistry redisLockRegistry;
    /**
     * 我的库存分类
     * @param webistId
     * @param goodsType
     * @return
     */
    public List<WebsitGoodsCategory> websitGoodsCategoryList(String webistId, String goodsType){
        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat();
        List<WebsitGoodsCategory> websitGoodsCategories = workerStockCMapper.queryWebsitGoodsCategoryList(webistId,currentCompanyWechat.getUserId(),  goodsType);
        return websitGoodsCategories;
    }

    /**
     * 我的库存列表
     * @param websitId
     * @param goodsType
     * @param pageNum
     * @param pageSize
     * @return
     */
    public IPage<WorkerStockBean> workerStockList(String websitId,String goodsType,String categoryId,Integer pageNum,Integer pageSize){
        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat();
        IPage<WorkerStockBean> workerStockBeanIPage = workerStockCMapper
                .queryWorkerStockList(new Page(pageNum, pageSize), websitId, currentCompanyWechat.getUserId(), goodsType,categoryId);

        return workerStockBeanIPage;
    }

    /**
     * 我的辅材配件库存详情
     */
    public WorkerStock detail(String id){
        WorkerStock workerStock = workerStockService.getById(id);
        WorkerStockDetailBean workerStockDetailBean = BeanUtil.copyProperties(workerStock, WorkerStockDetailBean.class);
        String unit = Optional.ofNullable(websitGoodsService.getById(workerStock.getGoodsId()))
                .map(WebsitGoods::getGoodsStockUnit)
                .orElse("");
        workerStockDetailBean.setUnit(unit);
        return workerStockDetailBean;
    }


    /**
     * 我的库存明细
     */
    public IPage<WorkerStockAccBean> workerStockAccList(String websitId,String websitGoodsId,String month,String directFlag,Integer pageNum,Integer pageSize){
        String startTime = null;
        String endTime = null;
        if(StringUtils.isNotBlank(month)) {
            startTime = month + "-01";
            endTime = DateUtil.offsetMonth(DateUtil.parseDate(startTime), 1).toDateStr();
        }
        CurrentCompanyWechat currentCompanyWechat = commonLogic.getCurrentCompanyWechat();
        Page page = workerStockAccService.lambdaQuery()
                .eq(StringUtils.isNotBlank(websitId),WorkerStockAcc::getWebsitId, websitId)
                .eq(StringUtils.isNotBlank(directFlag),WorkerStockAcc::getDirectFlag,directFlag)
                .eq(WorkerStockAcc::getWorkerId, currentCompanyWechat.getUserId())
                .eq(WorkerStockAcc::getGoodsId, websitGoodsId)
                .between(startTime != null && endTime != null ,WorkerStockAcc::getOperTime, startTime, endTime)
                .orderByDesc(WorkerStockAcc::getOperTime).page(new Page<>(pageNum, pageSize));

        List<WorkerStockAccBean> workerStockAccBeans = BeanUtil.copyToList(page.getRecords(), WorkerStockAccBean.class);
        for(WorkerStockAccBean workerStockAccBean : workerStockAccBeans){
            WebsitGoods websitGoods = websitGoodsService.getById(workerStockAccBean.getGoodsId());
            if(websitGoods != null){
                workerStockAccBean.setUnit(websitGoods.getGoodsStockUnit());
            }
        }
        page.setRecords(workerStockAccBeans);
        return page;
    }


    public MyDataCountBean myDataCount() {
        CurrentCompanyWechat wechat = commonLogic.getCurrentCompanyWechat();
        MyDataCountBean bean = new MyDataCountBean();
        String curDate = DateUtil.formatDate(DateUtil.date());
        Integer newRefundCount = websitPartsRetService.lambdaQuery()
                .eq(WebsitPartsRet::getType, "NEW")
                .eq(WebsitPartsRet::getFlag, "SUBMIT")
                .eq(WebsitPartsRet::getWorkerId, wechat.getUserId())
                .eq(WebsitPartsRet::getCompanyWechatId, wechat.getCurrentCompanyWechatId())
                .count();
        Integer oldRefundCount = websitPartsRetService.lambdaQuery()
                .eq(WebsitPartsRet::getType, "OLD")
                .eq(WebsitPartsRet::getFlag, "SUBMIT")
                .eq(WebsitPartsRet::getWorkerId, wechat.getUserId())
                .eq(WebsitPartsRet::getCompanyWechatId, wechat.getCurrentCompanyWechatId())
                .count();
        Integer materialSalesCount = websitNormRecordService.query()
                .select("DISTINCT ref_order")
                .eq("goods_type", "M")
                .eq("worker_id", wechat.getUserId())
                .between("create_time", curDate + " 00:00:00", curDate + " 23:59:59")
                .count();
        List<WebsitNormRecord> normRecords1 = websitNormRecordService.lambdaQuery()
                .eq(WebsitNormRecord::getGoodsType, "M")
                .eq(WebsitNormRecord::getWorkerId, wechat.getUserId())
                .between(WebsitNormRecord::getCreateTime, curDate + " 00:00:00", curDate + " 23:59:59")
                .list();
        Integer partsSalesCount = websitNormRecordService.query()
                .select("DISTINCT ref_order")
                .eq("goods_type", "P")
                .eq("worker_id", wechat.getUserId())
                .between("create_time", curDate + " 00:00:00", curDate + " 23:59:59")
                .count();
        List<WebsitNormRecord> normRecords2 = websitNormRecordService.lambdaQuery()
                .eq(WebsitNormRecord::getGoodsType, "P")
                .eq(WebsitNormRecord::getWorkerId, wechat.getUserId())
                .between(WebsitNormRecord::getCreateTime, curDate + " 00:00:00", curDate + " 23:59:59")
                .list();

        double materialSalesAmount = normRecords1.stream().mapToDouble(v -> v.getUseQty().multiply(v.getNormAmount()).doubleValue()).sum();
        double partsSalesAmount = normRecords2.stream().mapToDouble(v -> v.getUseQty().multiply(v.getNormAmount()).doubleValue()).sum();
        bean.setNewRefundCount(newRefundCount);
        bean.setOldRefundCount(oldRefundCount);
        bean.setMaterialSalesCount(materialSalesCount);
        bean.setMaterialSalesAmount(BigDecimal.valueOf(materialSalesAmount).setScale(2, BigDecimal.ROUND_HALF_UP));
        bean.setPartsSalesCount(partsSalesCount);
        bean.setPartsSalesAmount(BigDecimal.valueOf(partsSalesAmount).setScale(2, BigDecimal.ROUND_HALF_UP));
        return bean;
    }


    /**
     * 单个师傅库存处理
     * @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 (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 = workerStockCMapper.queryExistWorkerStockList(workerStockDTO.getCompanyWechatId()
                , workerStockDTO.getWorkerId(), stockList);
        // 全部商品库存
        Map<String, WorkerStock> existWorkerStockMap = this.createNotExistWorkerStock(stockList, existWorkerStockList);
        // 计算商品库存
        List<WorkerStockAcc> workerStockAccList = new ArrayList<>();
        this.computeWorkerStock(stockList, existWorkerStockMap, null, 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<WorkerStock> workerStockList, List<WorkerStockAcc> workerStockAccList) throws Exception {
        for (WorkerStockDTO workerStockDTO : stockList) {
            WorkerStock workerStock = existWorkerStockMap.get(workerStockDTO.getGoodsId());
            WorkerStockAcc workerStockAcc = new WorkerStockAcc();
            workerStockDTO.computeWorkerStock(workerStock, workerStockAcc);
            workerStockAccList.add(workerStockAcc);
        }
    }

    private Map<String, WorkerStock> createNotExistWorkerStock(List<WorkerStockDTO> stockList, List<WorkerStock> existWorkerStockList) {
        // 已存在的商品库存转成Map
        Map<String, WorkerStock> existShopStockMap = existWorkerStockList.stream()
                .collect(Collectors.toMap(WorkerStock::getGoodsId, Function.identity()));

        // 找出不存在的商品库存并存入新商品库存List
        for (WorkerStockDTO workerStockDTO : stockList) {
            WorkerStock workerStock = existShopStockMap.get(workerStockDTO.getGoodsId());
            if (Objects.isNull(workerStock)) {
                workerStock = workerStockDTO.createWorkerStock();
                existShopStockMap.put(workerStockDTO.getGoodsId(), workerStock);
            }
        }
        return existShopStockMap;
    }

    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();
        }
    }
}
