package com.njzscloud.common.cache;

import com.njzscloud.common.redis.util.Redis;
import io.lettuce.core.SetArgs;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Supplier;

@SuppressWarnings("unchecked")
public class SecondCache implements Cache {

    private final ConcurrentHashMap<String, ReentrantReadWriteLock> LOCKS = new ConcurrentHashMap<>();
    private final long timeout;

    public SecondCache(long timeout) {
        this.timeout = timeout;
    }


    public <T> T get(String key) {
        ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
        lock.readLock().lock();
        try {
            return Redis.get(key);
        } finally {
            LOCKS.remove(key);
            lock.readLock().unlock();
        }
    }

    @Override
    public <T> T get(String key, Supplier<T> supplier) {
        Object o = Redis.get(key);
        if (o != null) return (T) o;

        ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
        lock.writeLock().lock();
        try {
            o = Redis.get(key);
            if (o != null) return (T) o;
            o = supplier.get();
            if (o != null) Redis.set(key, o);
            return (T) o;
        } finally {
            LOCKS.remove(key);
            lock.writeLock().unlock();
        }
    }

    @Override
    public <T> T get(String key, long timeout, Supplier<T> supplier) {
        Object o = Redis.get(key);
        if (o != null) return (T) o;

        ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
        lock.writeLock().lock();
        try {
            o = Redis.get(key);
            if (o != null) return (T) o;
            o = supplier.get();
            if (o != null) Redis.set(key, o, new SetArgs().ex(timeout));
            return (T) o;
        } finally {
            LOCKS.remove(key);
            lock.writeLock().unlock();
        }
    }

    public void put(String key, Object value, long timeout) {
        ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
        lock.writeLock().lock();
        try {
            Redis.set(key, value, new SetArgs().ex(timeout));
        } finally {
            LOCKS.remove(key);
            lock.writeLock().unlock();
        }
    }

    public void put(String key, Object value) {
        ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
        lock.writeLock().lock();
        try {
            if (timeout > 0) Redis.set(key, value, new SetArgs().ex(timeout));
            else Redis.set(key, value);
        } finally {
            LOCKS.remove(key);
            lock.writeLock().unlock();
        }
    }

    public void remove(String key) {
        ReentrantReadWriteLock lock = LOCKS.computeIfAbsent(key, k -> new ReentrantReadWriteLock());
        lock.writeLock().lock();
        try {
            Redis.del(key);
        } finally {
            LOCKS.remove(key);
            lock.writeLock().unlock();
        }
    }
}
