// AuthorizationGuard 越权防御（B4 + president §2.7/§2.9）
// P2 实现：DEV_MODE 放行 + 日志告警；生产拒绝（保守策略）
// P3+ 替换为真实 gRPC 校验（core-edu.GetClassesByTeacher 比对 teacherId）
//
// 3 类越权错误码（president §2.7）：
//   - BFF_TEACHER_UNAUTHORIZED (401): x-user-id 缺失或无效
//   - BFF_TEACHER_FORBIDDEN_RESOURCE (403): teacherId 与资源无归属关系（场景 A）
//   - BFF_TEACHER_IDENTITY_MISMATCH (403): JWT teacherId 与 body teacherId 不一致（场景 B）
import { Injectable, Inject } from "@nestjs/common";
import { env } from "../config/env.js";
import { logger } from "../shared/observability/logger.js";
import {
  ForbiddenResourceError,
  IdentityMismatchError,
} from "../shared/errors/application-error.js";
import { IAM_CLIENT } from "../clients/iam/iam-client.interface.js";
import type { IamClient } from "../clients/iam/iam-client.interface.js";

/**
 * AuthorizationGuard 接口（president §2.9）
 * P2: DEV_MODE 放行；P3+: 真实 gRPC 校验
 */
export interface AuthorizationGuard {
  /** 校验教师是否有权访问指定班级（场景 A：teacherId 与资源归属） */
  canAccessClass(userId: string, classId: string): Promise<boolean>;

  /** 校验教师是否有权访问指定考试 */
  canAccessExam(userId: string, examId: string): Promise<boolean>;

  /** 校验教师是否有权访问指定作业 */
  canAccessHomework(userId: string, homeworkId: string): Promise<boolean>;

  /** 校验身份一致性（场景 B：JWT teacherId 与 body teacherId） */
  assertIdentityMatch(jwtTeacherId: string, bodyTeacherId: string): void;
}

export const AUTHORIZATION_GUARD = Symbol("AUTHORIZATION_GUARD");

/**
 * P2 实现：DEV_MODE 放行 + 日志告警
 * 生产环境（DEV_MODE=false）对所有资源校验返回 false（保守拒绝）
 * P3+ 替换为 AuthorizationGuardGrpcImpl（接入 core-edu gRPC 真实校验）
 */
@Injectable()
export class AuthorizationGuardImpl implements AuthorizationGuard {
  constructor(@Inject(IAM_CLIENT) private readonly iam: IamClient) {}

  async canAccessClass(userId: string, classId: string): Promise<boolean> {
    if (env.TEACHER_BFF_DEV_MODE) {
      logger.warn(
        { userId, classId, devMode: true },
        "AuthorizationGuard DEV_MODE: access allowed without real check",
      );
      return true;
    }
    // P2 生产模式：保守拒绝（P3+ 接入 core-edu.GetClassesByTeacher 真实校验）
    logger.error(
      { userId, classId, devMode: false },
      "AuthorizationGuard production mode not implemented yet (P3+)",
    );
    throw new ForbiddenResourceError(
      `User ${userId} has no access to class ${classId}`,
      { userId, classId, reason: "production_check_not_implemented" },
    );
  }

  async canAccessExam(userId: string, examId: string): Promise<boolean> {
    if (env.TEACHER_BFF_DEV_MODE) {
      logger.warn(
        { userId, examId, devMode: true },
        "AuthorizationGuard DEV_MODE: access allowed without real check",
      );
      return true;
    }
    logger.error(
      { userId, examId, devMode: false },
      "AuthorizationGuard production mode not implemented yet (P3+)",
    );
    throw new ForbiddenResourceError(
      `User ${userId} has no access to exam ${examId}`,
      { userId, examId, reason: "production_check_not_implemented" },
    );
  }

  async canAccessHomework(
    userId: string,
    homeworkId: string,
  ): Promise<boolean> {
    if (env.TEACHER_BFF_DEV_MODE) {
      logger.warn(
        { userId, homeworkId, devMode: true },
        "AuthorizationGuard DEV_MODE: access allowed without real check",
      );
      return true;
    }
    logger.error(
      { userId, homeworkId, devMode: false },
      "AuthorizationGuard production mode not implemented yet (P3+)",
    );
    throw new ForbiddenResourceError(
      `User ${userId} has no access to homework ${homeworkId}`,
      { userId, homeworkId, reason: "production_check_not_implemented" },
    );
  }

  /**
   * 校验身份一致性（场景 B，president §2.7）
   * JWT 中的 teacherId 必须与请求 body 中的 teacherId 一致
   * 此校验在所有模式（DEV_MODE / 生产）下都强制执行
   */
  assertIdentityMatch(jwtTeacherId: string, bodyTeacherId: string): void {
    if (jwtTeacherId !== bodyTeacherId) {
      logger.error(
        { jwtTeacherId, bodyTeacherId },
        "Identity mismatch: JWT teacherId != body teacherId",
      );
      throw new IdentityMismatchError(
        `JWT teacherId (${jwtTeacherId}) does not match body teacherId (${bodyTeacherId})`,
        { jwtTeacherId, bodyTeacherId },
      );
    }
  }
}
