package com.spring.infra.security.handler;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.MediaType;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.spring.infra.security.config.SecurityURI;
import com.spring.infra.security.dto.SignResponse;
import com.spring.infra.security.jwt.JwtTokenService;

import lombok.RequiredArgsConstructor;

/**
 * 사용자 로그인 성공을 처리하는 핸들러 클래스입니다.
 * 
 * <p>이 클래스는 Spring Security의 AuthenticationSuccessHandler 인터페이스를 구현하여,
 * 사용자 인증 성공 시 적절한 처리를 수행합니다.</p>
 * 
 * <p>로그인 성공 시 JWT 토큰을 생성하고, 사용자를 지정된 URL로 리다이렉트합니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@Component
@RequiredArgsConstructor
public class SigninSuccessHandler implements AuthenticationSuccessHandler {

    /**
     * JWT 토큰 서비스를 제공하는 서비스 객체입니다.
     * 
     * <p>사용자 인증 성공 시 JWT 토큰을 생성하는 데 사용됩니다.</p>
     */
    private final JwtTokenService jwtTokenService;

    /**
     * JSON 변환을 위한 ObjectMapper 객체입니다.
     * 
     * <p>로그인 성공 시 사용자 정보를 JSON 형식으로 변환하는 데 사용됩니다.</p>
     */
    private final ObjectMapper objectMapper;

    /**
     * 요청 캐시 객체입니다.
     * 
     * <p>사용자가 인증되기 전에 요청된 URL을 저장하는 데 사용됩니다.</p>
     */
    private RequestCache requestCache = new HttpSessionRequestCache();

    /**
     * 사용자 인증 성공 시 호출되는 메서드입니다.
     * 
     * <p>인증 성공 시 JWT 액세스 토큰과 리프레시 토큰을 생성하고,
     * 사용자를 지정된 URL로 리다이렉트합니다.</p>
     * 
     * @param request HTTP 요청 객체
     * @param response HTTP 응답 객체
     * @param authentication 인증 정보
     * @throws IOException 입출력 예외
     * @throws ServletException 서블릿 예외
     */
    @Override
    public void onAuthenticationSuccess(
        HttpServletRequest request, 
        HttpServletResponse response, 
        Authentication authentication
    ) throws IOException, ServletException {

        // 액세스 토큰 생성
        jwtTokenService.generateAccessToken(response, authentication);
        // 리프레시 토큰 저장
        jwtTokenService.saveRefreshToken(authentication.getName(), jwtTokenService.generateRefreshToken(response, authentication));

        // 요청 캐시에서 저장된 요청을 가져옵니다.
        SavedRequest savedRequest = requestCache.getRequest(request, response);
        String targetUrl = (savedRequest != null) ? savedRequest.getRedirectUrl() : SecurityURI.REDIRECT_URI.getUri();

        // 응답 설정
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.getWriter().write(objectMapper.writeValueAsString(SignResponse.of(true, targetUrl)));
    }
    
}
