package com.jwipc.sys.client.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jwipc.constant.FlagConst;
import com.jwipc.sys.client.SequenceClient;
import com.jwipc.sys.entity.Sequence;
import com.jwipc.sys.service.ISequenceService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.Collections;
import java.util.Date;
import java.util.Objects;

import static com.jwipc.constant.SequenceConfigConst.REDIS_KEY_PREFIX;
import static com.jwipc.constant.SequenceConfigConst.REDIS_TYPE;

/**
 * <p> </p>
 *
 * @author ZhangJie
 * @version 1.0
 * @since JDK 1.8
 */
@Service
@RequiredArgsConstructor
public class SequenceClientImpl implements SequenceClient {
    private final ISequenceService sequenceService;
    private final StringRedisTemplate redisTemplate;
    private RedisScript<Long> resetScript;
    private RedisScript<Long> incrementScript;

    @PostConstruct
    public void init() {
        // Lua script to check and set the initial value and expiration atomically for resetting case
        String resetLuaScript = "if redis.call('exists', KEYS[1]) == 0 then " +
                "redis.call('set', KEYS[1], 1); " +
                "redis.call('expire', KEYS[1], ARGV[1]); " +
                "return 1; " +
                "else " +
                "return redis.call('incr', KEYS[1]); " +
                "end";
        resetScript = RedisScript.of(resetLuaScript, Long.class);

        // Lua script to only increment the value without expiration
        String incrementLuaScript = "if redis.call('exists', KEYS[1]) == 0 then " +
                "redis.call('set', KEYS[1], 1); " +
                "return 1; " +
                "else " +
                "return redis.call('incr', KEYS[1]); " +
                "end";
        incrementScript = RedisScript.of(incrementLuaScript, Long.class);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public String createSequenceNo(String code) {
        Sequence entity = sequenceService.getOne(new LambdaQueryWrapper<Sequence>().eq(Sequence::getCode, code));
        StringBuilder str = new StringBuilder(
                StrUtil.isNotEmpty(entity.getPrefix()) ? entity.getPrefix() : StrUtil.EMPTY);
        switch (entity.getInfixRule()) {
            case 0:
                str.append(DateUtil.format(new Date(), "yyyyMM"));
                break;
            case 1:
                str.append(DateUtil.format(new Date(), "yyyyMMdd"));
                break;
            case 2:
                str.append(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
                break;
            case 3:
                str.append(System.currentTimeMillis() / 1000);
                break;
            case 4:
                str.append(System.currentTimeMillis());
                break;
            case 6:
                str.append(DateUtil.format(new Date(), "yyMM"));
                break;
            case 7:
                str.append(DateUtil.format(new Date(), "yy"));
                break;
            case 8:
                str.append(DateUtil.format(new Date(), "yyyy"));
                break;
            default:
                break;
        }
        //执行数据库函数获取后缀
        //判断是哪种类型 1:数据库;2:Redis
        final Integer type = entity.getType();
        if (Objects.equals(type, REDIS_TYPE)) {
            final String key = REDIS_KEY_PREFIX + entity.getCode();
            Long suffixStr;
            if (entity.getZeroingFlag().equals(FlagConst.Y.getCode())) {
                // Calculate the remaining time until midnight
                LocalTime now = LocalTime.now();
                long secondsUntilMidnight = 86400 - now.toSecondOfDay();
                // Execute the Lua script for resetting daily
                suffixStr = redisTemplate.execute(resetScript, Collections.singletonList(key),
                        String.valueOf(secondsUntilMidnight));
            } else {
                // Execute the Lua script for simple increment without reset
                suffixStr = redisTemplate.execute(incrementScript, Collections.singletonList(key));
            }

            //长度不够前置0，保证编码的长度一样
            str.append(String.format("%0" + entity.getSuffixLength() + "d", suffixStr));
        } else {
            Integer suffixStr = sequenceService.getSequenceSuffix(entity.getCode());
            //长度不够前置0，保证编码的长度一样
            str.append(String.format("%0" + entity.getSuffixLength() + "d", suffixStr));
        }
        return str.toString();
    }

    @Override
    public String createfinanceSequenceNo(String code) {
        return createSequenceNo(code);
    }

    public String createPurchaseSaleSequenceNo(String code) {
        Sequence entity = sequenceService.getOne(new LambdaQueryWrapper<Sequence>().eq(Sequence::getCode, code));
        StringBuilder str = new StringBuilder(
                StrUtil.isNotEmpty(entity.getPrefix()) ? entity.getPrefix() : StrUtil.EMPTY);
        switch (entity.getInfixRule()) {
            case 0:
                str.append(DateUtil.format(new Date(), "yyyyMM"));
                break;
            case 1:
                str.append(DateUtil.format(new Date(), "yyyyMMdd"));
                break;
            case 2:
                str.append(DateUtil.format(new Date(), "yyyyMMddHHmmss"));
                break;
            case 3:
                str.append(System.currentTimeMillis() / 1000);
                break;
            case 4:
                str.append(System.currentTimeMillis());
                break;
            case 6:
                str.append(DateUtil.format(new Date(), "yyMM"));
                break;
            case 7:
                str.append(DateUtil.format(new Date(), "yy"));
                break;
            case 8:
                str.append(DateUtil.format(new Date(), "yyyy"));
                break;
            default:
                break;
        }
        //执行数据库函数获取后缀
        //判断是哪种类型 1:数据库;2:Redis
        final Integer type = entity.getType();
        if (Objects.equals(type, REDIS_TYPE)) {
            final String key = REDIS_KEY_PREFIX + entity.getCode();
            Long suffixStr;
            if (entity.getZeroingFlag().equals(FlagConst.Y.getCode())) {
                // Calculate the remaining time until midnight
                LocalTime now = LocalTime.now();
                long secondsUntilMidnight = 86400 - now.toSecondOfDay();
                // Execute the Lua script for resetting daily
                suffixStr = redisTemplate.execute(resetScript, Collections.singletonList(key),
                        String.valueOf(secondsUntilMidnight));
            } else {
                // Execute the Lua script for simple increment without reset
                suffixStr = redisTemplate.execute(incrementScript, Collections.singletonList(key));
            }

            //长度不够前置0，保证编码的长度一样
            str.append(String.format("%0" + entity.getSuffixLength() + "d", suffixStr));
        } else {
            Integer suffixStr = sequenceService.getSequenceSuffix(entity.getCode());
            //长度不够前置0，保证编码的长度一样
            str.append(String.format("%0" + entity.getSuffixLength() + "d", suffixStr));
        }
        return str.toString();
    }

    @Override
    public String createDeliverySequenceNo(String code) {
        return createSequenceNo(code);
    }

    @Override
    public String createBarCodeSequenceNo(String code) {
        return createSequenceNo(code);
    }

    /**
     * 生成条件打印序列号，生成送货单序列号,预约看板, 预约记录编号
     * @param code
     * @param len
     * @return
     */
    @Override
    public String getBarCodeSequenceNo(String code, Integer len , boolean agent) {
        final String key = REDIS_KEY_PREFIX + code;
        // Calculate the remaining time until midnight
        LocalTime now = LocalTime.now();
        long secondsUntilMidnight = 86400 - now.toSecondOfDay();
        // Execute the Lua script for resetting daily
        Long suffixStr = redisTemplate.execute(resetScript, Collections.singletonList(key),
                String.valueOf(secondsUntilMidnight));
        switch (code) {
            case "appointment_code" :
                String appointment_code =  String.format("%0" + len + "d", suffixStr);
                appointment_code = "YY" + DateUtil.format(new Date(), "yyyyMMdd") + appointment_code;
                return appointment_code;
            case "demo_delivery_code" :
                String demo_delivery_code =  String.format("%0" + len + "d", suffixStr);
                demo_delivery_code = DateUtil.format(new Date(), "yyyyMMdd") + demo_delivery_code;
                return demo_delivery_code;
            case "purchase_order_batch" :
                String purchase_order_batch_no =  String.format("%0" + len + "d", suffixStr);
                purchase_order_batch_no = DateUtil.format(new Date(), "yyyyMMdd") + purchase_order_batch_no;
                return purchase_order_batch_no;
            case "delivery_seq" :
                String sequenceNo =  String.format("%0" + len + "d", suffixStr);
                /**
                 * 九立，朗华 物流商创建的预约单 H 开头
                 * 其他供应商预约单 C前缀
                 * 例如： C年（2码）月（一码）日（两码）流水号（5码）-日清空
                 */
                String prefix = "C";
                if (agent) {
                    prefix = "H";
                }
                final LocalDate now1 = LocalDate.now();
                // 获取年份的后两位
                int yearLastTwoDigits = now1.getYear() % 100;
                // 获取月份和日期
                int month = now1.getMonthValue();
                String mothStr = String.valueOf(month);
                switch (month) {
                    case 10:
                        mothStr = "A";
                        break;
                    case 11:
                        mothStr = "B";
                        break;
                    case 12:
                        mothStr = "C";
                        break;
                    default:
                        break;
                }
                String day = String.format("%02d", now1.getDayOfMonth());
                String deliveryCode = StrUtil.format("{}{}{}{}{}", prefix, yearLastTwoDigits, mothStr, day, sequenceNo);
                return deliveryCode;
            case "bar_code_seq" :
                return String.format("%0" + len + "d", suffixStr);
            case "contract_delivery" :
                String contract_delivery_code = String.format("%0" + len + "d", suffixStr);
                return contract_delivery_code = "SH"+ DateUtil.format(new Date(), "yyyyMMdd")+ contract_delivery_code;
            case "contract_return_order" :
                String contract_return_order = String.format("%0" + len + "d", suffixStr);
                return  contract_return_order = "TH"+ DateUtil.format(new Date(), "yyyyMMdd")+ contract_return_order;
            default:
                break;
        }
        //长度不够前置0，保证编码的长度一样
        return String.format("%0" + len + "d", suffixStr);
    }

    /**
     * 生成询价单号， 生成配额单号, 合同编号, 预付款单号， 货到付款单号（申请付款）
     * @param code
     * @param len
     * @return
     */
    @Override
    public String getCodeSequenceNo(String code, Integer len) {
        String key=null;
        Long suffixStr=null;
        if("finance_no".equals(code)|| "contract_code".equals(code)){
            //对账单号按年递增
            String year = DateUtil.format(new Date(), "yy");
            key = REDIS_KEY_PREFIX + code+year;
            suffixStr = redisTemplate.execute(incrementScript, Collections.singletonList(key));
        }else{
            key = REDIS_KEY_PREFIX + code;
            suffixStr = redisTemplate.execute(incrementScript, Collections.singletonList(key));
        }
//        final String key = REDIS_KEY_PREFIX + code;
//        // Calculate the remaining time until midnight
//        Long suffixStr = redisTemplate.execute(incrementScript, Collections.singletonList(key));
        switch (code) {
            case "inquiry_code" :
                String sequenceNo =  String.format("%0" + len + "d", suffixStr);
                sequenceNo = "RFQ" + DateUtil.format(new Date(), "yyyyMMdd") + sequenceNo;
                return sequenceNo;
            case "quota_seq" :
                String quota_No =  String.format("%0" + len + "d", suffixStr);
                quota_No = "PED" + DateUtil.format(new Date(), "yyyyMMdd") + quota_No;
                return quota_No;
            case "contract_code" :
                String contract_code =  String.format("%0" + len + "d", suffixStr);
                contract_code = "JW" + DateUtil.format(new Date(), "yyyyMMdd") + contract_code;
                return contract_code;
            case "payment_advance_apply" :
                String payment_advance_apply_no =  String.format("%0" + len + "d", suffixStr);
                payment_advance_apply_no = "YFK" + DateUtil.format(new Date(), "yyyyMMdd") + payment_advance_apply_no;
                return payment_advance_apply_no;
            case "payment_cod_apply" :
                String payment_cod_apply_no =  String.format("%0" + len + "d", suffixStr);
                payment_cod_apply_no = "PCA" + DateUtil.format(new Date(), "yyyyMMdd") + payment_cod_apply_no;
                return payment_cod_apply_no;
            case "Contract_payment_apply" :
                String payment_apply_no =  String.format("%0" + len + "d", suffixStr);
                payment_apply_no = "CCA" + DateUtil.format(new Date(), "yyyyMMdd") + payment_apply_no;
                return payment_apply_no;
            case "supplier_avl_code" :
                String supplier_avl_code =  String.format("%0" + len + "d", suffixStr);
                supplier_avl_code = "AVL" + DateUtil.format(new Date(), "yyyyMMdd") + supplier_avl_code;
                return supplier_avl_code;
            case "invoiced_apply_code" :
                String invoiced_apply_code =  String.format("%0" + len + "d", suffixStr);
                invoiced_apply_code = "IA" + DateUtil.format(new Date(), "yyyyMMdd") + invoiced_apply_code;
                return invoiced_apply_code;
            case "purchase_sale_no" :
                String purchase_sale_no =  String.format("%0" + len + "d", suffixStr);
                purchase_sale_no = "JW" +   purchase_sale_no;
                return purchase_sale_no;
            case "finance_no" :
                String finance_no =  String.format("%0" + len + "d", suffixStr);
                finance_no = DateUtil.format(new Date(), "yy") + finance_no;
                return finance_no;

            case "deduction_no" :
                String deduction_no =  String.format("%0" + len + "d", suffixStr);
                deduction_no = "JWKK" +  DateUtil.format(new Date(), "yyyyMMdd")+  deduction_no;
                return deduction_no;
            case "zy_supplier_code" :
                String zy_supplier_code =  String.format("%0" + len + "d", suffixStr);
                zy_supplier_code = "SRMZY"+  zy_supplier_code;
                return zy_supplier_code;
            case "payment_advance_refund_transfer_apply" :
               // BG+YYYY（年）+MM（月）+DD（日）+四位流水（0001）
                String payment_advance_refund_transfer_apply_code =  String.format("%0" + len + "d", suffixStr);
                payment_advance_refund_transfer_apply_code = "BG"+ DateUtil.format(new Date(), "yyyyMMdd")+ payment_advance_refund_transfer_apply_code;
                return payment_advance_refund_transfer_apply_code;
            case "contract_delivery" :
                //"SH" + 日期（YYYYMMDD) + 流水(3位)   {SH20251207001}
                String contract_delivery_code =  String.format("%0" + len + "d", suffixStr);
                payment_advance_refund_transfer_apply_code = "SH"+ DateUtil.format(new Date(), "yyyyMMdd")+ contract_delivery_code;
                return payment_advance_refund_transfer_apply_code;
            case "contract_return_order" :
                //"SH" + 日期（YYYYMMDD) + 流水(3位)   {SH20251207001}
                String contract_return_order =  String.format("%0" + len + "d", suffixStr);
                contract_return_order = "TH"+ DateUtil.format(new Date(), "yyyyMMdd")+ contract_return_order;
                return contract_return_order;
            default:
                break;
        }
        //长度不够前置0，保证编码的长度一样
        return String.format("%0" + len + "d", suffixStr);
    }

    @Override
    public String getCodeSequenceNo(String prefix, String code, Integer len) {
        final String key = REDIS_KEY_PREFIX + prefix;
        Long suffixStr = redisTemplate.execute(incrementScript, Collections.singletonList(key));
        switch (code) {
            case "payment_plan_code" :
                String payment_plan_code =  String.format("%0" + len + "d", suffixStr);
                return payment_plan_code;
            default:
                break;
        }
        //长度不够前置0，保证编码的长度一样
        return String.format("%0" + len + "d", suffixStr);
    }

    @Override
    public String createDeliverySequence(String code, Integer len , boolean agent, boolean xhdd,String ddlx) {
        final String key = REDIS_KEY_PREFIX + code;
        LocalTime now = LocalTime.now();
        long secondsUntilMidnight = 86400 - now.toSecondOfDay();
        // Execute the Lua script for resetting daily
//        String sequenceNo =  sequenceClient.createDeliverySequenceNo(DELIVERY_CODE.getCode());
        Long suffixStr = redisTemplate.execute(resetScript, Collections.singletonList(key),
                String.valueOf(secondsUntilMidnight));
        String sequenceNo =  String.format("%0" + len + "d", suffixStr);
        /**
         * H-港料到料（九立，朗华 物流商创建的预约单 H 开头）；
         * Y-样品到料（采购订单类型是Z006）；
         * X-现货到料（采购订单“现货订单”为“是”）；
         * C-国内到料（排除H、Y、X之外的来料）
         * 例如： C年（2码）月（一码）日（两码）流水号（5码）-日清空
         */
        String prefix = "C";
        if (xhdd) {
            prefix = "X";
        }else{
            if (agent) {
                prefix = "H";
            }else{
                if ("Z006".equals(ddlx)||"Z012".equals(ddlx)) {
                    prefix = "Y";
                }
            }
        }
        final LocalDate now1 = LocalDate.now();
        // 获取年份的后两位
        int yearLastTwoDigits = now1.getYear() % 100;
        // 获取月份和日期
        int month = now1.getMonthValue();
        String mothStr = String.valueOf(month);
        switch (month) {
            case 10:
                mothStr = "A";
                break;
            case 11:
                mothStr = "B";
                break;
            case 12:
                mothStr = "C";
                break;
            default:
                break;
        }
        String day = String.format("%02d", now1.getDayOfMonth());
        String deliveryCode = StrUtil.format("{}{}{}{}{}", prefix, yearLastTwoDigits, mothStr, day, sequenceNo);
        return deliveryCode;
    }

}
