package study.orderflow.web;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/// 统一错误响应：字段级 400、业务 4xx、锁超时 503（O03/N02/S03）。
/// 任何分支都不回显 Authorization/token/SQL；日志只记稳定 code。
@RestControllerAdvice
public class OrderErrorAdvice {
    private static final Logger log = LoggerFactory.getLogger(OrderErrorAdvice.class);

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Map<String, Object>> onValidation(MethodArgumentNotValidException ex) {
        List<Map<String, String>> fields = ex.getBindingResult().getFieldErrors().stream()
                .map(error -> Map.of("field", error.getField(), "code", error.getDefaultMessage()))
                .toList();
        return bad("validation_failed", fields);
    }

    @ExceptionHandler(OrderBusinessException.class)
    public ResponseEntity<Map<String, Object>> onBusiness(OrderBusinessException ex) {
        log.info("order rejected code={} status={}", ex.code(), ex.httpStatus().value());
        return ResponseEntity.status(ex.httpStatus())
                .body(Map.of("error", Map.of("code", ex.code())));
    }

    @ExceptionHandler({CannotAcquireLockException.class, org.springframework.dao.QueryTimeoutException.class})
    public ResponseEntity<Map<String, Object>> onLockTimeout(RuntimeException ex) {
        // 请求没有失败在业务上：锁等待/语句超时取消是基础设施超时，503 语义，可重试但不是库存不足
        return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE)
                .body(Map.of("error", Map.of("code", "lock_wait_timeout")));
    }

    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<Map<String, Object>> onDenied(AccessDeniedException ex) {
        // 方法级 @PreAuthorize 拒绝发生在控制器调用中，必须显式映射 403；
        // 否则会被兜底 handler 吞成 500，把"无权"误报成"服务坏了"。
        return ResponseEntity.status(HttpStatus.FORBIDDEN)
                .body(Map.of("error", Map.of("code", "forbidden")));
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<Map<String, Object>> onUnexpected(Exception ex) {
        // 只记异常类型与稳定 code：消息可能携带 SQL 片段或参数值，不写入日志
        log.error("order unexpected failure type={}", ex.getClass().getSimpleName());
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(Map.of("error", Map.of("code", "internal_error")));
    }

    @ExceptionHandler(AuthenticationException.class)
    public ResponseEntity<Map<String, Object>> onAuth(AuthenticationException ex) {
        return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(Map.of("error", Map.of("code", "invalid_token")));
    }

    private ResponseEntity<Map<String, Object>> bad(String code, List<Map<String, String>> fields) {
        Map<String, Object> error = new LinkedHashMap<>();
        error.put("code", code);
        if (fields != null) {
            error.put("fields", fields);
        }
        return ResponseEntity.badRequest().body(Map.of("error", error));
    }
}
