/*
* 기본 컨트롤러 어드바이스, 하위 컨트롤러 어드바이스는 이 클래스를 상속 하여 사용해야 함.
* */
public class BaseControllerAdvice {

  private String parseExceptionMessage(Exception exception) {
    String message = "no detail message for %s".formatted(exception.getClass().getSimpleName());

    if (StringUtil.isExist(exception.getMessage())) {
      message = exception.getMessage();
    }

    return message;
  }

  public ProblemDetail exceptionResponse(HttpStatusCode status, String detail) {
    ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(status, detail);
    problemDetail.setTitle(status.toString());

    return problemDetail;
  }

  public ProblemDetail exceptionResponse(HttpStatusCode status, Exception exception) {
    ProblemDetail problemDetail = ProblemDetail.forStatus(status);
    problemDetail.setDetail(parseExceptionMessage(exception));
    problemDetail.setTitle(status.toString());

    return problemDetail;
  }
}

@RestControllerAdvice
@Slf4j
@Order(Ordered.LOWEST_PRECEDENCE)
public class ApiControllerAdvice extends BaseControllerAdvice {

  @ExceptionHandler({AlreadyExistException.class})
  public ProblemDetail handleAlreadyExistException(AlreadyExistException ex) {
    return this.exceptionResponse(HttpStatus.BAD_REQUEST, ex);
  }

  @ExceptionHandler({BadRequestException.class})
  public ProblemDetail handleBadRequestException(BadRequestException ex) {
    return this.exceptionResponse(HttpStatus.BAD_REQUEST, ex);
  }

  @ExceptionHandler(Exception.class)
  public ProblemDetail handlerException(Exception ex) {
    return this.exceptionResponse(HttpStatus.INTERNAL_SERVER_ERROR, new Exception("unknown server error"));
  }
}

@RestControllerAdvice
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE)
public class LoginControllerAdvice extends BaseControllerAdvice{

  // 로그인 예외 실패 수 있으면, 예외 응답에 실패 수 포함.
  @ExceptionHandler({LoginException.class})
  public ProblemDetail  handleLoginFail(LoginException ex) {
    ProblemDetail problemDetail = this.exceptionResponse(HttpStatus.UNAUTHORIZED, ex);
    if (ex.getLoginFailCount() != null) {
      problemDetail.setProperty("loginFailCount", ex.getLoginFailCount());
    }
    return problemDetail;
  }
}

@Getter
public class LoginException extends RuntimeException{
  private final Integer loginFailCount;

  public LoginException(String message) {
    super(message);
    loginFailCount = null;
  }

  public LoginException(String message, Integer loginFailCount) {
    super(message);
    this.loginFailCount = loginFailCount;
  }

  // 없는 유저 로그인 시도
  public static class NoUser extends LoginException {
    public NoUser() { super("no user"); }
  }

  // 로그인 실패 횟수 초과
  public static class TooManyFailedLogin extends LoginException {
    public TooManyFailedLogin(Integer loginFailCount) {
      super("too many failed login", loginFailCount);
    }
  }

  // 비활성화 계정 로그인 시도
  public static class DeactivatedUser extends LoginException {
    public DeactivatedUser(Integer loginFailCount) {
      super("deactivated user", loginFailCount);
    }
  }

  // 비밀번호 틀림
  public static class InvalidPassword extends LoginException {
    public InvalidPassword(Integer loginFailCount) {
      super("invalid password", loginFailCount);
    }
  }
}