package com.takensoft.common.util;

import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author takensoft
 * @since 2025.03.21
 * @modification
 *     since    |    author    | description
 *  2025.03.21  |  takensoft   | 최초 등록
 *  2025.05.29  |  takensoft   | Redis 통합 중복로그인 관리
 *  2025.06.09  |  takensoft   | 세션 관리 로직 개선, 안정성 향상
 *
 * 세션 로그인 방식의 유틸리티
 */
@Slf4j
@Component
public class SessionUtil {

    private final Map<String, HttpSession> sessionMap = new ConcurrentHashMap<>();
    private final RedisTemplate<String, String> redisTemplate;

    public SessionUtil(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 세션 등록 - 중복로그인 처리 개선
     */
    public synchronized void registerSession(String mbrId, HttpSession newSession) {

            // 1. 기존 세션 확인 및 무효화
            HttpSession oldSession = sessionMap.get(mbrId);
            if (oldSession != null && !oldSession.getId().equals(newSession.getId())) {
                    oldSession.invalidate();
            }
            // 2. 새 세션을 메모리에 등록
            sessionMap.put(mbrId, newSession);

            // 3. Redis에 세션 정보 저장 (세션 ID 저장)
            String sessionKey = "session:" + mbrId;
            redisTemplate.opsForValue().set(sessionKey, newSession.getId(),
                    Duration.ofSeconds(newSession.getMaxInactiveInterval()));
    }

    /**
     * 세션 ID로 세션 무효화
     */
    public void invalidateSessionById(String sessionId) {
            // 메모리에서 해당 세션 ID를 가진 세션 찾아서 무효화
            String targetMbrId = null;
            sessionMap.entrySet().removeIf(entry -> {
                HttpSession session = entry.getValue();
                if (session != null && session.getId().equals(sessionId)) {
                        session.invalidate();
                        return true;
                }
                return false;
            });
    }

    /**
     * 사용자별 세션 제거 - Redis 연동 개선
     */
    public void removeSession(String mbrId) {
            // 1. 메모리 세션 무효화
            HttpSession session = sessionMap.get(mbrId);
            if (session != null) {
                    session.invalidate();
            }
            sessionMap.remove(mbrId);

            // 2. Redis에서 모든 관련 키 제거
            cleanupRedisSessionData(mbrId);
    }

    /**
     * 전체 세션 무효화 - Redis 연동 개선
     */
    public void invalidateAllSessions() {

            // 1. 모든 메모리 세션 무효화
            int invalidatedCount = 0;
            for (Map.Entry<String, HttpSession> entry : sessionMap.entrySet()) {
                HttpSession session = entry.getValue();
                if (session != null) {
                        session.invalidate();
                        invalidatedCount++;
                }
            }
            sessionMap.clear();

            // 2. Redis에서 모든 세션 관련 키 삭제
            cleanupAllRedisSessionData();
    }

    /**
     * 특정 사용자의 Redis 세션 데이터 정리
     */
    private void cleanupRedisSessionData(String mbrId) {
            // 세션 관련 키들 삭제
            String sessionKey = "session:" + mbrId;
            String sessionTokenKey = "session_token:" + mbrId;

            redisTemplate.delete(sessionKey);
            redisTemplate.delete(sessionTokenKey);

            // 패턴 매칭으로 사용자 관련 모든 세션 키 찾아서 삭제
            Set<String> userSessionKeys = redisTemplate.keys("session*:" + mbrId);
            if (userSessionKeys != null && !userSessionKeys.isEmpty()) {
                redisTemplate.delete(userSessionKeys);
            }
    }

    /**
     * 모든 Redis 세션 데이터 정리
     */
    private void cleanupAllRedisSessionData() {
            // 모든 세션 관련 키 패턴들
            String[] sessionKeyPatterns = {
                    "session:*",
                    "session_token:*"
            };

            for (String pattern : sessionKeyPatterns) {
                Set<String> keys = redisTemplate.keys(pattern);
                if (keys != null && !keys.isEmpty()) {
                    redisTemplate.delete(keys);
                }
            }
    }

    /**
     * 활성 세션 수 조회
     */
    public int getActiveSessionCount() {
        return sessionMap.size();
    }

    /**
     * 특정 사용자의 세션 활성 상태 확인
     */
    public boolean isSessionActive(String mbrId) {
        HttpSession session = sessionMap.get(mbrId);
        if (session == null) {
            return false;
        }

        try {
            // 세션 접근을 통한 유효성 확인
            session.getLastAccessedTime();
            return true;
        } catch (IllegalStateException e) {
            // 무효화된 세션이면 맵에서 제거
            sessionMap.remove(mbrId);
            return false;
        }
    }

}