package com.sonic.bear.service.impl;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.sonic.bear.dao.UserAccessLogDao;
import com.sonic.bear.domain.entity.UserAccessLog;
import com.sonic.bear.service.UserAccessLogService;
import com.sonic.bear.utils.RedisKeyUtils;
import com.sonic.common.utils.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * @author code
 */
@Slf4j
@Service
public class UserAccessLogServiceImpl extends ServiceImpl<UserAccessLogDao, UserAccessLog> implements UserAccessLogService, InitializingBean {

    private static volatile Duration DEFAULT_LOCAL_CACHE_SECONDS = Duration.ofSeconds(3 * 60 * 60);

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private LoadingCache<Long, String> isAddLogStatusCache;
    @Autowired
    private RedisLock.RedisWrapper redisWrapper;
    @Autowired
    private RedisKeyUtils redisKeyUtils;

    @Override
    public void addLog(Long userId, String clientCode) {
        try {
            String status = isAddLogStatusCache.get(userId);
            if(StringUtils.isNotEmpty(status)) {
                return;
            }
            //加锁进行保存操作
            RedisLock redisLock = new RedisLock(redisKeyUtils.addAccessLogLockKey(userId), redisWrapper);
            redisLock.tryAcquireRun(() -> {
                //添加数据到日志表中
                save(UserAccessLog.builder().userId(userId).clientCode(clientCode).accessTime(LocalDateTime.now()).build());
                return true;
            });
            //写入redis缓存中 24小时过期
            redisTemplate.opsForValue().set(redisKeyUtils.addAccessLogKey(userId), "1", 6, TimeUnit.HOURS);
        } catch (Exception e) {
            log.error("===> addLog error : ", e);
        }

    }

    @Override
    public void afterPropertiesSet() throws Exception {
        isAddLogStatusCache = Caffeine.newBuilder()
                .expireAfterWrite(DEFAULT_LOCAL_CACHE_SECONDS)
                // 目前活跃用户不到1w，这里最大按50倍计算，设置50w缓存数量
                .maximumSize(500_000)
                .initialCapacity(500_000 / 50)
                .recordStats()
                .build(new CacheLoader<Long, String>() {
                    @Nullable
                    @Override
                    public String load(@NonNull Long key) {
                        return redisTemplate.opsForValue().get(redisKeyUtils.addAccessLogKey(key));
                    }
                });
    }

}
