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;

    /**
     * @param req - HTTP 요청 객체
     * @param res - HTTP 응답 객체
     * @return ResponseEntity - 로그아웃 응답 결과
     *
     * 로그아웃 - 세션/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");
                }
            }

            log.info("로그아웃 시작 - 사용자: {}, 모드: {}", mbrId, loginMode);

            // 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. 공통 정리 작업
            performCommonCleanup(req, res);

            log.info("로그아웃 완료 - 사용자: {}", mbrId);
            return resUtil.successRes(dbResult, MessageCode.LOGOUT_SUCCESS);

        } catch (Exception e) {
            log.error("로그아웃 처리 중 오류 발생 - 사용자: {}, 오류: {}", mbrId, e.getMessage(), e);

            // 오류가 발생해도 기본 정리는 수행
            try {
                performCommonCleanup(req, res);
            } catch (Exception cleanupError) {
                log.error("정리 작업 중 오류: {}", cleanupError.getMessage());
            }

            return resUtil.successRes(0, MessageCode.LOGOUT_SUCCESS); // 클라이언트에는 성공으로 응답
        }
    }

    /**
     * 세션 모드 로그아웃 처리
     */
    private void handleSessionLogout(HttpServletRequest req, HttpServletResponse res, String mbrId) {
        try {
            // 1. 현재 세션 무효화
            HttpSession session = req.getSession(false);
            if (session != null) {
                try {
                    session.invalidate();
                    log.debug("세션 무효화 완료: {}", session.getId());
                } catch (IllegalStateException e) {
                    log.debug("이미 무효화된 세션: {}", e.getMessage());
                }
            }

            // 2. SessionUtil에서 제거
            if (mbrId != null) {
                sessionUtil.removeSession(mbrId);
            }

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

            // 4. 세션 쿠키 제거
            removeSessionCookies(res);

        } catch (Exception e) {
            log.error("세션 모드 로그아웃 처리 중 오류: {}", e.getMessage(), e);
        }
    }

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

            // 2. JWT 관련 쿠키 제거
            removeJWTCookies(res);

        } catch (Exception e) {
            log.error("JWT 모드 로그아웃 처리 중 오류: {}", e.getMessage(), e);
        }
    }

    /**
     * Redis 세션 데이터 정리
     */
    private void cleanupSessionRedisData(String mbrId) {
        try {
            // 세션 토큰 키 삭제
            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);
                log.debug("사용자별 Redis 키 삭제: {}", userKeys);
            }

        } catch (Exception e) {
            log.error("Redis 세션 데이터 정리 중 오류: {}", e.getMessage(), e);
        }
    }

    /**
     * 세션 관련 쿠키 제거
     */
    private void removeSessionCookies(HttpServletResponse res) {
        String[] sessionCookies = {"JSESSIONID", "SESSION"};

        for (String cookieName : sessionCookies) {
            // 기본 경로
            Cookie cookie = new Cookie(cookieName, null);
            cookie.setMaxAge(0);
            cookie.setPath("/");
            res.addCookie(cookie);

            // 도메인별 쿠키도 삭제 시도
            Cookie domainCookie = new Cookie(cookieName, null);
            domainCookie.setMaxAge(0);
            domainCookie.setPath("/");
            // domainCookie.setDomain(".example.com"); // 필요시 도메인 설정
            res.addCookie(domainCookie);
        }
    }

    /**
     * JWT 관련 쿠키 제거
     */
    private void removeJWTCookies(HttpServletResponse res) {
        String[] jwtCookies = {"refresh", "Authorization", "access_token"};

        for (String cookieName : jwtCookies) {
            Cookie cookie = new Cookie(cookieName, null);
            cookie.setMaxAge(0);
            cookie.setHttpOnly(true);
            cookie.setPath("/");
            res.addCookie(cookie);
        }
    }

    /**
     * OAuth2 관련 쿠키 제거
     */
    private void removeOAuth2Cookies(HttpServletResponse res) {
        String[] oauthCookies = {
                "oauth_access_token", "oauth_refresh_token", "oauth_state",
                "OAUTH2_AUTHORIZATION_REQUEST", "oauth2_auth_request"
        };

        for (String cookieName : oauthCookies) {
            Cookie cookie = new Cookie(cookieName, null);
            cookie.setMaxAge(0);
            cookie.setPath("/");
            res.addCookie(cookie);
        }
    }

    /**
     * 공통 정리 작업
     */
    private void performCommonCleanup(HttpServletRequest req, HttpServletResponse res) {
        try {
            // 1. SecurityContext 제거
            SecurityContextHolder.clearContext();

            // 2. OAuth2 쿠키 제거
            removeOAuth2Cookies(res);

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

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

        } catch (Exception e) {
            log.error("공통 정리 작업 중 오류: {}", e.getMessage(), e);
        }
    }

    /**
     * @param req - HTTP 요청 객체
     * @param res - HTTP 응답 객체
     * @return ResponseEntity - 토큰 재발급 응답 결과
     *
     * 토큰 재발급
     */
    @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) {
            log.error("토큰 재발급 중 오류: {}", e.getMessage(), e);
            return resUtil.errorRes(MessageCode.JWT_EXPIRED);
        }
    }
}