package com.gree.mall.manager.logic.goods;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gree.mall.manager.bean.admin.AdminUserCom;
import com.gree.mall.manager.bean.goods.GoodsMaterialBean;
import com.gree.mall.manager.bean.goods.GoodsMaterialItemSendVO;
import com.gree.mall.manager.bean.goods.GoodsMaterialItemVO;
import com.gree.mall.manager.bean.goods.GoodsMaterialVO;
import com.gree.mall.manager.commonmapper.CommonMapper;
import com.gree.mall.manager.constant.Constant;
import com.gree.mall.manager.enums.IsYesNoEnum;
import com.gree.mall.manager.enums.material.GoodsMaterialTypeEnum;
import com.gree.mall.manager.enums.material.StateEnum;
import com.gree.mall.manager.exception.RemoteServiceException;
import com.gree.mall.manager.logic.common.CommonLogic;
import com.gree.mall.manager.plus.entity.GoodsMaterial;
import com.gree.mall.manager.plus.entity.GoodsMaterialItem;
import com.gree.mall.manager.plus.entity.GoodsMaterialStorage;
import com.gree.mall.manager.plus.service.*;
import com.gree.mall.manager.zfire.bean.ZfireParamBean;
import com.gree.mall.manager.zfire.util.FieldUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.integration.redis.util.RedisLockRegistry;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Service
public class GoodsMaterialLogic {

    private final CommonLogic commonLogic;
    private final CommonMapper commonMapper;
    private final GoodsMaterialService goodsMaterialService;
    private final GoodsMaterialItemService goodsMaterialItemService;
    private final GoodsPurchaseItemService goodsPurchaseItemService;
    private final GoodsCodeService goodsCodeService;
    private final RedisLockRegistry redisLockRegistry;
    private final GoodsMaterialStorageService goodsMaterialStorageService;

    public IPage<GoodsMaterialVO> page(ZfireParamBean zfireParamBean) {
        FieldUtils.supplyParam(zfireParamBean, GoodsMaterialVO.class, commonLogic.getAdminUser());
        IPage<GoodsMaterialVO> page = commonMapper.goodsMaterialList(new Page(zfireParamBean.getPageNum(), zfireParamBean.getPageSize()), zfireParamBean);

        return page;
    }

    public GoodsMaterialBean detail(String id) {
        GoodsMaterialBean bean = new GoodsMaterialBean();
        GoodsMaterial goodsMaterial = goodsMaterialService.getById(id);
        BeanUtils.copyProperties(goodsMaterial, bean);
        List<GoodsMaterialItem> items = goodsMaterialItemService.lambdaQuery()
                .eq(GoodsMaterialItem::getGoodsMaterialId, id)
                .list();
        bean.setItems(items);

//        GoodsStock stock = goodsStockService.lambdaQuery()
//                .eq(GoodsStock::getCompanyWechatId, bean.getCompanyWechatId())
//                .eq(GoodsStock::getGoodsMaterialId, id)
//                .one();
//        if (Objects.nonNull(stock)) {
//            bean.setStockQty(stock.getStockQty());
//        }

        return bean;
    }

    @Transactional(rollbackFor = Exception.class)
    public void add(GoodsMaterialBean bean) throws Exception {
        AdminUserCom adminUser = commonLogic.getAdminUser();
        String goodsId = IdWorker.getIdStr();

        // 检查参数
        this.checkParams(bean, goodsId);

        if (Objects.nonNull(adminUser.getAdminCompanyWechat())) {
            bean.setCompanyWechatId(adminUser.getAdminCompanyWechat().getCompanyWechatId());
            bean.setCompanyWechatName(adminUser.getAdminCompanyWechat().getCompanyName());
        }
        // 检查商品唯一
        this.checkUniqueMaterial(bean);

        GoodsMaterial goods = new GoodsMaterial();
        BeanUtils.copyProperties(bean, goods);
        goods.setId(goodsId);

        // 检查厂物料编码唯一
        if (StringUtils.isNotBlank(goods.getFactoryNo())) {
            checkUniqueFactoryNo(goods.getCompanyWechatId(), goods.getFactoryNo(), "");
        }

        goods.insert();

        goodsMaterialItemService.saveBatch(bean.getItems());
    }

    private void checkUniqueMaterial(GoodsMaterialBean bean) {
        Integer count = goodsMaterialService.lambdaQuery()
                .ne(StringUtils.isNotBlank(bean.getId()), GoodsMaterial::getId, bean.getId())
                .eq(GoodsMaterial::getCompanyWechatId, bean.getCompanyWechatId())
                .eq(GoodsMaterial::getBrandId, bean.getBrandId())
                .eq(GoodsMaterial::getMainId, bean.getMainId())
                .eq(GoodsMaterial::getSmallId, bean.getSmallId())
                .eq(GoodsMaterial::getGoodsName, bean.getGoodsName())
                .eq(GoodsMaterial::getSpecsName, bean.getSpecsName())
                .count();

        if (count > 0) {
            throw new RemoteServiceException("同类品牌、产品大类、产品小类、名称、规格型号的商品已存在, 请检查");
        }
    }

    private void checkUniqueFactoryNo(String companyWechatId, String factoryNo, String goodsId) throws Exception {
        Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.LOCK_GOODS_MATERIAL + companyWechatId);
        try {
            if (obtain.tryLock(5, TimeUnit.SECONDS)) {
                Integer count = goodsMaterialService.lambdaQuery()
                        .eq(GoodsMaterial::getCompanyWechatId, companyWechatId)
                        .eq(GoodsMaterial::getFactoryNo, factoryNo)
                        .ne(StringUtils.isNotBlank(goodsId), GoodsMaterial::getId, goodsId)
                        .count();
                if (count > 0) {
                    throw new RemoteServiceException("厂物料编码已存在, 请检查");
                }
            }
        } finally {
            obtain.unlock();
        }
    }

    private void checkParams(GoodsMaterialBean bean, String goodsId) {
        if (StringUtils.isBlank(bean.getBrandId())) {
            throw new RemoteServiceException("请选择品牌");
        }
        if (StringUtils.isBlank(bean.getMainId())) {
            throw new RemoteServiceException("请选择大类");
        }
        if (StringUtils.isBlank(bean.getSmallId())) {
            throw new RemoteServiceException("请选择小类");
        }
        if (StringUtils.isBlank(bean.getGoodsName())) {
            throw new RemoteServiceException("商品名称不能为空");
        }
        if (StringUtils.isBlank(bean.getSpecsName())) {
            throw new RemoteServiceException("规格型号不能为空");
        }

        if (bean.getIsVirtyual().equals(IsYesNoEnum.NO.getKey())) {
            if (CollectionUtil.isEmpty(bean.getItems())) {
                throw new RemoteServiceException("子物料不能为空");
            }

            for (int i = 0; i < bean.getItems().size(); i++) {
                GoodsMaterialItem item = bean.getItems().get(i);
                if (StringUtils.isBlank(item.getType())) {
                    throw new RemoteServiceException("第" + (i + 1) + "行, 请选择物料类型");
                }
                if (StringUtils.isBlank(item.getName())) {
                    throw new RemoteServiceException("第" + (i + 1) + "行, 请选择名称");
                }
                if (StringUtils.isBlank(item.getUniqueCode())) {
                    throw new RemoteServiceException("第" + (i + 1) + "行, 请选择是否一物一码");
                }
                if (Objects.isNull(item.getQty()) || item.getQty() < 0) {
                    throw new RemoteServiceException("第" + (i + 1) + "行, 请填写有效数量");
                }

                item.setType(StrUtil.trim(item.getType()))
                        .setName(StrUtil.trim(item.getName()))
                        .setGoodsMaterialId(goodsId);
            }

            // 检查子物料唯一
            Map<String, List<GoodsMaterialItem>> subMap = bean.getItems().stream()
                    .collect(Collectors.groupingBy(v -> v.getType() + "-" + v.getName()));
            for (Map.Entry<String, List<GoodsMaterialItem>> entry : subMap.entrySet()) {
                if (entry.getValue().size() > 1) {
                    throw new RemoteServiceException(entry.getKey() + "有重复的物料类型和名称, 请检查！");
                }
            }
        }
    }

    public void update(GoodsMaterialBean bean) throws Exception {
        AdminUserCom adminUser = commonLogic.getAdminUser();
        if (Objects.nonNull(adminUser.getAdminCompanyWechat())) {
            bean.setCompanyWechatId(adminUser.getAdminCompanyWechat().getCompanyWechatId());
            bean.setCompanyWechatName(adminUser.getAdminCompanyWechat().getCompanyName());
        }

        // 检查商品唯一
        this.checkUniqueMaterial(bean);

        GoodsMaterial goodsMaterial = goodsMaterialService.lambdaQuery()
                .eq(GoodsMaterial::getId, bean.getId())
                .eq(GoodsMaterial::getCompanyWechatId, bean.getCompanyWechatId())
                .one();

        if (Objects.isNull(goodsMaterial)) {
            return;
        }

        // 检查厂物料编码唯一
        if (StringUtils.isNotBlank(bean.getFactoryNo())) {
            checkUniqueFactoryNo(goodsMaterial.getCompanyWechatId(), bean.getFactoryNo(), goodsMaterial.getId());
        }

        // 需要检查没有入库单才能修改
//        Integer count = goodsPurchaseItemService.lambdaQuery()
//                .eq(GoodsPurchaseItem::getGoodsMaterialId, bean.getId())
//                .count();
//
//        if (count > 0) {
//            throw new RemoteServiceException("已有商品采购入库单记录, 不能再变动内容");
//        }
//
//        count = goodsCodeService.lambdaQuery()
//                .eq(GoodsCode::getGoodsMaterialId, bean.getId())
//                .count();
//
//        if (count > 0) {
//            throw new RemoteServiceException("已有商品条码记录, 不能再变动内容");
//        }

        // 检查参数
        this.checkParams(bean, goodsMaterial.getId());

        BeanUtils.copyProperties(bean, goodsMaterial);
        goodsMaterial.updateById();

        if (bean.getIsVirtyual().equals(IsYesNoEnum.NO.getKey())) {
            goodsMaterialItemService.lambdaUpdate()
                    .eq(GoodsMaterialItem::getGoodsMaterialId, bean.getId())
                    .remove();
            goodsMaterialItemService.saveBatch(bean.getItems());
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void batchUpdateStatus(List<String> ids, StateEnum stateEnum) {
        AdminUserCom adminUser = commonLogic.getAdminUser();
        if (Objects.isNull(stateEnum)) {
            throw new RemoteServiceException("状态不能为空");
        }
        String companyWechatId = Objects.nonNull(adminUser.getAdminCompanyWechat()) ? adminUser.getAdminCompanyWechat().getCompanyWechatId() : null;
        goodsMaterialService.lambdaUpdate()
                .set(GoodsMaterial::getState, stateEnum.getKey())
                .set(GoodsMaterial::getUpdateBy, adminUser.getJoinNickName())
                .set(GoodsMaterial::getUpdateTime, DateUtil.date())
                .eq(GoodsMaterial::getCompanyWechatId, companyWechatId)
                .in(GoodsMaterial::getId, ids)
                .update();
    }

    /**
     * 获取物料明细（包含内机数量，外机数量，配件数量）
     * @param specsName
     * @param goodsName
     * @param pageNum
     * @param pageSize
     * @return
     */
    public IPage<GoodsMaterialItemVO> queryGoodsMaterialItem(String specsName,String goodsName,Integer pageNum,Integer pageSize){
        AdminUserCom adminUser = commonLogic.getAdminUser();
        Page page = goodsMaterialService.lambdaQuery()
                .in(CollectionUtils.isNotEmpty(adminUser.getCompanyWechatIds()),GoodsMaterial::getCompanyWechatId,adminUser.getCompanyWechatIds())
                .eq(GoodsMaterial::getState, StateEnum.ON.getKey())
                .like(StringUtils.isNotBlank(specsName),GoodsMaterial::getSpecsName, specsName)
                .like(StringUtils.isNotBlank(goodsName),GoodsMaterial::getGoodsName,goodsName)
                .orderByDesc(GoodsMaterial::getCreateTime)
                .page(new Page<>(pageNum, pageSize));
        List<GoodsMaterialItemVO> goodsMaterialItemVOS = BeanUtil.copyToList(page.getRecords(), GoodsMaterialItemVO.class);
        if(CollectionUtils.isEmpty(goodsMaterialItemVOS)){
            return new Page<>(pageNum,pageSize);
        }
        List<String> ids = goodsMaterialItemVOS.stream().map(GoodsMaterialItemVO::getId).distinct().collect(Collectors.toList());
        List<GoodsMaterialItem> list = goodsMaterialItemService.lambdaQuery().in(GoodsMaterialItem::getGoodsMaterialId, ids).list();

        List<GoodsMaterialStorage> goodsMaterialStorageList = goodsMaterialStorageService.lambdaQuery()
                .eq(GoodsMaterialStorage::getCompanyWechatId,adminUser.getCompanyWechatId())
                .list();
        for(GoodsMaterialItemVO bean : goodsMaterialItemVOS){
            //List<GoodsMaterialItem> list = goodsMaterialItemService.lambdaQuery().eq(GoodsMaterialItem::getGoodsMaterialId, bean.getId()).list();
            Integer insideQty = list.stream().filter(v -> v.getType().equals(GoodsMaterialTypeEnum.INSIDE.getKey()) && StringUtils.equals(v.getGoodsMaterialId(),bean.getId()))
                    .map(GoodsMaterialItem::getQty).reduce(Integer::sum).orElse(0);
            Integer outQty = list.stream().filter(v -> v.getType().equals(GoodsMaterialTypeEnum.OUT.getKey()) && StringUtils.equals(v.getGoodsMaterialId(),bean.getId()))
                    .map(GoodsMaterialItem::getQty).reduce(Integer::sum).orElse(0);
            Integer partsQty = list.stream().filter(v -> v.getType().equals(GoodsMaterialTypeEnum.PARTS.getKey()) && StringUtils.equals(v.getGoodsMaterialId(),bean.getId()))
                    .map(GoodsMaterialItem::getQty).reduce(Integer::sum).orElse(0);


            Integer integer = goodsMaterialStorageList.stream().filter(item -> item.getGoodsMaterialId().equals(bean.getId())).map(GoodsMaterialStorage::getStockQty).reduce(Integer::sum)
                    .orElse(0);

            bean.setInsideQty(insideQty);
            bean.setOutQty(outQty);
            bean.setPartsQty(partsQty);
            bean.setStockQty(integer);
        }
        page.setRecords(goodsMaterialItemVOS);
        return page;
    }


    public Page<GoodsMaterialItemSendVO> itemList(String specsName, String type, String name, Integer pageNum, Integer pageSize) {
        List<GoodsMaterial> list = goodsMaterialService.lambdaQuery().eq(GoodsMaterial::getSpecsName, specsName).list();

        Page<GoodsMaterialItem> page = goodsMaterialItemService.lambdaQuery()
                .in(GoodsMaterialItem::getGoodsMaterialId, list.stream().map(GoodsMaterial::getId).collect(Collectors.toList()))
                .like(StringUtils.isNotBlank(type),GoodsMaterialItem::getType, type)
                .like(StringUtils.isNotBlank(name),GoodsMaterialItem::getName, name)
                .page(new Page<>(pageNum, pageSize));

        if (CollectionUtils.isEmpty(page.getRecords()))
            return new Page<>();

        List<GoodsMaterialItemSendVO> goodsMaterialItemVOS = new ArrayList<>();
        for (GoodsMaterialItem record : page.getRecords()) {
            GoodsMaterialItemSendVO goodsMaterialItemSendVO = new GoodsMaterialItemSendVO();
            BeanUtil.copyProperties(record,goodsMaterialItemSendVO);
            goodsMaterialItemSendVO.setItemId(record.getId());
            goodsMaterialItemSendVO.setItemUnit(record.getUnit());
            List<GoodsMaterial> collect = list.stream().filter(item -> item.getId().equals(record.getGoodsMaterialId())).collect(Collectors.toList());
            GoodsMaterial goodsMaterial = collect.get(0);
            BeanUtil.copyProperties(goodsMaterial,goodsMaterialItemSendVO);
            goodsMaterialItemVOS.add(goodsMaterialItemSendVO);
        }

        Page<GoodsMaterialItemSendVO> goodsMaterial = new Page<>();

        goodsMaterial.setRecords(goodsMaterialItemVOS);
        goodsMaterial.setCurrent(page.getCurrent());
        goodsMaterial.setTotal(page.getTotal());
        goodsMaterial.setPages(page.getPages());


        return goodsMaterial;

    }
}
