package com.project.movienight.adapters.web import com.project.movienight.domain.exception.BlockedValueException import com.project.movienight.domain.exception.DomainException import com.project.movienight.domain.exception.EntityNotFoundException import org.slf4j.LoggerFactory import org.slf4j.MDC import org.springframework.http.HttpStatus import org.springframework.web.bind.annotation.ExceptionHandler import org.springframework.web.bind.annotation.ResponseStatus import org.springframework.web.bind.annotation.RestControllerAdvice @RestControllerAdvice class ApiExceptionHandler { private val log = LoggerFactory.getLogger(javaClass) @ExceptionHandler(EntityNotFoundException::class) @ResponseStatus(HttpStatus.NOT_FOUND) fun handleNotFound(exception: EntityNotFoundException): ErrorResponse { val traceId = currentTraceId() log.warn("Entity not found: traceId='{}', message='{}'", traceId, exception.message) return ErrorResponse( message = exception.message ?: "Entity not found", traceId = traceId, ) } @ExceptionHandler(BlockedValueException::class) @ResponseStatus(HttpStatus.BAD_REQUEST) fun handleBlockedValue(exception: BlockedValueException): ErrorResponse { val traceId = currentTraceId() log.warn("Blocked value: traceId='{}', message='{}'", traceId, exception.message) return ErrorResponse( message = exception.message ?: "Blocked value", traceId = traceId, ) } @ExceptionHandler(DomainException::class) @ResponseStatus(HttpStatus.BAD_REQUEST) fun handleDomainException(exception: DomainException): ErrorResponse { val traceId = currentTraceId() log.warn("Domain error: traceId='{}', message='{}'", traceId, exception.message) return ErrorResponse( message = exception.message ?: "Domain error", traceId = traceId, ) } @ExceptionHandler(Exception::class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) fun handleUnexpectedException(exception: Exception): ErrorResponse { val traceId = currentTraceId() log.error("Unexpected error: traceId='{}'", traceId, exception) return ErrorResponse( message = "Internal server error", traceId = traceId, ) } private fun currentTraceId(): String = MDC.get("traceId") ?: "unknown" } data class ErrorResponse( val message: String, val traceId: String, )