package me.qwq.doghouse.component;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;

import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.command.CommandAsyncExecutor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.sqids.Sqids;

import me.qwq.doghouse.entity.Comment;
import me.qwq.doghouse.entity.config.SiteConfig;
import me.qwq.doghouse.util.Cryptos;
/**
 * Redis Key Tool
 * @author Doghole
 *
 */
@Component
@Slf4j
public class Rk {

    static SiteConfig siteConfig;
    
    static RedissonClient redisson;
    
    
    public static String SYSTEM_PREFIX;
    
    static final String NOTIFY_KEYSPACE_EVENTS = "notify-keyspace-events",
                        x = "x", E = "E";

    
    @Autowired
    public void setService(SiteConfig siteConfig, RedissonClient redisson) {
        Rk.siteConfig = siteConfig;
        Rk.redisson = redisson;
    }
    
    @PostConstruct
    private void init() {
        
        SYSTEM_PREFIX = "doghouse:" + siteConfig.getAddress().hashCode() + ":";
        
        // 使用 Redisson 设置 redis 服务端的 x(expired) 和 E(evicted) 事件
        Redisson redissonImpl = (Redisson) redisson;
        CommandAsyncExecutor executor = redissonImpl.getCommandExecutor();

        @SuppressWarnings("unchecked")
        Map<String, String> config = (Map<String, String>) executor
                .readAsync("default", StringCodec.INSTANCE, RedisCommands.CONFIG_GET_MAP, NOTIFY_KEYSPACE_EVENTS)
                .toCompletableFuture().join();
        
        String keyEvents = config.getOrDefault(NOTIFY_KEYSPACE_EVENTS, "");
        boolean changed = false;
        if (!keyEvents.contains(x)) {
            keyEvents += x;
            changed = true;
        }
        if (!keyEvents.contains(E)) {
            keyEvents += E;
            changed = true;
        }
        if (changed) {
            log.debug("Redis setConfig {} value {}", NOTIFY_KEYSPACE_EVENTS, keyEvents);
            executor
                .writeAsync("default", StringCodec.INSTANCE, RedisCommands.CONFIG_SET, NOTIFY_KEYSPACE_EVENTS, keyEvents)
                .toCompletableFuture()
                .join();
        }
    }
    
    public static class Mail {
        
        public static final String ID = "mail:";
        
        private static String getMailKey(String subfix) {
            return SYSTEM_PREFIX + ID + subfix;
        }
        
        /**
         * 获取所有过期事件的 key
         * @return
         */
        public static Iterable<String> getAllExpirationTaskKeys() {
            return redisson.getKeys()
                    .getKeysByPattern(getMailKey("task-"));
        }
        
        /**
         * 获取邮件推送任务的 Bucket（不包括管理员邮件）
         * @param commentId
         * @return
         */
        public static RBucket<Object> getTaskBucket(Long commentId) {
            return redisson.getBucket(getMailKey("task-" + commentId));
        }

        /**
         * 获取邮件推送任务的 Comment 缓存 Bucket（不包括管理员邮件）
         * @param commentId
         * @return
         */
        public static RBucket<Comment> getTaskCommentBucket(Long commentId) {
            return redisson.getBucket(getMailKey("task-" + commentId + "-comment"));
        }
        
        /**
         * 获取用户邮件订阅管理 UUID 缓存 Bucket
         * @param mail
         * @return 包含 uuid 的 Bucket，用以建立 email → uuid 的映射关系
         */
        public static RBucket<String> getMgrBucket(String mail) {
            return redisson.getBucket(getMailKey("mgr-addr-" + mail));
        }
        
        /**
         * 获取用户邮件订阅管理邮箱地址缓存 Bucket
         * @param uuid
         * @return 包含 email 的 Bucket，用以建立 uuid → email 的映射关系
         */
        public static RBucket<String> getMgrUuidBucket(String uuid) {
            return redisson.getBucket(getMailKey("mgr-uuid-" + uuid));
        }
        
        /**
         * 获取一个随机 Key 的用于过期事件的 Bucket
         */
        public static RBucket<Object> getRandomExpirationBucket(){
            List<Long> list = new ArrayList<>();
            SecureRandom random = new SecureRandom();
            list.add(Math.abs(random.nextLong()));
            list.add(Math.abs(random.nextLong()));
            return redisson.getBucket(getMailKey(Sqids.builder().build().encode(list)));
        }
    }
    
    public static class Gravatar {

        public static final String ID = "gravatar:";
        
        public static final String GRAVATAR_PREFIX = "image:";
        
        public static final String EMAIL_HASH_PREFIX = "email-hash:";
        
        public static final String PRIVATE_HASH_PREFIX = "private-hash:";
        
        public static final String SALT = "~D09H0U5E-9R4V474R^_^";
        
        private static String getGravatarKey(String subfix) {
            return SYSTEM_PREFIX + ID + subfix;
        }
        
        /**
         * 获取对应 hash 和尺寸 size 的头像缓存 Bucket
         * @param email
         * @param size
         * @return
         */
        public static RBucket<Object> getGravatarBucket(String dogHash, int size) {
            return redisson.getBucket(getGravatarKey(GRAVATAR_PREFIX + dogHash + ":" + size));
        }
        
        /**
         * 清除 gravatar 图片缓存
         */
        public static void clearGravatarCache() {
            for (String name : redisson.getKeys().getKeysByPattern(getGravatarKey(GRAVATAR_PREFIX + "*"))) {
                redisson.getBucket(name).deleteAsync();
            }
        }
        
        /**
         * 获取对应 email 的 hash，并且提前在 Redis 中存储对应关系
         * @param email
         * @return
         */
        public static String prepareDogHash(String email) {
            if (email == null) email = "";
            email = email.toLowerCase(Locale.ENGLISH);
            
            RBucket<String> gravatarHashBucket = redisson.getBucket(getGravatarKey(EMAIL_HASH_PREFIX + email));
            if (gravatarHashBucket.isExists()) {
                return gravatarHashBucket.get();
            }
            
            byte[] hash = Cryptos.sha3((email + SALT).getBytes(), 256);
            
            List<Long> list = new ArrayList<>();
            
            for (int i = 0; i < 4; i++) {
                Long x = 0L;
                for (int j = 0; j < 8; j++) {
                    x |= (((long) hash[i * 8 + j]) & 0xFFL) << ((7 - j) * 8);
                }
                i++;
                for (int j = 0; j < 8; j++) {
                    x ^= (((long) hash[i * 8 + j]) & 0xFFL) << ((7 - j) * 8);
                }
                if (x < 0) {
                    list.add(0L);
                    list.add(Math.abs(x));
                }
                else {
                    list.add(x);
                }
            }
            
            String dogHash = Sqids.builder().build().encode(list);
            gravatarHashBucket.set(dogHash);
            String realHash = Cryptos.sha256(email);
            
            redisson.getBucket(getGravatarKey(PRIVATE_HASH_PREFIX + dogHash))
                .set(realHash);
            
            log.info("Email: {}, real hash: {}, dog hash: {}", email, realHash, dogHash);
            
            return dogHash;
        }
        
        /**
         * 根据 hash 获取 gravatar md5
         * @param dogHash
         * @return
         */
        public static String getRealHash(String dogHash) {
            RBucket<String> email = redisson.getBucket(getGravatarKey(PRIVATE_HASH_PREFIX + dogHash));
            return email.get();
        }
    }
}
