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.warn("400 ValidationError: {} - Field errors: {} - Global errors: {}", 
                e.getMessage(), e.getFieldErrors().size(), e.getGlobalErrors().size());
        var details = new java.util.LinkedHashMap<String, Object>();
        e.getFieldErrors().forEach(fe -> details.put(fe.getField(), fe.getDefaultMessage()));
        e.getGlobalErrors().forEach(ge -> details.put(ge.getObjectName(), ge.getDefaultMessage()));
        return body(HttpStatus.BAD_REQUEST.value(), "参数校验失败", details);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Object handleConstraintViolation(ConstraintViolationException e) {
        log.warn("400 ConstraintViolation: {} - Violations: {}", 
                e.getMessage(), e.getConstraintViolations().size());
        var details = new java.util.LinkedHashMap<String, Object>();
        for (ConstraintViolation<?> v : e.getConstraintViolations()) {
            details.put(String.valueOf(v.getPropertyPath()), v.getMessage());
        }
        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";
    }
}
