package com.xyw.console.common;

import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/** 文件用途（白话）：把未被业务模块处理的数据库和服务器异常统一转换为页面可展示的 JSON 错误，避免浏览器把非认证故障误认为未登录。 */
@RestControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class GlobalExceptionHandler {
  /** 代码作用（白话）：把数据库唯一约束冲突转换成重复数据提示；关联文件：PhoneAssetService.java、phone-api-client.js；关联逻辑（调用链/数据流）：新增或保存 -> DuplicateKeyException -> 本方法 -> ApiResponse -> 页面提示。 */
  @ExceptionHandler(DuplicateKeyException.class)
  @ResponseStatus(HttpStatus.CONFLICT)
  public ApiResponse<Void> duplicate(DuplicateKeyException error) {
    String detail = exceptionDetails(error);
    String message = detail.contains("uk_phone_asset_phone_number") ? "手机号已存在，请勿重复新增" : "数据已存在，请勿重复提交";
    return ApiResponse.error(409, message);
  }

  /** 代码作用（白话）：把字段缺失等 SQL 结构错误转换成数据库迁移提示；关联文件：WecomAccountService.java、V2__add_wecom_phone_link_mode.sql；关联逻辑（调用链/数据流）：业务写入 -> BadSqlGrammarException -> 本方法 -> ApiResponse -> 页面提示。 */
  @ExceptionHandler(BadSqlGrammarException.class)
  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  public ApiResponse<Void> badSqlGrammar(BadSqlGrammarException error) {
    return ApiResponse.error(500, "数据库字段未同步，请完成数据库迁移后重试");
  }

  /** 代码作用（白话）：给未预期的服务端异常提供安全的通用提示，不把 SQL、路径或堆栈暴露给浏览器；关联文件：各 Controller、前端 API 客户端；关联逻辑（调用链/数据流）：未知异常 -> 本方法 -> ApiResponse -> 页面错误提示。 */
  @ExceptionHandler(Exception.class)
  @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
  public ApiResponse<Void> unexpected(Exception error) {
    return ApiResponse.error(500, "服务器处理失败，请稍后重试");
  }

  /** 代码作用（白话）：拼接异常及其原因链的文字，用于识别数据库约束名称而不向浏览器返回底层细节；关联文件：GlobalExceptionHandlerTest.java、PhoneAssetService.java；关联逻辑（调用链/数据流）：数据库驱动异常 -> Spring 包装异常 -> 本方法识别约束 -> 业务错误消息。 */
  private String exceptionDetails(Throwable error) {
    StringBuilder details = new StringBuilder();
    for (Throwable current = error; current != null; current = current.getCause()) details.append(' ').append(current.getMessage());
    return details.toString();
  }
}
