package com.takensoft.cms.token.web;

import com.takensoft.cms.loginPolicy.service.LoginModeService;
import com.takensoft.cms.loginPolicy.service.LoginPolicyService;
import com.takensoft.cms.mber.vo.MberVO;
import com.takensoft.cms.token.service.RefreshTokenService;
import com.takensoft.cms.token.vo.RefreshTknVO;
import com.takensoft.common.message.MessageCode;
import com.takensoft.common.util.ResponseUtil;
import com.takensoft.common.util.SessionUtil;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Set;

/**
 * @author takensoft
 * @since 2024.04.01
 * @modification
 *     since    |    author    | description
 *  2024.04.01  |  takensoft   | 최초 등록
 *  2025.06.02  |  takensoft   | 세션 모드 Redis 정보 삭제 추가
 *
 * RefreshToken 정보 관련 컨트롤러
 */
@RestController
@RequiredArgsConstructor
@Slf4j
public class RefreshTokenController {

    private final ResponseUtil resUtil;
    private final RefreshTokenService refreshTokenService;
    private final LoginPolicyService loginPolicyService;
    private final LoginModeService loginModeService;
    private final SessionUtil sessionUtil;
    private final RedisTemplate<String, String> redisTemplate;

    /**
     * 로그아웃 - 세션/JWT 모드 통합 처리
     */
    @PostMapping(value = "/mbr/logout.json")
    public ResponseEntity<?> logout(HttpServletRequest req, HttpServletResponse res){
        String mbrId = null;
        String loginMode = loginModeService.getLoginMode();

        try {
            // 1. 인증 정보에서 사용자 ID 추출
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (auth != null && auth.getPrincipal() instanceof MberVO) {
                MberVO mber = (MberVO) auth.getPrincipal();
                mbrId = mber.getMbrId();
            }

            // 2. 세션에서 사용자 ID 추출 (인증 정보가 없는 경우)
            if (mbrId == null) {
                HttpSession session = req.getSession(false);
                if (session != null) {
                    mbrId = (String) session.getAttribute("mbrId");
                }
            }

            // 3. DB에서 Refresh 토큰 삭제
            int dbResult = 0;
            if (mbrId != null) {
                RefreshTknVO refresh = new RefreshTknVO();
                refresh.setMbrId(mbrId);
                dbResult = refreshTokenService.delete(req, refresh);
            }

            // 4. 로그인 모드별 처리
            if ("S".equals(loginMode)) {
                handleSessionLogout(req, res, mbrId);
            } else {
                handleJWTLogout(req, res, mbrId);
            }

            // 5. 공통 정리 작업 (모든 쿠키 완전 제거)
            performCompleteCleanup(req, res);

            return resUtil.successRes(dbResult, MessageCode.LOGOUT_SUCCESS);

        } catch (Exception e) {
            // 오류가 발생해도 기본 정리는 수행
            performCompleteCleanup(req, res);
            return resUtil.successRes(0, MessageCode.LOGOUT_SUCCESS); // 클라이언트에는 성공으로 응답
        }
    }

    /**
     * 관리자 시스템 설정 변경시 전체 사용자 로그아웃
     */
    @PostMapping(value = "/mbr/logoutAll.json")
    public ResponseEntity<?> logoutAll(HttpServletRequest req, HttpServletResponse res) {
        try {
            // 1. 모든 세션 무효화
            sessionUtil.invalidateAllSessions();

            // 2. Redis의 모든 인증 관련 데이터 삭제
            clearAllRedisAuthData();

            // 3. 모든 Refresh 토큰 삭제
                refreshTokenService.deleteAll();

            // 4. 현재 요청자도 로그아웃
            performCompleteCleanup(req, res);
            return resUtil.successRes("모든 사용자가 로그아웃되었습니다.", MessageCode.LOGOUT_SUCCESS);

        } catch (Exception e) {
            // 오류가 발생해도 현재 요청자는 로그아웃 처리
            performCompleteCleanup(req, res);
            return resUtil.successRes("로그아웃 처리되었습니다.", MessageCode.LOGOUT_SUCCESS);
        }
    }

    /**
     * 세션 모드 로그아웃 처리
     */
    private void handleSessionLogout(HttpServletRequest req, HttpServletResponse res, String mbrId) {
            // 1. 현재 세션 무효화
            HttpSession session = req.getSession(false);
            if (session != null) {
                    session.invalidate();
            }
            // 2. SessionUtil에서 제거
            if (mbrId != null) {
                sessionUtil.removeSession(mbrId);
            }

            // 3. Redis에서 세션 관련 정보 삭제
            if (mbrId != null) {
                cleanupSessionRedisData(mbrId);
            }
    }

    /**
     * JWT 모드 로그아웃 처리
     */
    private void handleJWTLogout(HttpServletRequest req, HttpServletResponse res, String mbrId) {
            // 1. Redis에서 JWT 정보 삭제 (중복로그인 관리용)
            if (mbrId != null && !loginPolicyService.getPolicy()) {
                redisTemplate.delete("jwt:" + mbrId);
            }
    }

    /**
     * Redis 세션 데이터 정리
     */
    private void cleanupSessionRedisData(String mbrId) {
            // 세션 토큰 키 삭제
            String sessionTokenKey = "session_token:" + mbrId;
            redisTemplate.delete(sessionTokenKey);

            // 세션 키 삭제
            String sessionKey = "session:" + mbrId;
            redisTemplate.delete(sessionKey);

            // 기타 사용자별 Redis 키 패턴 삭제
            Set<String> userKeys = redisTemplate.keys("*:" + mbrId);
            if (userKeys != null && !userKeys.isEmpty()) {
                redisTemplate.delete(userKeys);
            }
    }

    /**
     * 모든 Redis 인증 데이터 정리 (전체 로그아웃용)
     */
    private void clearAllRedisAuthData() {
            String[] globalPatterns = {
                    "session:*",
                    "session_token:*",
                    "jwt:*",
                    "user:*",
                    "auth:*",
                    "refresh:*"
            };

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

    /**
     * 완전한 정리 작업 (모든 쿠키 제거 포함)
     */
    private void performCompleteCleanup(HttpServletRequest req, HttpServletResponse res) {
            // 1. SecurityContext 제거
            SecurityContextHolder.clearContext();

            // 2. 모든 쿠키 완전 제거
            clearAllCookiesCompletely(req, res);

            // 3. 응답 헤더에서 인증 정보 제거
            res.setHeader("Authorization", "");
            res.setHeader("loginMode", "");

            // 4. 캐시 무효화 헤더 설정
            res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate, private");
            res.setHeader("Pragma", "no-cache");
            res.setHeader("Expires", "0");
    }

    /**
     * 모든 쿠키 완전 제거 (확장된 버전)
     */
    private void clearAllCookiesCompletely(HttpServletRequest req, HttpServletResponse res) {
            // 제거할 쿠키 목록 (확장)
            String[] cookieNames = {
                    // 일반 인증 쿠키
                    "refresh", "Authorization", "access_token", "JSESSIONID", "SESSION",

                    // OAuth 관련 쿠키
                    "oauth_access_token", "oauth_refresh_token", "oauth_state",
                    "OAUTH2_AUTHORIZATION_REQUEST", "oauth2_auth_request",

                    // 카카오 관련
                    "kakao_login", "_kadu", "_kadub", "_kalt", "_kawlt", "_kawltea",
                    "_karmt", "_karmts", "_tiara", "_dfs",

                    // 네이버 관련
                    "NID_AUT", "NID_SES", "NID_JKL", "NID_INF", "NID_LOG",

                    // 구글 관련
                    "SACSID", "APISID", "SSID", "HSID", "SID", "1P_JAR",
                    "__Secure-1PAPISID", "__Secure-1PSID", "__Secure-3PAPISID", "__Secure-3PSID",
                    "ACCOUNT_CHOOSER", "LSID", "GAPS",

                    // 기타 소셜 로그인
                    "facebook_login", "twitter_login", "github_login",

                    // 시스템 쿠키
                    "remember-me", "user-session", "auth-token", "login-token", "csrf-token"
            };

            // 다양한 경로와 도메인에서 쿠키 삭제
            String[] paths = { "/", "/oauth2", "/login", "/auth", "/api", "/mbr" };
            String serverName = req.getServerName();
            String[] domains = {
                    null, // 도메인 없음
                    "." + serverName,
                    serverName,
                    ".localhost",
                    "localhost",
                    ".google.com",
                    ".kakao.com",
                    ".naver.com"
            };

            // 각 쿠키를 모든 경로와 도메인 조합으로 삭제
            for (String cookieName : cookieNames) {
                for (String path : paths) {
                    for (String domain : domains) {
                        Cookie cookie = new Cookie(cookieName, "");
                        cookie.setMaxAge(0);
                        cookie.setPath(path);
                        cookie.setHttpOnly(true);

                        if (domain != null && !domain.equals("null") && !domain.isEmpty()) {
                            try {
                                cookie.setDomain(domain);
                            } catch (Exception e) {
                                // 도메인 설정 실패해도 계속 진행
                            }
                        }

                        // HTTPS 환경이면 Secure 설정
                        if (req.isSecure()) {
                            cookie.setSecure(true);
                        }

                        res.addCookie(cookie);
                    }
                }
            }

            // 기존 요청에 있는 모든 쿠키도 제거
            if (req.getCookies() != null) {
                for (Cookie existingCookie : req.getCookies()) {
                    // 기본 경로로 삭제
                    Cookie deleteCookie = new Cookie(existingCookie.getName(), "");
                    deleteCookie.setMaxAge(0);
                    deleteCookie.setPath("/");
                    deleteCookie.setHttpOnly(true);

                    if (req.isSecure()) {
                        deleteCookie.setSecure(true);
                    }

                    res.addCookie(deleteCookie);

                    // 원본 경로로도 삭제 시도
                    if (existingCookie.getPath() != null) {
                        Cookie originalPathCookie = new Cookie(existingCookie.getName(), "");
                        originalPathCookie.setMaxAge(0);
                        originalPathCookie.setPath(existingCookie.getPath());
                        originalPathCookie.setHttpOnly(true);

                        if (existingCookie.getDomain() != null) {
                            try {
                                originalPathCookie.setDomain(existingCookie.getDomain());
                            } catch (Exception e) {
                                // 도메인 설정 실패해도 계속
                            }
                        }

                        if (req.isSecure()) {
                            originalPathCookie.setSecure(true);
                        }

                        res.addCookie(originalPathCookie);
                    }
                }
            }
    }

    /**
     * 토큰 재발급
     */
    @PostMapping("/refresh/tokenReissue.json")
    public ResponseEntity<?> tokenReissue(HttpServletRequest req, HttpServletResponse res) {
        try {
            int result = refreshTokenService.tokenReissueProc(req, res);

            if(result > 0) {
                return resUtil.successRes(result, MessageCode.COMMON_SUCCESS);
            } else {
                return resUtil.errorRes(MessageCode.JWT_EXPIRED);
            }
        } catch (Exception e) {
            return resUtil.errorRes(MessageCode.JWT_EXPIRED);
        }
    }
}