BusinessExceptionHandler.java

package com.smartsupplypro.inventory.exception;

import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import com.smartsupplypro.inventory.exception.dto.ErrorResponse;

/**
 * Business exception handler for domain-specific application logic failures.
 *
 * <p>Complements {@link GlobalExceptionHandler} by focusing exclusively on
 * Smart Supply Pro business rules, validation logic, and domain constraints.
 * Provides context-rich error messages for business operations while maintaining
 * consistent REST API error response structure.
 *
 * <p><strong>Scope</strong>: Domain exceptions (DuplicateResourceException,
 * InvalidRequestException, IllegalStateException for business rules).
 *
 * <p><strong>Response Format</strong>: Delegates to {@link ErrorResponse} builder
 * for consistent JSON structure with correlation tracking.
 *
 * <p><strong>Handled Business Scenarios</strong>:
 * <ul>
 *   <li>Duplicate entity creation (email, SKU, supplier name conflicts)</li>
 *   <li>Custom validation failures (business rule violations)</li>
 *   <li>State transition conflicts (e.g., deleting supplier with active inventory)</li>
 * </ul>
 *
 * @author Smart Supply Pro Development Team
 * @version 1.0.0
 * @since 2.0.0
 * @see GlobalExceptionHandler
 * @see ErrorResponse
 */
@Order(Ordered.HIGHEST_PRECEDENCE)  // Runs before GlobalExceptionHandler to catch business exceptions first
@RestControllerAdvice
public class BusinessExceptionHandler {

    /* =======================================================================
     * 400 - Business Validation Errors
     * ======================================================================= */

    /**
     * Handles custom application validation failures with business context.
     * Extracts field-level errors when available for detailed client feedback.
     *
     * <p><strong>Example Scenarios</strong>:
     * <ul>
     *   <li>Invalid SKU format (must match pattern)</li>
     *   <li>Quantity below minimum threshold</li>
     *   <li>Price validation failures</li>
     * </ul>
     *
     * @param ex application-specific validation exception with optional field errors
     * @return 400 with business validation details
     */
    @ExceptionHandler(InvalidRequestException.class)
    public ResponseEntity<ErrorResponse> handleInvalidRequest(InvalidRequestException ex) {
        String message = ex.hasFieldErrors()
            ? "Validation failed: " + ex.getFieldErrors().size() + " field error(s)"
            : (ex.getMessage() != null ? ex.getMessage() : "Invalid request");
        
        return ErrorResponse.builder()
                .status(HttpStatus.BAD_REQUEST)
                .message(message)
                .build();
    }

    /* =======================================================================
     * 409 - Business Conflicts
     * ======================================================================= */

    /**
     * Handles enterprise duplicate resource violations with conflict resolution context.
     * Provides specific conflict details when available for client-side handling.
     *
     * <p><strong>Example Scenarios</strong>:
     * <ul>
     *   <li>Email already registered for user account</li>
     *   <li>Product SKU already exists in catalog</li>
     *   <li>Supplier name conflict in database</li>
     * </ul>
     *
     * @param ex business rule uniqueness constraint exception
     * @return 409 with resource conflict details
     */
    @ExceptionHandler(DuplicateResourceException.class)
    public ResponseEntity<ErrorResponse> handleDuplicateResource(DuplicateResourceException ex) {
        String message = ex.hasDetailedContext()
            ? ex.getClientMessage()
            : (ex.getMessage() != null ? ex.getMessage() : "Duplicate resource");
        
        return ErrorResponse.builder()
                .status(HttpStatus.CONFLICT)
                .message(message)
                .build();
    }

    /**
     * Handles business state violations with operational context preservation.
     * Focuses on domain-specific state transitions and business rule conflicts.
     *
     * <p><strong>Example Scenarios</strong>:
     * <ul>
     *   <li>Deleting supplier with active product inventory</li>
     *   <li>Canceling order in "shipped" status</li>
     *   <li>Modifying locked financial records</li>
     * </ul>
     *
     * <p><strong>Note</strong>: This handler captures IllegalStateException from
     * service layer business logic, not framework-level state exceptions.
     *
     * @param ex illegal state exception indicating business rule violation
     * @return 409 with business context details
     */
    @ExceptionHandler(IllegalStateException.class)
    public ResponseEntity<ErrorResponse> handleBusinessStateConflict(IllegalStateException ex) {
        String message = (ex.getMessage() != null && !ex.getMessage().isBlank())
            ? ex.getMessage()
            : "Business rule conflict";
        
        return ErrorResponse.builder()
                .status(HttpStatus.CONFLICT)
                .message(message)
                .build();
    }
}