package com.njzscloud.supervisory.sys.auth.service;

import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.njzscloud.common.core.fastjson.Fastjson;
import com.njzscloud.common.security.contant.Constants;
import com.njzscloud.common.security.support.ITokenService;
import com.njzscloud.common.security.support.Token;
import com.njzscloud.common.security.support.UserDetail;
import com.njzscloud.supervisory.sys.auth.mapper.SysTokenMapper;
import com.njzscloud.supervisory.sys.auth.pojo.entity.SysTokenEntity;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.concurrent.locks.ReentrantReadWriteLock;

@Service
@RequiredArgsConstructor
public class TokenService implements ITokenService {
    private static final TimedCache<String, UserDetail> TOKEN_CACHE = CacheUtil.newTimedCache(60 * 1000);
    private final SysTokenMapper sysTokenMapper;

    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

    @Override
    public void saveToken(UserDetail userDetail) {
        try {
            writeLock.lock();
            Token token = userDetail.getToken();
            long userId = token.getUserId();
            String tid = token.getTid();
            String key = Constants.TOKEN_CACHE_KEY.fill(userId, tid);
            Long l = sysTokenMapper.selectCount(Wrappers.<SysTokenEntity>lambdaQuery()
                    .eq(SysTokenEntity::getUserId, userId)
                    .eq(SysTokenEntity::getTid, tid));
            if (l > 0) return;
            sysTokenMapper.insert(new SysTokenEntity()
                    .setUserId(userId)
                    .setTid(tid)
                    .setTkey(key)
                    .setTval(token.toString())
                    .setUserDetail(Fastjson.toJsonStr(userDetail))
            );
            TOKEN_CACHE.put(key, userDetail);
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public UserDetail loadUser(String tokenStr) {
        try {
            readLock.lock();
            Token token = Token.create(tokenStr);
            String tid = token.getTid();
            long userId = token.getUserId();
            String key = Constants.TOKEN_CACHE_KEY.fill(userId, tid);
            UserDetail userDetail = TOKEN_CACHE.get(key);
            if (userDetail != null) return userDetail;
            SysTokenEntity sysTokenEntity = sysTokenMapper.selectOne(Wrappers.<SysTokenEntity>lambdaQuery()
                    .eq(SysTokenEntity::getUserId, userId)
                    .eq(SysTokenEntity::getTid, tid));
            if (sysTokenEntity == null) return null;
            userDetail = Fastjson.toBean(sysTokenEntity.getUserDetail(),
                    UserDetail.class);
            TOKEN_CACHE.put(key, userDetail);
            return userDetail;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void removeToken(Token token) {
        try {
            writeLock.lock();
            long userId = token.getUserId();
            String tid = token.getTid();
            String key = Constants.TOKEN_CACHE_KEY.fill(userId, tid);
            TOKEN_CACHE.remove(key);
            sysTokenMapper.delete(Wrappers.<SysTokenEntity>lambdaQuery()
                    .eq(SysTokenEntity::getUserId, userId)
                    .eq(SysTokenEntity::getTid, tid)
            );
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public void removeToken(Long userId) {
        try {
            writeLock.lock();
            ArrayList<String> keys = new ArrayList<>();
            for (UserDetail userDetail : TOKEN_CACHE) {
                Token token = userDetail.getToken();
                if (token.getUserId() == userId) {
                    String key = Constants.TOKEN_CACHE_KEY.fill(userId, token.getTid());
                    keys.add(key);
                }
            }
            for (String key : keys) {
                TOKEN_CACHE.remove(key);
            }
            sysTokenMapper.delete(Wrappers.<SysTokenEntity>lambdaQuery()
                    .eq(SysTokenEntity::getUserId, userId)
            );
        } finally {
            writeLock.unlock();
        }
    }
}
