package com.bcxin.identify.components;

import com.bcxin.identify.caches.CacheItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.sql.Timestamp;
import java.time.Instant;
import java.util.Collection;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 马拉松期间优化的代码, 但是目前仅用于百度的功能, 用于IM的时候会导致sig的异常
 * todo: 2025-01-02
 */
@Component
public class HotCacheProviderImpl implements HotCacheProvider {
    private static final Logger logger = LoggerFactory.getLogger(HotCacheProviderImpl.class);
    private static HashMap<String, CacheItem> _hotCache = new HashMap<>();

    private final RedisTemplate redisTemplate;

    public HotCacheProviderImpl(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 这边的缓存时间要大于_hotCache的时间
     * @param key
     * @param supplier
     * @param cacheInSeconds
     * @param tClass
     * @return
     * @param <T>
     */
    @Override
    public <T> T get(String key, Supplier<T> supplier, long cacheInSeconds, Class<T> tClass, boolean enableLocalCache) {
        T data = this.get(key,tClass);
        try {
            if (data == null) {
                data = supplier.get();
                if (data != null) {
                    this.put(key, cacheInSeconds, data, enableLocalCache);
                }
            }

            return data;
        } catch (Exception ex) {
            logger.error("缓存获取异常v2:{}", getCacheKey(key), ex);
            return supplier.get();
        }

    }

    @Override
    public <T> T get(String key, Class<T> tClass) {
        T data = null;
        try {
            CacheItem cacheItem = _hotCache.get(key);

            if(cacheItem!=null && !cacheItem.isExpired()){
                data = (T)cacheItem.getData();
            }
        } catch (Exception ex) {
            //todo: 忽略本地缓存的问题
        }

        if (data == null) {
            try {
                CacheItem cacheItem = (CacheItem) this.redisTemplate.opsForValue().get(getCacheKey(key));
                //由于无法取得时间, 因此，直接忽略保存.
                if (cacheItem != null) {
                    putCache2Local(key,cacheItem);
                }else {
                    return null;
                }

                return (T) cacheItem.getData();
            } catch (Exception ex) {
                logger.error("缓存获取异常:{}", getCacheKey(key), ex);
            }
        }

        return data;
    }

    @Override
    public <T> void put(String key, long cacheInSeconds, T data, boolean enableLocalCache) {
        CacheItem cacheItem = CacheItem.create(data, getTimestamp((int) cacheInSeconds));
        try {
            this.redisTemplate.opsForValue().set(getCacheKey(key), cacheItem,cacheInSeconds,TimeUnit.SECONDS);
            if (enableLocalCache) {

                putCache2Local(key,cacheItem);
            }
        } catch (Exception ex) {
            logger.error("存储缓存发生异常:{}", getCacheKey(key), ex);
        }
    }

    @Override
    public void delete(String key) {
        try {
            this.redisTemplate.delete(getCacheKey(key));
            removeLocalCache(key);
        } catch (Exception ex) {
            logger.error("缓存删除异常:{}", getCacheKey(key), ex);
        }
    }

    @Override
    public void delete(Collection<String> keys) {
        if (CollectionUtils.isEmpty(keys)) {
            return;
        }

        Collection<String> cacheKeys = keys.stream().map(ii -> getCacheKey(ii)).collect(Collectors.toList());
        try {
            keys.forEach(ix->{
                removeLocalCache(ix);
            });
        } catch (Exception ex) {
            logger.error("批量本地缓存删除异常:{}", cacheKeys, ex);
        }

        try {
            this.redisTemplate.delete(cacheKeys);
        } catch (Exception ex) {
            logger.error("批量缓存删除异常:{}", cacheKeys, ex);
        }
    }

    @Override
    public boolean hasKey(String key) {
        boolean flag = _hotCache.containsKey(key);

        if (flag) {
            return true;
        }

        return this.redisTemplate.hasKey(key);
    }

    private static String getCacheKey(String key) {
        return String.format("lc6:%s", key);
    }

    private Timestamp getTimestamp(int expiredSeconds) {
        Instant now = Instant.now();
        now = now.plusSeconds(expiredSeconds);

        return Timestamp.from(now);
    }

    private synchronized void removeLocalCache(String key) {
        _hotCache.remove(key);
    }

    private synchronized  void putCache2Local(String key,CacheItem data) {
        _hotCache.put(key, data);
    }
}