package com.bio.bio_backend.global.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.bio.bio_backend.global.dto.ApiResponseDto;
import com.bio.bio_backend.domain.base.member.dto.LoginRequestDto;
import com.bio.bio_backend.domain.base.member.dto.LoginResponseDto;
import com.bio.bio_backend.domain.base.member.dto.MemberDto;
import com.bio.bio_backend.domain.base.member.mapper.MemberMapper;
import com.bio.bio_backend.domain.base.member.service.MemberService;
import com.bio.bio_backend.global.constants.ApiResponseCode;
import com.bio.bio_backend.global.utils.JwtUtils;
import com.bio.bio_backend.global.utils.HttpUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.io.IOException;
import java.time.LocalDateTime;

@RequiredArgsConstructor
@Slf4j
public class JwtTokenIssuanceFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;
    private final JwtUtils jwtUtils;
    private final ObjectMapper objectMapper;
    private final MemberService memberService;
    private final HttpUtils httpUtils;
    private final MemberMapper memberMapper;

    // 사용자 login 인증 처리
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            LoginRequestDto requestDto = new ObjectMapper().readValue(request.getInputStream(), LoginRequestDto.class);
            UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                requestDto.getUserId(), requestDto.getPassword());

            return authenticationManager.authenticate(authToken);
        } catch (IOException e) {
            throw new AuthenticationServiceException("로그인 요청 파싱 중 오류가 발생했습니다", e);
        }
    }

    // 사용자 인증 성공 후 token 발급
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                            FilterChain chain, Authentication authResult) throws IOException, ServletException {

        UserDetails userDetails = (UserDetails) authResult.getPrincipal();
        MemberDto member = (MemberDto) userDetails;
        
        // 토큰 생성
        String accessToken = jwtUtils.createAccessToken(member.getOid(), member.getUserId(), member.getName());
        String refreshToken = jwtUtils.createRefreshToken(member.getUserId(), httpUtils.getClientIp());
        
        member.setRefreshToken(refreshToken);
        member.setLoginIp(httpUtils.getClientIp());
        member.setLastLoginAt(LocalDateTime.now());
        memberService.updateMember(member);

        // Refresh 토큰 쿠키 저장
        jwtUtils.setRefreshTokenCookie(response, refreshToken);

        // Access 토큰 쿠키 저장
        jwtUtils.setAccessTokenCookie(response, accessToken);

        SecurityContextHolderStrategy contextHolder = SecurityContextHolder.getContextHolderStrategy();
        SecurityContext context = contextHolder.createEmptyContext();
        context.setAuthentication(authResult);
        contextHolder.setContext(context);

        LoginResponseDto loginResponseDto = memberMapper.toLoginResponseDto(member);

        // login 성공 메시지 전송
        response.setStatus(HttpStatus.OK.value());
        response.setContentType(MediaType.APPLICATION_JSON_VALUE + ";charset=UTF-8");
        objectMapper.writeValue(
            response.getWriter(),
            ApiResponseDto.success(ApiResponseCode.LOGIN_SUCCESSFUL, loginResponseDto)
        );
    }
}
