package com.yeejoin.amos.boot.module.jg.biz.utils;

import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.boot.module.jg.api.dto.CodeGenerateDto;
import com.yeejoin.amos.boot.module.jg.api.mapper.CommonMapper;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import com.yeejoin.amos.feign.systemctl.model.RegionModel;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBucket;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Administrator
 */
@Component
@Slf4j
public class CodeUtil {


    private RedissonClient redissonClient;

    private CommonMapper commonMapper;

    /**
     * 特殊独立的区县
     */
    private static String[] EXCLUSION_CITY_REGIONS = {"610403", "610581"};

    private final RedisUtils redisUtils;

    private final Map<String, String> REGIONSEQ_CODE_MAP = new ConcurrentHashMap<>();

    private static final String REGION_SEQ_MAP_KEY = "region:seq_map";

    public CodeUtil(RedissonClient redissonClient, CommonMapper commonMapper, RedisUtils redisUtils) {
        this.redissonClient = redissonClient;
        this.commonMapper = commonMapper;
        this.redisUtils = redisUtils;
    }


    /**
     * 按照特种设备代码规范生成 设备代码
     *
     * @param codeGenerateDto 生成代码需要的数据
     * @return 设备基本代码（4位）+ 行政区域代码（6位）+制造年份（4位）+ 制造顺序号（5位）
     */
    public String generateEquipmentCode(CodeGenerateDto codeGenerateDto) {
        // 根据区县或者地市级的接收机构代码获取地市级的行政区域代码
        String regionCode = this.getCityRegionCode(codeGenerateDto.getReceiveCompanyCode());
        // 并发锁处理
        String lockKey = this.buildLockKey(codeGenerateDto.getEquipBaseCode(), regionCode, codeGenerateDto.getYear());
        RLock rLock = redissonClient.getLock(lockKey);
        try {
            rLock.lock();
            // 获取当前已经使用过的序列号
            RBucket<Long> rBucket = redissonClient.getBucket(this.buildManufactureNumberKey(lockKey));
            // 初始化逻辑，初始化为0（编号1开始）
            if (rBucket.get() == null) {
                rBucket.set(0L);
            }
            long currentNumber = rBucket.get();
            long nextNumber = currentNumber + 1;
            // 使用后redis序列号+1
            rBucket.set(nextNumber);
            return this.buildEquipmentCode(codeGenerateDto.getEquipBaseCode(), regionCode, codeGenerateDto.getYear(), nextNumber);
        } finally {
            if (rLock.isHeldByCurrentThread()) {
                rLock.unlock();
            }
        }
    }

    public void initCode(CodeGenerateDto codeGenerateDto ,Long intNumber){
        // 根据区县或者地市级的接收机构代码获取地市级的行政区域代码
        String regionCode = this.getCityRegionCode(codeGenerateDto.getReceiveCompanyCode());
        String lockKey = this.buildLockKey(codeGenerateDto.getEquipBaseCode(), regionCode, codeGenerateDto.getYear());
        RBucket<Long> rBucket = redissonClient.getBucket(this.buildManufactureNumberKey(lockKey));
        // 初始化逻辑，初始化为0（编号1开始）
        if(intNumber != null){
            rBucket.set(intNumber);
        } else {
            rBucket.set(0L);
        }
    }

    /**
     * 获取地市级的行政审批区域代码
     *
     * @param receiveCompanyCode 接收机构的行政区域代码
     * @return 地市级行政审批区域代码
     */
    public String getCityRegionCode(String receiveCompanyCode) {
        // 需求《需要注意的是其中的行政区域代码直到地市一级，西咸、杨凌、韩城用自己的》,西咸组织机构独立的所以不需要单独排除
        if (Arrays.asList(EXCLUSION_CITY_REGIONS).contains(receiveCompanyCode)) {
            return receiveCompanyCode;
        } else {
            if (!ValidationUtil.isEmpty(receiveCompanyCode)) {
                String regionSeq = commonMapper.selectCompanyRegionSeq(receiveCompanyCode);
                return this.getRegionCodeByRegionSeq(regionSeq);
//                return commonMapper.selectCityCompanyCode(this.getRegionCodeByRegionSeq(regionSeq), EXCLUSION_CITY_REGIONS);
            }
            return null;
        }
    }

    public String getRegionCodeByRegionSeq(String regionSeq) {
        return REGIONSEQ_CODE_MAP.computeIfAbsent(regionSeq, (k) -> getRegionCodeFromRedisOrRemote(regionSeq));
    }

    /**
     * 从 Redis 或远程接口获取 regionCode
     */
    private String getRegionCodeFromRedisOrRemote(String regionSeq) {
        // 1. 先从 Redis 获取整个 seqToCodeMap
        Map<String, RegionModel> seqToCodeMap = getRegionSeqToCodeMapFromRedis();

        // 2. 如果在 Redis Map 中找到，直接返回
        RegionModel regionModel = seqToCodeMap.get(regionSeq);
        if (!ValidationUtil.isEmpty(regionModel)) {
            return regionModel.getRegionCode().toString();
        }

        // 3. Redis 中没有，调用远程接口获取最新数据
        return getRegionCodeFromRemoteAndRefreshCache(regionSeq);
    }

    /**
     * 从 Redis 获取 seqToCodeMap
     */
    @SuppressWarnings("unchecked")
    private Map<String, RegionModel> getRegionSeqToCodeMapFromRedis() {
        try {
            Object cachedMap = redisUtils.get(REGION_SEQ_MAP_KEY);
            if (cachedMap instanceof Map) {
                return (Map<String, RegionModel>) cachedMap;
            }
        } catch (Exception e) {
            log.warn("从 Redis 获取 seqToCodeMap 失败，将重新加载", e);
        }
        return new HashMap<>();
    }

    /**
     * 调用远程接口获取数据，并刷新 Redis 缓存
     */
    private String getRegionCodeFromRemoteAndRefreshCache(String regionSeq) {
        // 1. 调用远程接口获取完整的树结构数据
        Collection<RegionModel> regionTree = Systemctl.regionClient.queryForTreeByAgencyCodeRightLike(null).getResult();

        // 2. 将树结构转换为 seqToCodeMap
        Map<String, RegionModel> seqToCodeMap = convertRegionTreeToMap(regionTree);

        // 3. 将转换后的 Map 写入 Redis
        try {
            redisUtils.set(REGION_SEQ_MAP_KEY, seqToCodeMap);
            log.info("已更新 Redis 中的 region seqToCodeMap，包含 {} 条记录", seqToCodeMap.size());
        } catch (Exception e) {
            log.error("更新 Redis seqToCodeMap 失败", e);
        }

        // 4. 从新生成的 Map 中查找需要的值
        return Optional.ofNullable(seqToCodeMap.get(regionSeq)).map(RegionModel::getRegionCode).map(String::valueOf).orElse(null);
    }

    /**
     * 将 RegionModel 树结构转换为 Map<sequenceNbr, regionCode>
     */
    private Map<String, RegionModel> convertRegionTreeToMap(Collection<RegionModel> regionTree) {
        Map<String, RegionModel> seqToCodeMap = new HashMap<>();
        for (RegionModel region : regionTree) {
            traverseAndAddToMap(region, seqToCodeMap);
        }
        return seqToCodeMap;
    }

    /**
     * 递归遍历 RegionModel 树，将每个节点的 sequenceNbr 和 regionCode 存入 Map
     */
    private void traverseAndAddToMap(RegionModel region, Map<String, RegionModel> seqToCodeMap) {
        if (region == null) {
            return;
        }

        // 将当前节点存入 Map
        if (region.getSequenceNbr() != null && region.getRegionCode() != null) {
            seqToCodeMap.put(String.valueOf(region.getSequenceNbr()), region);
        }

        // 递归处理子节点
        Collection<RegionModel> children = region.getChildren();
        if (children != null && !children.isEmpty()) {
            for (RegionModel child : children) {
                traverseAndAddToMap(child, seqToCodeMap);
            }
        }
    }

    private String buildEquipmentCode(String equipBaseCode, String regionCode, String year, long nextNumber) {
        String manufactureNumber = longCode2StrCode(nextNumber);
        return String.format("%s%s%s%s", equipBaseCode, regionCode, year, manufactureNumber);
    }

    private String longCode2StrCode(long nextNumber) {
        if (nextNumber >= 100000) {
            char prefix = (char) ('A' + (nextNumber - 100000) / 10000);
            return prefix + "" + String.format("%04d", (nextNumber - 100000) % 10000);
        } else {
            return String.format("%05d", nextNumber);
        }
    }


    private String buildManufactureNumberKey(String lockKey) {
        return "MANUFACTURE_NUMBER_KEY:" + lockKey;
    }

    private String buildLockKey(String equipBaseCode, String regionCode, String year) {
        return equipBaseCode + regionCode + year;
    }
}
