package com.gameplatform.server.exception;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.support.WebExchangeBindException;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebInputException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.time.Instant;
import java.util.LinkedHashMap;

@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Object handleBadRequest(IllegalArgumentException e) {
        log.warn("400 BadRequest: {} - Stack: {}", e.getMessage(), getStackTrace(e));
        return body(HttpStatus.BAD_REQUEST.value(), e.getMessage());
    }

    @ExceptionHandler(IllegalStateException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Object handleForbidden(IllegalStateException e) {
        log.warn("403 Forbidden: {} - Stack: {}", e.getMessage(), getStackTrace(e));
        return body(HttpStatus.FORBIDDEN.value(), e.getMessage());
    }

    // 认证相关异常处理
    @ExceptionHandler(AuthenticationException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Object handleAuthenticationException(AuthenticationException e) {
        log.warn("401 Authentication Failed: {} - Type: {} - Stack: {}", 
                e.getMessage(), e.getClass().getSimpleName(), getStackTrace(e));
        return body(HttpStatus.UNAUTHORIZED.value(), "认证失败: " + e.getMessage());
    }

    @ExceptionHandler(BadCredentialsException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public Object handleBadCredentials(BadCredentialsException e) {
        log.warn("401 Bad Credentials: {} - Stack: {}", e.getMessage(), getStackTrace(e));
        return body(HttpStatus.UNAUTHORIZED.value(), "用户名或密码错误");
    }

    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public Object handleAccessDenied(AccessDeniedException e) {
        log.warn("403 Access Denied: {} - Stack: {}", e.getMessage(), getStackTrace(e));
        return body(HttpStatus.FORBIDDEN.value(), "访问被拒绝: " + e.getMessage());
    }

    @ExceptionHandler(WebExchangeBindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Object handleBindException(WebExchangeBindException e) {
        log.error("=== 参数校验失败详情 ===");
        log.error("异常类型: WebExchangeBindException");
        log.error("异常消息: {}", e.getMessage());
        log.error("字段错误数量: {}, 全局错误数量: {}", e.getFieldErrors().size(), e.getGlobalErrors().size());
        
        var details = new java.util.LinkedHashMap<String, Object>();
        
        // 详细记录字段错误
        if (!e.getFieldErrors().isEmpty()) {
            log.error("--- 字段校验错误详情 ---");
            e.getFieldErrors().forEach(fe -> {
                log.error("字段: {}, 拒绝值: [{}], 错误消息: {}, 错误代码: {}", 
                        fe.getField(), fe.getRejectedValue(), fe.getDefaultMessage(), fe.getCode());
                details.put(fe.getField(), fe.getDefaultMessage());
            });
        }
        
        // 详细记录全局错误
        if (!e.getGlobalErrors().isEmpty()) {
            log.error("--- 全局校验错误详情 ---");
            e.getGlobalErrors().forEach(ge -> {
                log.error("对象: {}, 错误消息: {}, 错误代码: {}", 
                        ge.getObjectName(), ge.getDefaultMessage(), ge.getCode());
                details.put(ge.getObjectName(), ge.getDefaultMessage());
            });
        }
        
        // 记录请求的原始数据（如果可获取）
        if (e.getBindingResult() != null && e.getBindingResult().getTarget() != null) {
            log.error("请求对象类型: {}", e.getBindingResult().getTarget().getClass().getSimpleName());
            log.error("请求对象内容: {}", e.getBindingResult().getTarget().toString());
        }
        
        log.error("=== 参数校验失败详情结束 ===");
        
        return body(HttpStatus.BAD_REQUEST.value(), "参数校验失败", details);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Object handleConstraintViolation(ConstraintViolationException e) {
        log.error("=== 约束校验失败详情 ===");
        log.error("异常类型: ConstraintViolationException");
        log.error("异常消息: {}", e.getMessage());
        log.error("约束违反数量: {}", e.getConstraintViolations().size());
        
        var details = new java.util.LinkedHashMap<String, Object>();
        
        log.error("--- 约束违反详情 ---");
        for (ConstraintViolation<?> v : e.getConstraintViolations()) {
            String propertyPath = String.valueOf(v.getPropertyPath());
            String message = v.getMessage();
            Object invalidValue = v.getInvalidValue();
            
            log.error("属性路径: {}, 无效值: [{}], 错误消息: {}, 约束注解: {}", 
                    propertyPath, invalidValue, message, v.getConstraintDescriptor().getAnnotation().annotationType().getSimpleName());
            
            details.put(propertyPath, message);
        }
        
        log.error("=== 约束校验失败详情结束 ===");
        
        return body(HttpStatus.BAD_REQUEST.value(), "参数校验失败", details);
    }

    @ExceptionHandler(ServerWebInputException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Object handleInput(ServerWebInputException e) {
        log.warn("400 InputError: {} - Reason: {} - Stack: {}", 
                e.getMessage(), e.getReason(), getStackTrace(e));
        return body(HttpStatus.BAD_REQUEST.value(), "请求解析失败: " + e.getReason());
    }

    @ExceptionHandler(ResponseStatusException.class)
    public Object handleRse(ResponseStatusException e) {
        var status = e.getStatus();
        log.warn("{} ResponseStatusException: {} - Stack: {}", 
                status, e.getReason(), getStackTrace(e));
        return body(status.value(), e.getReason());
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Object handleOther(Exception e) {
        log.error("500 InternalServerError: {} - Type: {} - Stack: {}", 
                e.getMessage(), e.getClass().getSimpleName(), getStackTrace(e), e);
        return body(HttpStatus.INTERNAL_SERVER_ERROR.value(), "服务器内部错误");
    }

    private Object body(int code, String message) {
        return new LinkedHashMap<>() {{
            put("code", code);
            put("message", message);
            put("timestamp", Instant.now().toString());
        }};
    }

    private Object body(int code, String message, Object details) {
        return new LinkedHashMap<>() {{
            put("code", code);
            put("message", message);
            put("details", details);
            put("timestamp", Instant.now().toString());
        }};
    }

    private String getStackTrace(Exception e) {
        StackTraceElement[] stackTrace = e.getStackTrace();
        if (stackTrace.length > 0) {
            return stackTrace[0].toString();
        }
        return "No stack trace available";
    }
}
