/**
 * student-bff GlobalErrorFilter.
 *
 * 仲裁依据:
 *   - coord-final-decisions §1 G8 (首次实现即 GlobalErrorFilter + ActionState 信封)
 *   - president-final-rulings §2.6 (降级模式方案 B: success=true + degraded)
 *
 * GraphQL Yoga 错误格式化由 graphql-error-formatter.ts 处理;
 * 本 Filter 处理 NestJS HTTP 异常 (健康检查 /metrics 等), 并被 GraphQL Yoga
 * 在错误转换时复用 code/i18nKey 生成逻辑.
 */
import {
  Catch,
  ExceptionFilter,
  ArgumentsHost,
  HttpException,
  Logger,
} from "@nestjs/common";
import type { Request, Response } from "express";
import { ZodError } from "zod";
import { ApplicationError } from "./application-error.js";
import { DownstreamError } from "@edu/shared-ts/bff";

@Catch()
export class GlobalErrorFilter implements ExceptionFilter {
  private readonly logger = new Logger(GlobalErrorFilter.name);

  catch(exception: unknown, host: ArgumentsHost): void {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();

    const traceIdHeader = request.headers["x-request-id"];
    const traceId =
      typeof traceIdHeader === "string" ? traceIdHeader : "unknown";

    let statusCode = 500;
    let body: Record<string, unknown>;

    if (exception instanceof ApplicationError) {
      exception.traceId = traceId;
      statusCode = exception.statusCode;
      body = exception.toJSON();
    } else if (exception instanceof DownstreamError) {
      // 下游 gRPC 错误归一化为 BFF_STUDENT_BAD_GATEWAY (B2 + G8)
      statusCode = 502;
      body = {
        success: false,
        error: {
          code: "BFF_STUDENT_BAD_GATEWAY",
          message: `Downstream ${exception.service}.${exception.method} failed: ${exception.message}`,
          i18nKey: "error.bffStudent.bad_gateway",
          details: {
            service: exception.service,
            method: exception.method,
            downstreamCode: exception.code,
            traceId,
          },
          traceId,
        },
      };
    } else if (exception instanceof ZodError) {
      statusCode = 400;
      body = {
        success: false,
        error: {
          code: "BFF_STUDENT_VALIDATION_ERROR",
          message: "Validation failed",
          i18nKey: "error.bffStudent.validation_error",
          details: exception.flatten(),
          traceId,
        },
      };
    } else if (exception instanceof HttpException) {
      statusCode = exception.getStatus();
      const res = exception.getResponse();
      const message = this.extractHttpMessage(res, exception);
      body = {
        success: false,
        error: {
          code: "BFF_STUDENT_HTTP_ERROR",
          message,
          i18nKey: "error.bffStudent.http_error",
          traceId,
        },
      };
    } else {
      this.logger.error(
        `Unhandled exception: ${exception}`,
        exception instanceof Error ? exception.stack : undefined,
      );
      body = {
        success: false,
        error: {
          code: "BFF_STUDENT_INTERNAL_ERROR",
          message: "An unexpected error occurred",
          i18nKey: "error.bffStudent.internal_error",
          traceId,
        },
      };
    }

    response.status(statusCode).json(body);
  }

  private extractHttpMessage(
    res: string | object,
    exception: HttpException,
  ): string {
    if (typeof res === "string") {
      return res;
    }
    if (res && typeof res === "object" && "message" in res) {
      const msg = (res as { message: unknown }).message;
      return typeof msg === "string" ? msg : exception.message;
    }
    return exception.message;
  }
}
