package org.dromara.app.session;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Slf4j
@Component
@RequiredArgsConstructor
public class AppSessionManager {
    private static final String KEY_INDEX_FMT = "APP:SESS_INDEX:%s";
    private static final String KEY_SESS_FMT = "APP:SESS:%s:%s";
    private static final String CH_EVT_FMT = "APP:SESS_EVT:%s";

    public void register(AppSession s, Duration ttl) {
        String indexKey = indexKey(s.getUserId());
        String sessKey = sessKey(s.getUserId(), s.getSessionId());
        RedisUtils.addCacheSet(indexKey, sessKey);
        RedisUtils.setCacheObject(sessKey, JsonUtils.toJsonString(s), ttl);
        publishEvent(s.getUserId(), "REGISTER", s.getSessionId());
    }

    public List<AppSession> list(Long userId) {
        String indexKey = indexKey(userId);
        Set<String> keys = RedisUtils.getCacheSet(indexKey);
        List<AppSession> out = new ArrayList<>();
        if (keys != null) {
            for (String k : keys) {
                String val = RedisUtils.getCacheObject(k);
                if (val != null) {
                    out.add(JsonUtils.parseObject(val, AppSession.class));
                }
            }
        }
        return out;
    }

    public boolean terminate(Long userId, String sessionId) {
        String indexKey = indexKey(userId);
        String sessKey = sessKey(userId, sessionId);
        RedisUtils.deleteObject(sessKey);
        try {
            org.redisson.api.RSet<String> set = org.dromara.common.redis.utils.RedisUtils.getClient().getSet(indexKey);
            set.remove(sessKey);
        } catch (Exception ignored) {
        }
        publishEvent(userId, "TERMINATE", sessionId);
        return true;
    }

    public boolean heartbeat(Long userId, String sessionId, Duration ttlExtend) {
        String sessKey = sessKey(userId, sessionId);
        String val = RedisUtils.getCacheObject(sessKey);
        if (val == null) return false;
        AppSession s = JsonUtils.parseObject(val, AppSession.class);
        RedisUtils.setCacheObject(sessKey, JsonUtils.toJsonString(s), ttlExtend);
        publishEvent(userId, "HEARTBEAT", sessionId);
        return true;
    }

    public void publishEvent(Long userId, String type, String sessionId) {
        String ch = String.format(CH_EVT_FMT, userId);
        String payload = type + ":" + sessionId;
        RedisUtils.publish(ch, payload);
    }

    private String indexKey(Long userId) {
        return String.format(KEY_INDEX_FMT, userId);
    }
    private String sessKey(Long userId, String sessionId) {
        return String.format(KEY_SESS_FMT, userId, sessionId);
    }
}
