package com.sw.mes.itemclean.clean.service;

import cn.hutool.core.convert.Convert;
import com.sw.mes.core.api.system.YbFeignClients;
import com.sw.mes.core.context.BaseContextHandler;
import com.sw.mes.core.dto.Classno;
import com.sw.mes.core.dto.materialCleanDto.MaterialCleanModelDto;
import com.sw.mes.core.enums.ItemCleanEnums;
import com.sw.mes.core.response.Result;
import com.sw.mes.core.response.ResultCode;
import com.sw.mes.core.utils.StringUtils;
import com.sw.mes.itemclean.clean.dto.batchNoOperate.BatchNoOperate;
import com.sw.mes.itemclean.clean.dto.boxNoOperate.BoxNoOperate;
import com.sw.mes.redis.server.RedissonUtils;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.internal.util.Assert;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Calendar;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

/**
 * @author liuzemin
 * @date 2022/11/11 13:37
 * @description
 */
@Service
@Slf4j
public class GenerateBusinessNoService {

    @Autowired
    private YbFeignClients ybFeignClients;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    /**
     * @title 生成箱号
     * @description
     * @author liuzemin
     * @updateTime 2022/11/11 13:38
     * @return
     * @throws
     */
    public String generateBoxNo(BoxNoOperate boxNoOperate){
        if (ItemCleanEnums.BoxNoType.默认 == boxNoOperate.getBoxNoType()){

            return generateBoxNoForDefault(boxNoOperate.getValue());

        }else if (ItemCleanEnums.BoxNoType.分选成箱 == boxNoOperate.getBoxNoType()){

            return generateBoxNoForBoxNoType(boxNoOperate.getBoxNoType(), boxNoOperate.getValue());

        }else if (ItemCleanEnums.BoxNoType.清洗箱号 == boxNoOperate.getBoxNoType()){

            return generateBoxNoForBoxNoType(boxNoOperate.getBoxNoType(), boxNoOperate.getValue());

        }else if(ItemCleanEnums.BoxNoType.成品托号 == boxNoOperate.getBoxNoType()){

            return generateBoxNoForGroupNo(boxNoOperate.getBoxNoType());

        }else {
            throw new RuntimeException("未知枚举BoxNoType, 未实现号码生成！");
        }

    }

    private String generateBoxNoForGroupNo(ItemCleanEnums.BoxNoType boxNoType) {
        String pre = boxNoType.getValue() + dateString();
        Result<Classno> result = ybFeignClients.getClassNoByName(BaseContextHandler.getUserClassNo());
        String classno = Optional.of(result).map(Result::getData).map(Classno::getClassnonameeng).orElse("-");
        return pre + classno + genSerialNumber(pre);
    }

    private String generateBoxNoForBoxNoType(ItemCleanEnums.BoxNoType boxNoType, String value) {
        Result<MaterialCleanModelDto> result = ybFeignClients.findMaterialCleanByCode(value);
        if (!result.getCode().equals(ResultCode.SUCCESS.code)) {
            throw new RuntimeException(result.getMessage());
        }
        MaterialCleanModelDto dto = Optional.of(result).map(Result::getData).orElse(null);
        if (dto == null) {
            throw new RuntimeException("不存在"+value+"原料清洗产品模型！");
        }

        return generateBoxNoForBoxNoType(boxNoType, dto);
    }

    /**
     * @param boxNoType {@link com.sw.mes.core.enums.ItemCleanEnums.BoxNoType}
     * @param dto 清洗模型
     * @return
     */
    private String generateBoxNoForBoxNoType(ItemCleanEnums.BoxNoType boxNoType, MaterialCleanModelDto dto) {
        Assert.notNull(dto, "清洗模型不能空");
        String level = dto.getLevel();
        String siliconCode = dto.getSiliconCode();
        if (StringUtils.isBlank(level)){
            throw new RuntimeException(dto.getFinishedCode()+"该原料清洗产品模型：等级信息缺失");
        }
        if (StringUtils.isBlank(siliconCode)){
            throw new RuntimeException(dto.getFinishedCode()+"该原料清洗产品模型：硅料代码信息缺失");
        }

        String pre = boxNoType.getValue() + dateString() + siliconCode + level;

        return pre + genSerialNumber(pre);
    }

    private String generateBoxNoForDefault(String value) {
        return value + genSerialNumber(value);
    }

    /**
     * 批次号生成
     * @param batchNoOperate
     * @return
     */
    public String generateProductBatchNo(BatchNoOperate batchNoOperate){
        if (batchNoOperate.getBatchType().getKey() == ItemCleanEnums.BatchType.原料生产.getKey()){
            return generateProductBatchNo(batchNoOperate.getMaterialCode());
        }else {
            return generateProductBatchNo(batchNoOperate.getBatchType(), batchNoOperate.getMaterialResource(), batchNoOperate.getMaterialCode());
        }
    }

    private String generateProductBatchNo(ItemCleanEnums.BatchType batchType, ItemCleanEnums.MaterialResource materialResource, String materialCode) {
        Result<MaterialCleanModelDto> result = ybFeignClients.findMaterialCleanByCode(materialCode);
        if (!result.getCode().equals(ResultCode.SUCCESS.code)) {
            throw new RuntimeException(result.getMessage());
        }
        MaterialCleanModelDto dto = Optional.of(result).map(Result::getData).orElse(null);
        if (dto == null) {
            throw new RuntimeException("不存在"+materialCode+"原料清洗产品模型！");
        }

        checkMaterialCleanModel(dto); //检验原料清洗模型数据

        return generateBatchNo(batchType, materialResource, dto);
    }

    private String generateBatchNo(ItemCleanEnums.BatchType batchType, ItemCleanEnums.MaterialResource materialResource, MaterialCleanModelDto dto) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(batchType.getExt());
        stringBuilder.append(materialResource.getExt());
        stringBuilder.append(dto.getDopedTypeCode());
        stringBuilder.append(dto.getLevel());
        stringBuilder.append(dto.getSiliconCode());
//        stringBuilder.append(dto.getResistanceRangeCode());
        stringBuilder.append(dateString());
        Result<Classno> result = ybFeignClients.getClassNoByName(BaseContextHandler.getUserClassNo());
        String classno = Optional.of(result).map(Result::getData).map(Classno::getClassnonameeng).orElse("-");
        stringBuilder.append(classno);
        return stringBuilder.toString();
    }


    /**
     * @title 生成生产批次号（废弃）
     * @description
     * @author liuzemin
     * @param code 物料编码
     * @updateTime 2022/11/11 13:39
     * @return
     * @throws
     */
    @Deprecated
    public String generateProductBatchNo(String code){
        Result<MaterialCleanModelDto> result = ybFeignClients.findMaterialCleanByCode(code);
        if (!result.getCode().equals(ResultCode.SUCCESS.code)) {
            throw new RuntimeException(result.getMessage());
        }
        MaterialCleanModelDto dto = Optional.of(result).map(Result::getData).orElse(null);
        if (dto == null) {
            throw new RuntimeException("不存在"+code+"原料清洗产品模型！");
        }
        return generateProductBatchNo(dto);
    }

    /**
     * @title 生成生产批次号（废弃）
     * @description
     * @author liuzemin
     * @param dto 模型
     * @updateTime 2022/11/17 16:46
     * @return
     * @throws
     */
    @Deprecated
    public String generateProductBatchNo(MaterialCleanModelDto dto) {
        checkMaterialCleanModel(dto); //检验原料清洗模型数据
        return generateBatchNo(dto);
    }

    /**
     * 构建生产批次号
     * @param dto
     * @return
     */
    private String generateBatchNo(MaterialCleanModelDto dto) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(dto.getSiliconTypeCode());
        stringBuilder.append(dto.getFactoryCode());
        stringBuilder.append(dto.getDopedTypeCode());
        stringBuilder.append(dto.getLevel());
        stringBuilder.append(dto.getSiliconCode());
        stringBuilder.append(dto.getResistanceRangeCode());
        stringBuilder.append(dateString());
        return stringBuilder.toString();
    }

    /**
     * 生成当日4位流水号-10进制4位
     *
     * @return
     */
    public String genSerialNumber(String key) {
        String serialNumber = redisTemplate.opsForValue().get(key);
        log.info("生成编码前缀:{},当前完整编码:{}", key, serialNumber);
        if(StringUtils.isBlank(serialNumber)){

            RLock rLock = RedissonUtils.getRLock(redissonClient, "&" + key);
            if (RedissonUtils.getRLockExpireTime(rLock, 5L)){
                try {
                    serialNumber="1";
                    redisTemplate.opsForValue().set(key, serialNumber, 90L, TimeUnit.DAYS);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    rLock.unlock();
                }
            }else {
                throw new RuntimeException("流水号初始化中, 请稍后再试");
            }

        } else {

            Long increment = redisTemplate.opsForValue().increment(key);
            redisTemplate.expire(key, 90L, TimeUnit.DAYS);
            serialNumber = Convert.toStr(increment);
            log.info("生成编码前缀:{},重置后的完整编码:{}", key, serialNumber);
        }

        int sequence = Convert.toInt(serialNumber);
        String strSequence = Convert.toStr(sequence);
        if (sequence < 10000) {
            strSequence = StringUtils.leftPad(strSequence, 4, "0");
        } else {
            //strSequence = Integer.toHexString(sequence);
            //if (strSequence.length() > 4){
            throw new RuntimeException("生成 生产批次号的流水号超出4位，无法生成！");
            // }
        }
        return strSequence;
    }

    /**
     * 获取当前时间到第二天凌晨的秒数
     * @return
     */
    private Long getSecondsNextEarlyMorning() {
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.DAY_OF_YEAR, 1);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
    }

    private void checkMaterialCleanModel(MaterialCleanModelDto dto) {
        String siliconTypeCode = dto.getSiliconTypeCode();
        String factoryCode = dto.getFactoryCode();
        String dopedTypeCode = dto.getDopedTypeCode();
        String level = dto.getLevel();
        String siliconCode = dto.getSiliconCode();
        String resistanceRangeCode = dto.getResistanceRangeCode();
        if (StringUtils.isBlank(siliconTypeCode)){
            throw new RuntimeException(dto.getFinishedCode()+"该原料清洗产品模型：硅料种类信息缺失");
        }
        if (StringUtils.isBlank(factoryCode) ){
            throw new RuntimeException(dto.getFinishedCode()+"该原料清洗产品模型：厂家/来源信息缺失");
        }
        if (StringUtils.isBlank(dopedTypeCode)){
            throw new RuntimeException(dto.getFinishedCode()+"该原料清洗产品模型：掺杂类型信息缺失");
        }
        if (StringUtils.isBlank(level)){
            throw new RuntimeException(dto.getFinishedCode()+"该原料清洗产品模型：等级信息缺失");
        }
        if (StringUtils.isBlank(siliconCode)){
            throw new RuntimeException(dto.getFinishedCode()+"该原料清洗产品模型：硅料代码信息缺失");
        }
        if (StringUtils.isBlank(resistanceRangeCode)){
            throw new RuntimeException(dto.getFinishedCode()+"该原料清洗产品模型：电阻范围信息缺失");
        }

    }

    /**
     * 获取日期 例如： 年份：22年
     *               月份：1-9月用数字表示，10-12月用A/B/C表示
     *               日期：1-31号
     *               22A23  --  22年10月23日
     * @return
     */
    private String dateString(){
        Calendar instance = Calendar.getInstance();
        instance.setTime(new Date());

        String yearAll = Convert.toStr(instance.get(Calendar.YEAR));
        String strYear = yearAll.substring(yearAll.length() - 2);

        int intMonth = instance.get(Calendar.MONTH) + 1;
        String strMonth = Convert.toStr(intMonth);
        if (intMonth>9){
            switch (intMonth){
                case 10:
                    strMonth="A";
                    break;
                case 11:
                    strMonth="B";
                    break;
                case 12:
                    strMonth="C";
                    break;
            }
        }

        String strDay = Convert.toStr(instance.get(Calendar.DAY_OF_MONTH));

        //给 1月3日 补充0；3 -> 03
        String supplementZeroStrDay = StringUtils.leftPad(strDay, 2, "0");

        return strYear + strMonth + supplementZeroStrDay;
    }

    /**
     * 返工/分选箱号(分选批号)生成方法
     * PP+年（倒数2位）+月份（两位）+日（两位）+4位流水号；每日00点开始生成流水号
     *
     * @param mark 特殊标识
     * @return
     */
    public String generateBoxNoForUnpack(String mark) {
        return mark + datePPString() + genPPSerialNumber(datePPString());
    }

    /**
     * 拆花篮装箱(批号)生成方法
     *
     * @param productBatchNo 生成批次
     * @param mark           特殊标识
     * @return
     */
    public String generateBoxNoForBasketBox(String productBatchNo, String mark) {
        return productBatchNo + mark + genSerialNumber(productBatchNo + mark);
    }

    /**
     * 生成PP分选号当日4位流水号-10进制4位
     *
     * @return
     */
    public String genPPSerialNumber(String key) {
        String serialNumber = redisTemplate.opsForValue().get(key);
        log.info("生成编码前缀:{},当前完整编码:{}", key, serialNumber);
        if(StringUtils.isBlank(serialNumber)){

            RLock rLock = RedissonUtils.getRLock(redissonClient, "&" + key);
            if (RedissonUtils.getRLockExpireTime(rLock, 5L)){
                try {
                    serialNumber="1";
                    redisTemplate.opsForValue().set(key, serialNumber, 90L, TimeUnit.DAYS);
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    rLock.unlock();
                }
            }else {
                throw new RuntimeException("流水号初始化中, 请稍后再试");
            }
        } else {

            Long increment = redisTemplate.opsForValue().increment(key);
            redisTemplate.expire(key, 90L, TimeUnit.DAYS);
            serialNumber = Convert.toStr(increment);
            log.info("生成编码前缀:{},重置后的完整编码:{}", key, serialNumber);
        }

        int sequence = Convert.toInt(serialNumber);
        String strSequence = Convert.toStr(sequence);
        if (sequence < 10000) {
            strSequence = StringUtils.leftPad(strSequence, 4, "0");
        } else {
            //strSequence = Integer.toHexString(sequence);
            //if (strSequence.length() > 4){
            throw new RuntimeException("生成 生产批次号的流水号超出4位，无法生成！");
            // }
        }
        return strSequence;
    }

    /**
     * 获取日期 例如： 年份：22年
     *               月份：1-12月用A/B/C表示月用数字表示
     *               日期：1-31号
     *               22A23  --  22年10月23日
     * @return
     */
    private String datePPString(){
        Calendar instance = Calendar.getInstance();
        instance.setTime(new Date());

        String yearAll = Convert.toStr(instance.get(Calendar.YEAR));
        String strYear = yearAll.substring(yearAll.length() - 2);

        int intMonth = instance.get(Calendar.MONTH) + 1;
        String strMonth = Convert.toStr(intMonth);
        strMonth = org.apache.commons.lang3.StringUtils.leftPad(strMonth, 2, "0");

        String strDay = Convert.toStr(instance.get(Calendar.DAY_OF_MONTH));

        //给 1月3日 补充0；3 -> 03
        String supplementZeroStrDay = StringUtils.leftPad(strDay, 2, "0");

        return strYear + strMonth + supplementZeroStrDay;
    }

    /**
     * 托号规则：L+基地名称+年倒数两位+月份（2位）+日（2位）+4位流水号（根据日期0点开始计算）
     * @return
     */
    public String generateForGroupNo() {
        return "L" + "TY" + datePPString() + genPPSerialNumber("L" + "TY" + datePPString());
    }
}
