package com.spring.common.error;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.validation.ConstraintViolationException;

import org.springframework.dao.DataAccessException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 전역 예외 처리를 위한 클래스입니다.
 * 
 * <p>이 클래스는 Spring의 @RestControllerAdvice를 사용하여 애플리케이션 전역에서 발생하는
 * 예외를 처리합니다.</p>
 * 
 * <p>특정 예외 유형에 대해 적절한 응답을 생성하여 클라이언트에 반환하는 기능을 제공합니다.</p>
 * 
 * @author mindol
 * @version 1.0
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * BizBaseException 예외를 처리하는 메서드입니다.
     * 
     * <p>BizBaseException이 발생했을 때 호출되며, 해당 예외의 오류 규칙을 기반으로
     * BizErrorResponse 객체를 생성하여 반환합니다.</p>
     * 
     * @param e 발생한 BizBaseException 예외
     * @return 생성된 BizErrorResponse 객체
     */
    @ExceptionHandler(BizBaseException.class)
    public BizErrorResponse handleCustomException(BizBaseException e) {
        return BizErrorResponse.fromErrorRule(e.getErrorRule());
    }

    /**
     * 모든 일반 예외를 처리하는 메서드입니다.
     * 
     * <p>예기치 않은 예외가 발생했을 때 호출되며, 시스템 오류에 대한 BizErrorResponse 객체를 생성하여 반환합니다.</p>
     * 
     * @param e 발생한 일반 예외
     * @return 생성된 BizErrorResponse 객체
     */
    @ExceptionHandler(Exception.class)
    public BizErrorResponse handleException(Exception e) {
        return BizErrorResponse.fromErrorRule(ExceptionRule.SYSTEM_ERROR);
    }

    /**
     * MethodArgumentNotValidException 예외를 처리하는 메서드입니다.
     * 
     * <p>MethodArgumentNotValidException이 발생했을 때 호출되며, 필드 오류 목록을 기반으로
     * BizErrorResponse 객체를 생성하여 반환합니다.</p>
     * 
     * @param e 발생한 MethodArgumentNotValidException 예외
     * @return 생성된 BizErrorResponse 객체
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public BizErrorResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {
        return BizErrorResponse.fromFieldError(e.getFieldErrors());
    }

    /**
     * ConstraintViolationException 예외를 처리하는 메서드입니다.
     * 
     * <p>ConstraintViolationException이 발생했을 때 호출되며, 제약 조건 위반에 대한 필드 오류 목록을 기반으로
     * BizErrorResponse 객체를 생성하여 반환합니다.</p>
     * 
     * @param e 발생한 ConstraintViolationException 예외
     * @return 생성된 BizErrorResponse 객체
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public BizErrorResponse constraintViolationException(ConstraintViolationException e) {
        List<FieldError> fieldErrors = e.getConstraintViolations().stream()
            .map(violation -> new FieldError(
                violation.getRootBeanClass().getName(),
                violation.getPropertyPath().toString(),
                violation.getMessage()))
            .collect(Collectors.toList());
        return BizErrorResponse.fromFieldError(fieldErrors);
    }

    /**
     * DataAccessException 예외를 처리하는 메서드입니다.
     * 
     * <p>데이터 접근 중 예외가 발생했을 때 호출되며, 가장 구체적인 원인 예외를 기반으로
     * BizErrorResponse 객체를 생성하여 반환합니다.</p>
     * 
     * @param e 발생한 DataAccessException 예외
     * @return 생성된 BizErrorResponse 객체
     */
    @ExceptionHandler(DataAccessException.class)
    public BizErrorResponse handleDataAccessException(DataAccessException e) {
        Throwable cause = e.getMostSpecificCause();
        return BizErrorResponse.of(
                ExceptionRule.SYSTEM_ERROR,
                Optional.ofNullable(cause)
                        .map(Throwable::getMessage)
                        .orElse(ExceptionRule.SYSTEM_ERROR.getMessage())
            );
    }
    
}
