/**
 * student-bff ApplicationError 层次.
 *
 * 仲裁依据:
 *   - coord-final-decisions §1 G8 (首次实现即 GlobalErrorFilter + ActionState 信封)
 *   - coord-final-decisions §1 G14 (服务名大写前缀, BFF_STUDENT_*)
 *   - coord-final-decisions §2 B5 (统一 BFF_ 前缀)
 *   - president-final-rulings §2.7 (3 类越权防御错误码)
 *
 * 错误码清单:
 *   BFF_STUDENT_VALIDATION_ERROR (400)        Zod 校验失败
 *   BFF_STUDENT_UNAUTHORIZED (401)            x-user-id 缺失或无效
 *   BFF_STUDENT_FORBIDDEN_RESOURCE (403)      资源无归属关系 (场景 A)
 *   BFF_STUDENT_IDENTITY_MISMATCH (403)       JWT userId 与 body 不一致 (场景 B)
 *   BFF_STUDENT_NOT_FOUND (404)               资源不存在
 *   BFF_STUDENT_CONFLICT (409)                重复提交 / 状态冲突
 *   BFF_STUDENT_BUSINESS_ERROR (422)          业务规则违反
 *   BFF_STUDENT_BAD_GATEWAY (502)             下游 gRPC 失败
 *   BFF_STUDENT_GATEWAY_TIMEOUT (504)         下游超时
 *   BFF_STUDENT_SERVICE_UNAVAILABLE (503)     熔断器开启
 *   BFF_STUDENT_INTERNAL_ERROR (500)          未捕获异常
 */
export type ErrorType =
  | "validation"
  | "not_found"
  | "permission_denied"
  | "unauthorized"
  | "conflict"
  | "business"
  | "bad_gateway"
  | "gateway_timeout"
  | "service_unavailable"
  | "internal";

export interface ErrorDetails {
  [key: string]: unknown;
}

/**
 * i18n key 生成 (president §2.7 + F4 裁决): error.bffStudent.<code_snake>.
 */
function toI18nKey(code: string): string {
  const snake = code
    .replace(/^BFF_STUDENT_/, "")
    .toLowerCase()
    .replace(/_/g, "_");
  return `error.bffStudent.${snake}`;
}

export abstract class ApplicationError extends Error {
  abstract readonly type: ErrorType;
  abstract readonly statusCode: number;
  readonly code: string;
  readonly details?: ErrorDetails;
  traceId?: string;

  constructor(message: string, code: string, details?: ErrorDetails) {
    super(message);
    this.name = this.constructor.name;
    this.code = code;
    this.details = details;
  }

  /**
   * 序列化为 ActionState 信封响应体 (G8).
   */
  toJSON(): Record<string, unknown> {
    return {
      success: false,
      error: {
        code: this.code,
        message: this.message,
        i18nKey: toI18nKey(this.code),
        details: this.details,
        traceId: this.traceId,
      },
    };
  }
}

export class ValidationError extends ApplicationError {
  readonly type = "validation" as const;
  readonly statusCode = 400;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_VALIDATION_ERROR", details);
  }
}

export class UnauthorizedError extends ApplicationError {
  readonly type = "unauthorized" as const;
  readonly statusCode = 401;
  constructor(message = "Missing or invalid x-user-id", details?: ErrorDetails) {
    super(message, "BFF_STUDENT_UNAUTHORIZED", details);
  }
}

/**
 * 场景 A: 资源无归属关系 (president §2.7).
 * 如学生请求的 studentId 与 JWT userId 不一致.
 */
export class ForbiddenResourceError extends ApplicationError {
  readonly type = "permission_denied" as const;
  readonly statusCode = 403;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_FORBIDDEN_RESOURCE", details);
  }
}

/**
 * 场景 B: JWT userId 与请求 body userId 不一致 (president §2.7).
 */
export class IdentityMismatchError extends ApplicationError {
  readonly type = "permission_denied" as const;
  readonly statusCode = 403;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_IDENTITY_MISMATCH", details);
  }
}

export class NotFoundError extends ApplicationError {
  readonly type = "not_found" as const;
  readonly statusCode = 404;
  constructor(resource: string, id: string) {
    super(`${resource} not found: ${id}`, "BFF_STUDENT_NOT_FOUND", {
      resource,
      id,
    });
  }
}

export class ConflictError extends ApplicationError {
  readonly type = "conflict" as const;
  readonly statusCode = 409;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_CONFLICT", details);
  }
}

export class BusinessError extends ApplicationError {
  readonly type = "business" as const;
  readonly statusCode = 422;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_BUSINESS_ERROR", details);
  }
}

export class BadGatewayError extends ApplicationError {
  readonly type = "bad_gateway" as const;
  readonly statusCode = 502;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_BAD_GATEWAY", details);
  }
}

export class GatewayTimeoutError extends ApplicationError {
  readonly type = "gateway_timeout" as const;
  readonly statusCode = 504;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_GATEWAY_TIMEOUT", details);
  }
}

export class ServiceUnavailableError extends ApplicationError {
  readonly type = "service_unavailable" as const;
  readonly statusCode = 503;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_SERVICE_UNAVAILABLE", details);
  }
}

export class InternalError extends ApplicationError {
  readonly type = "internal" as const;
  readonly statusCode = 500;
  constructor(message: string, details?: ErrorDetails) {
    super(message, "BFF_STUDENT_INTERNAL_ERROR", details);
  }
}
