package com.kamco.cd.kamcoback.members;

import com.kamco.cd.kamcoback.auth.JwtTokenProvider;
import com.kamco.cd.kamcoback.auth.RefreshTokenService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse;
import java.time.Duration;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseCookie;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.ErrorResponse;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "인증(Auth)", description = "로그인, 토큰 재발급, 로그아웃 API")
@RestController
@RequestMapping("/api/auth")
@RequiredArgsConstructor
public class AuthController {

  private final AuthenticationManager authenticationManager;
  private final JwtTokenProvider jwtTokenProvider;
  private final RefreshTokenService refreshTokenService;

  @Value("${token.refresh-cookie-name}")
  private String refreshCookieName;

  @Value("${token.refresh-cookie-secure:true}")
  private boolean refreshCookieSecure;

  @PostMapping("/signin")
  @Operation(summary = "로그인", description = "사번 또는 이메일과 비밀번호로 로그인하여 액세스/리프레시 토큰을 발급합니다.")
  @ApiResponses({
    @ApiResponse(
        responseCode = "200",
        description = "로그인 성공",
        content = @Content(schema = @Schema(implementation = TokenResponse.class))),
    @ApiResponse(
        responseCode = "401",
        description = "ID 또는 비밀번호 불일치",
        content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
  })
  public ResponseEntity<TokenResponse> signin(
      @io.swagger.v3.oas.annotations.parameters.RequestBody(
              description = "로그인 요청 정보",
              required = true)
          @RequestBody
          SignInRequest request,
      HttpServletResponse response) {
    Authentication authentication =
        authenticationManager.authenticate(
            new UsernamePasswordAuthenticationToken(request.username(), request.password()));

    String username = authentication.getName(); // UserDetailsService 에서 사용한 username

    String accessToken = jwtTokenProvider.createAccessToken(username);
    String refreshToken = jwtTokenProvider.createRefreshToken(username);

    // Redis에 RefreshToken 저장 (TTL = 7일)
    refreshTokenService.save(
        username, refreshToken, jwtTokenProvider.getRefreshTokenValidityInMs());

    // HttpOnly + Secure 쿠키에 RefreshToken 저장
    ResponseCookie cookie =
        ResponseCookie.from(refreshCookieName, refreshToken)
            .httpOnly(true)
            .secure(refreshCookieSecure) // 로컬 개발에서 http만 쓰면 false 로 바꿔야 할 수도 있음
            .path("/")
            .maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs()))
            .sameSite("Strict")
            .build();

    response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());

    return ResponseEntity.ok(new TokenResponse(accessToken));
  }

  @PostMapping("/refresh")
  @Operation(summary = "토큰 재발급", description = "리프레시 토큰으로 새로운 액세스/리프레시 토큰을 재발급합니다.")
  @ApiResponses({
    @ApiResponse(
        responseCode = "200",
        description = "재발급 성공",
        content = @Content(schema = @Schema(implementation = TokenResponse.class))),
    @ApiResponse(
        responseCode = "401",
        description = "만료되었거나 유효하지 않은 리프레시 토큰",
        content = @Content(schema = @Schema(implementation = ErrorResponse.class)))
  })
  public ResponseEntity<TokenResponse> refresh(String refreshToken, HttpServletResponse response) {
    if (refreshToken == null || !jwtTokenProvider.isValidToken(refreshToken)) {
      return ResponseEntity.status(401).build();
    }

    String username = jwtTokenProvider.getSubject(refreshToken);

    // Redis에 저장된 RefreshToken과 일치하는지 확인
    if (!refreshTokenService.validate(username, refreshToken)) {
      return ResponseEntity.status(401).build();
    }

    // 새 토큰 발급
    String newAccessToken = jwtTokenProvider.createAccessToken(username);
    String newRefreshToken = jwtTokenProvider.createRefreshToken(username);

    // Redis 갱신
    refreshTokenService.save(
        username, newRefreshToken, jwtTokenProvider.getRefreshTokenValidityInMs());

    // 쿠키 갱신
    ResponseCookie cookie =
        ResponseCookie.from(refreshCookieName, newRefreshToken)
            .httpOnly(true)
            .secure(refreshCookieSecure)
            .path("/")
            .maxAge(Duration.ofMillis(jwtTokenProvider.getRefreshTokenValidityInMs()))
            .sameSite("Strict")
            .build();
    response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());

    return ResponseEntity.ok(new TokenResponse(newAccessToken));
  }

  @PostMapping("/logout")
  @Operation(summary = "로그아웃", description = "현재 사용자의 토큰을 무효화(블랙리스트 처리 또는 리프레시 토큰 삭제)합니다.")
  @ApiResponses({
    @ApiResponse(
        responseCode = "200",
        description = "로그아웃 성공",
        content = @Content(schema = @Schema(implementation = Void.class)))
  })
  public ResponseEntity<Void> logout(Authentication authentication, HttpServletResponse response) {
    if (authentication != null) {
      String username = authentication.getName();
      // Redis에서 RefreshToken 삭제
      refreshTokenService.delete(username);
    }

    // 쿠키 삭제 (Max-Age=0)
    ResponseCookie cookie =
        ResponseCookie.from(refreshCookieName, "")
            .httpOnly(true)
            .secure(refreshCookieSecure)
            .path("/")
            .maxAge(0)
            .sameSite("Strict")
            .build();
    response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());

    return ResponseEntity.noContent().build();
  }

  @Schema(description = "로그인 요청 DTO")
  public record SignInRequest(
      @Schema(description = "사번", example = "11111") String username,
      @Schema(description = "비밀번호", example = "kamco1234!") String password) {}

  public record TokenResponse(String accessToken) {}
}
