package com.fjhx.service.water.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fjhx.base.Condition;
import com.fjhx.base.StorageBaseEntity;
import com.fjhx.constants.StatusConstant;
import com.fjhx.entity.contract.Contract;
import com.fjhx.entity.water.WaterBatch;
import com.fjhx.entity.water.WaterTag;
import com.fjhx.enums.CheckStatusEnum;
import com.fjhx.enums.InTypeEnum;
import com.fjhx.enums.PurchaseProgressEnum;
import com.fjhx.mapper.water.WaterTagMapper;
import com.fjhx.params.water.WaterTagVo;
import com.fjhx.service.contract.ContractService;
import com.fjhx.service.water.WaterBatchService;
import com.fjhx.service.water.WaterTagService;
import com.fjhx.utils.Assert;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.secure.utils.AuthUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 标签 服务实现类
 * </p>
 *
 * @author ${author}
 * @since 2022-09-21
 */
@Service
public class WaterTagServiceImpl extends ServiceImpl<WaterTagMapper, WaterTag> implements WaterTagService {

    // 保证批次统计标签个数和数量正常
    private static final Object BATCH_COUNT_LOCK_OBJECT = new Object();

    @Autowired
    private WaterBatchService waterBatchService;

    @Autowired
    private ContractService contractService;

    @Override
    public Page<Map<String, Object>> getPage(Condition condition) {

        Long waterBatchId = condition.getLong("waterBatchId");
        Integer hadPrinter = condition.getInt("hadPrinter");
        Date beginTime = condition.getBeginTime();
        Date endTime = condition.getEndTime();
        String keyword = condition.getKeyword();

        QueryWrapper<WaterTag> wrapper = Wrappers.<WaterTag>query()
                .between(ObjectUtil.isAllNotEmpty(beginTime, endTime), "wt.create_time", beginTime, endTime)
                .eq(waterBatchId != null, "wt.water_batch_id", waterBatchId)
                .eq(hadPrinter != null, "wt.had_printer", hadPrinter)
                .and(keyword != null, q -> q
                        .like("m.name", keyword).or()
                        .like("m.code", keyword).or()
                        .like("wt.rfid_code", keyword)
                );

        return baseMapper.getPage(condition.getPage(), wrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void add(WaterTagVo waterTagVo) {
        BigDecimal quantity = waterTagVo.getQuantity();
        Assert.notEmpty(quantity, "物料长度不能为空");

        Long waterBatchId = waterTagVo.getWaterBatchId();
        Assert.notEmpty(waterBatchId, "批次id不能为空");

        // 标签数量
        int num = waterTagVo.getNum() == null || waterTagVo.getNum() < 1 ? 1 : waterTagVo.getNum();

        // 获取最新添加标签
        WaterTag waterTag = getNewestWaterTag(waterBatchId);
        int number = waterTag != null ? waterTag.getNumber() : 0;

        List<WaterTag> waterTagList = new ArrayList<>();

        synchronized (BATCH_COUNT_LOCK_OBJECT) {
            // 获取批次详情
            WaterBatch waterBatch = waterBatchService.getById(waterBatchId);
            Assert.notEmpty(waterBatch, "没有找到批次信息");
            // 更新批次标签总个数以及标签总米数
            waterBatchService.editTagCountAndQuantitySum(waterBatch, num, quantity.multiply(BigDecimal.valueOf(num)));

            // 添加标签信息
            for (int i = 1; i <= num; i++) {
                waterTagList.add(createWaterTag(waterBatch, quantity, number + i));
            }

            // 保存标签信息
            saveBatch(waterTagList);

            // 修改合同处理状态为已处理，后续操作不再允许删除合同
            updateContractProcessed(waterBatch);
        }

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void uploadTag(MultipartFile file, Long waterBatchId) {

        Assert.notEmpty(waterBatchId, "批次id不能为空");

        // 解析excel文件中的数据
        List<BigDecimal> quantityList = getQuantityListByExcel(file);

        // 获取此批次最新标签
        WaterTag waterTag = getNewestWaterTag(waterBatchId);

        int number = waterTag != null ? waterTag.getNumber() : 0;

        List<WaterTag> waterTagList = new ArrayList<>();
        BigDecimal tagQuantitySum = BigDecimal.ZERO;


        WaterBatch waterBatch;
        synchronized (BATCH_COUNT_LOCK_OBJECT) {
            // 获取批次详情
            waterBatch = waterBatchService.getById(waterBatchId);
            Assert.notEmpty(waterBatch, "没有找到批次信息");

            for (BigDecimal quantity : quantityList) {
                number++;
                waterTagList.add(createWaterTag(waterBatch, quantity, number));
                tagQuantitySum = tagQuantitySum.add(quantity);
            }

            // 更新批次标签总个数以及标签总米数
            waterBatchService.editTagCountAndQuantitySum(waterBatch, quantityList.size(), tagQuantitySum);

            // 保存标签信息
            saveBatch(waterTagList);

            // 修改合同处理状态为已处理，后续操作不再允许删除合同
            updateContractProcessed(waterBatch);
        }
    }

    @Override
    public void printer(List<Long> tagIdList) {
        update(Wrappers.<WaterTag>lambdaUpdate()
                .in(StorageBaseEntity::getId, tagIdList)
                .set(WaterTag::getHadPrinter, StatusConstant.YES)
                .set(StorageBaseEntity::getUpdateTime, new Date())
                .set(StorageBaseEntity::getUpdateUser, AuthUtil.getUserId())
        );
    }

    @Override
    public void shipment(Long waterBatchId) {
        update(Wrappers.<WaterTag>lambdaUpdate()
                .eq(WaterTag::getWaterBatchId, waterBatchId)
                .set(WaterTag::getHadShipment, StatusConstant.YES)
                .set(WaterTag::getPurchaseProgress, PurchaseProgressEnum.SHIPMENT.getType())
                .set(StorageBaseEntity::getUpdateTime, new Date())
                .set(StorageBaseEntity::getUpdateUser, AuthUtil.getUserId())
        );
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void bindingRfid(Long id, String rfidCode) {

        Date date = new Date();
        Long userId = AuthUtil.getUserId();

        // 原先正在使用的标签变为未使用
        update(Wrappers.<WaterTag>lambdaUpdate()
                .ne(StorageBaseEntity::getId, id)
                .eq(WaterTag::getRfidCode, rfidCode)
                .set(WaterTag::getUseStatus, StatusConstant.NO)
                .set(StorageBaseEntity::getUpdateTime, date)
                .set(StorageBaseEntity::getUpdateUser, userId)
        );

        // 新标签设置为已使用
        update(Wrappers.<WaterTag>lambdaUpdate()
                .eq(StorageBaseEntity::getId, id)
                .set(WaterTag::getRfidCode, rfidCode)
                .set(WaterTag::getPurchaseProgress, PurchaseProgressEnum.LABELING.getType())
                .set(WaterTag::getLabelUserId, userId)
                .set(WaterTag::getUseStatus, StatusConstant.YES)
                .set(StorageBaseEntity::getUpdateTime, date)
                .set(StorageBaseEntity::getUpdateUser, userId)
        );

    }

    @Override
    public Map<String, Object> getDetailsByQrCode(String qrCode) {
        return baseMapper.getDetailsByQrCode(qrCode);
    }

    @Override
    public Map<String, Object> getDetailsByRfid(String rfidCode) {
        return baseMapper.getDetailsByRfid(rfidCode);
    }

    @Override
    public void editInHouse(Enum<?> typeEnum, List<WaterTag> waterTagList, Long stockHouseId) {
        List<Long> waterTagIdList = waterTagList.stream().map(StorageBaseEntity::getId).collect(Collectors.toList());

        // 入库
        if (typeEnum instanceof InTypeEnum) {
            update(Wrappers.<WaterTag>lambdaUpdate()
                    .in(StorageBaseEntity::getId, waterTagIdList)
                    .set(WaterTag::getInHouse, StatusConstant.YES)
                    .set(WaterTag::getStockHouseId, stockHouseId)
                    .set(WaterTag::getPurchaseProgress, PurchaseProgressEnum.WAREHOUSING.getType())
                    .set(StorageBaseEntity::getUpdateTime, new Date())
                    .set(StorageBaseEntity::getUpdateUser, AuthUtil.getUserId())
            );
        } else {
            update(Wrappers.<WaterTag>lambdaUpdate()
                    .in(StorageBaseEntity::getId, waterTagIdList)
                    .set(WaterTag::getInHouse, StatusConstant.NO)
                    .set(WaterTag::getStockHouseId, null)
                    .set(StorageBaseEntity::getUpdateTime, new Date())
                    .set(StorageBaseEntity::getUpdateUser, AuthUtil.getUserId())
            );
        }

    }

    @Override
    public List<WaterTag> getInHouseTagList(WaterTag waterTag) {
        Long stockHouseId = waterTag.getStockHouseId();
        Long materialId = waterTag.getMaterialId();

        Assert.notEmpty(stockHouseId, "仓库id不能为空");
        Assert.notEmpty(materialId, "物料id不能为空");
        
        return list(q -> q
                .eq(WaterTag::getStockHouseId, stockHouseId)
                .eq(WaterTag::getMaterialId, materialId)
                .eq(WaterTag::getInHouse, StatusConstant.YES)
                .eq(WaterTag::getUseStatus, StatusConstant.YES)
        );
    }

    /**
     * 解析excel文件中的数据
     */
    private List<BigDecimal> getQuantityListByExcel(MultipartFile file) {

        List<BigDecimal> quantityList = new ArrayList<>();
        InputStream inputStream = null;
        Workbook workbook = null;
        try {

            inputStream = file.getInputStream();
            String filename = file.getOriginalFilename();
            assert filename != null;

            if (filename.endsWith(".xls")) {
                workbook = new XSSFWorkbook(inputStream);
            } else if (filename.endsWith(".xlsx")) {
                workbook = new XSSFWorkbook(inputStream);
            } else {
                throw new ServiceException("文件格式错误");
            }

            // 取到excel 中的第一张工作表
            Sheet sheet = workbook.getSheetAt(0);
            if (sheet == null) {
                throw new ServiceException("excel表格读取失败");
            }

            // 行
            List<Integer> lineList = Arrays.asList(12, 15, 18, 21, 24, 27, 30, 33, 36, 39);

            // 列
            List<Integer> columnList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

            for (Integer line : lineList) {

                // 获取到这一行的数据
                Row row = sheet.getRow(line);
                if (row == null) {
                    continue;
                }

                // 循环列
                for (Integer column : columnList) {
                    Cell cell = row.getCell(column);
                    if (cell != null) {
                        cell.setCellType(CellType.STRING);
                        BigDecimal quantity = Convert.toBigDecimal(cell.getStringCellValue().trim());
                        if (quantity != null && quantity.compareTo(BigDecimal.ZERO) > 0) {
                            // 获取数量
                            quantityList.add(quantity);
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("excel表格读取失败");
        } finally {
            // 关闭io流
            IoUtil.close(inputStream);
            IoUtil.close(workbook);
        }

        Assert.gtZero(quantityList.size(), "导入标签数量为空");

        return quantityList;
    }


    /**
     * 创建标签
     *
     * @param waterBatch 批次信息
     * @param quantity   数量
     * @param number     二维码编码
     */
    private WaterTag createWaterTag(WaterBatch waterBatch, BigDecimal quantity, Integer number) {

        WaterTag waterTagTemp = new WaterTag();
        waterTagTemp.setMaterialId(waterBatch.getMaterialId());
        waterTagTemp.setQuantity(quantity);
        waterTagTemp.setPrice(waterBatch.getPrice());
        waterTagTemp.setQrCode(UUID.fastUUID().toString());
        waterTagTemp.setContractId(waterBatch.getContractId());
        waterTagTemp.setContractDetailsId(waterBatch.getContractDetailsId());
        waterTagTemp.setWaterBatchId(waterBatch.getId());
        waterTagTemp.setWaterBatchCode(waterBatch.getCode());
        waterTagTemp.setNumber(number);
        waterTagTemp.setHadPrinter(StatusConstant.NO);
        waterTagTemp.setHadShipment(StatusConstant.NO);
        waterTagTemp.setPurchaseProgress(PurchaseProgressEnum.CODING.getType());
        waterTagTemp.setCheckStatus(CheckStatusEnum.NO_QUALITY_INSPECTION.getType());
        waterTagTemp.setInHouse(StatusConstant.NO);
        waterTagTemp.setUseStatus(StatusConstant.NO);
        return waterTagTemp;
    }

    /**
     * 获取最新添加标签
     */
    private WaterTag getNewestWaterTag(Long waterBatchId) {
        return getOne(Wrappers.<WaterTag>lambdaQuery()
                .eq(WaterTag::getWaterBatchId, waterBatchId)
                .orderByDesc(WaterTag::getNumber)
                .last("limit 1"));
    }

    /**
     * 修改合同处理状态为已处理，后续操作不再允许删除合同
     *
     * @param waterBatch 批次信息
     */
    private void updateContractProcessed(WaterBatch waterBatch) {
        if (waterBatch.getTagCount() == 0) {
            contractService.update(Wrappers.<Contract>lambdaUpdate()
                    .eq(StorageBaseEntity::getId, waterBatch.getContractId())
                    .eq(Contract::getProcessed, StatusConstant.NO)
                    .set(Contract::getProcessed, StatusConstant.YES)
            );
        }
    }

}
