import { Controller } from "@nestjs/common";
import { GrpcMethod } from "@nestjs/microservices";
import { IamService } from "./iam.service.js";

/**
 * IAM gRPC Controller（双入口之 gRPC 侧，I1 裁决）。
 *
 * 端口 50052，供 BFF 聚合调用（teacher-bff / student-bff / parent-bff）。
 * 同一 IamService 实例同时被 REST Controller 和本 gRPC Controller 调用，
 * 业务逻辑不重复（president §2.16 双入口策略）。
 *
 * proto package: next_edu_cloud.iam.v1
 * service name: IamService
 */
@Controller()
export class IamGrpcController {
  constructor(private readonly service: IamService) {}

  @GrpcMethod("IamService", "Register")
  async register(data: {
    email: string;
    password: string;
    name: string;
  }): Promise<unknown> {
    const result = await this.service.register(data);
    return {
      user: this.toUserInfoProto(result.user),
      tokens: this.toTokenPairProto(result.tokens),
    };
  }

  @GrpcMethod("IamService", "Login")
  async login(data: { email: string; password: string }): Promise<unknown> {
    const result = await this.service.login(data);
    return {
      user: this.toUserInfoProto(result.user),
      tokens: this.toTokenPairProto(result.tokens),
    };
  }

  @GrpcMethod("IamService", "RefreshToken")
  async refreshToken(data: { refreshToken: string }): Promise<unknown> {
    const tokens = await this.service.refresh(data.refreshToken);
    return this.toTokenPairProto(tokens);
  }

  @GrpcMethod("IamService", "Logout")
  async logout(data: {
    refreshToken: string;
    userId: string;
  }): Promise<{ success: boolean }> {
    await this.service.logout(data.refreshToken, data.userId);
    return { success: true };
  }

  @GrpcMethod("IamService", "GetUserInfo")
  async getUserInfo(data: { userId: string }): Promise<unknown> {
    const user = await this.service.getUserInfo(data.userId);
    return this.toUserInfoProto(user);
  }

  @GrpcMethod("IamService", "BatchGetUsers")
  async batchGetUsers(data: { userIds: string[] }): Promise<unknown> {
    const users = await this.service.batchGetUsers(data.userIds ?? []);
    return { users: users.map((u) => this.toUserInfoProto(u)) };
  }

  @GrpcMethod("IamService", "GetEffectivePermissions")
  async getEffectivePermissions(data: {
    userId: string;
  }): Promise<{ permissions: string[] }> {
    const permissions = await this.service.getEffectivePermissions(data.userId);
    return { permissions };
  }

  @GrpcMethod("IamService", "GetEffectiveAccess")
  async getEffectiveAccess(data: {
    userId: string;
    permission: string;
  }): Promise<{ allowed: boolean; dataScope: string }> {
    return this.service.getEffectiveAccess(data.userId, data.permission);
  }

  @GrpcMethod("IamService", "GetEffectiveDataScope")
  async getEffectiveDataScope(data: {
    userId: string;
  }): Promise<{ dataScope: string }> {
    const dataScope = await this.service.getEffectiveDataScope(data.userId);
    return { dataScope };
  }

  @GrpcMethod("IamService", "GetViewports")
  async getViewports(data: { userId: string }): Promise<unknown> {
    const viewports = await this.service.getViewports(data.userId);
    return {
      viewports: viewports.map((vp) => ({
        key: vp.key,
        label: vp.label,
        route: vp.route,
        icon: vp.icon ?? "",
        sortOrder: vp.sortOrder,
        requiredPermission: vp.requiredPermission ?? "",
      })),
    };
  }

  @GrpcMethod("IamService", "GetPublicKey")
  async getPublicKey(): Promise<{
    kid: string;
    alg: string;
    publicKeyPem: string;
  }> {
    return this.service.getPublicKey();
  }

  @GrpcMethod("IamService", "GetChildrenByParent")
  async getChildrenByParent(data: { parentId: string }): Promise<unknown> {
    const children = await this.service.getChildrenByParent(data.parentId);
    return {
      children: children.map((c) => ({
        studentId: c.studentId,
        name: c.name,
        relation: c.relation,
      })),
    };
  }

  private toUserInfoProto(user: {
    id: string;
    email: string;
    name: string;
    roles: string[];
    permissions: string[];
    dataScope: string;
    status: string;
  }): Record<string, unknown> {
    return {
      id: user.id,
      email: user.email,
      name: user.name,
      roles: user.roles,
      permissions: user.permissions,
      dataScope: user.dataScope,
      status: user.status,
    };
  }

  private toTokenPairProto(tokens: {
    accessToken: string;
    refreshToken: string;
    expiresIn: number;
  }): Record<string, unknown> {
    return {
      accessToken: tokens.accessToken,
      refreshToken: tokens.refreshToken,
      expiresIn: tokens.expiresIn,
    };
  }
}
