import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { OrgRole } from '@prisma/client';
import { PrismaService } from '../../prisma/prisma.service';
import { ROLES_KEY } from '../decorators/roles.decorator';
import { Principal, ROLE_RANK } from '../principal';

// Runs after UserOrApiKeyGuard on org-scoped routes (/orgs/:orgId/...).
// Users: must be a member of the org, with at least the @Roles() role if present.
// API keys: must belong to the org (scopes are checked by ScopesGuard).
@Injectable()
export class OrgRolesGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly prisma: PrismaService,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const req = context.switchToHttp().getRequest();
    const principal: Principal | undefined = req.user;
    const orgId: string | undefined = req.params?.orgId;
    if (!principal || !orgId) {
      throw new ForbiddenException('Organization scope required');
    }

    if (principal.type === 'apiKey') {
      if (principal.orgId !== orgId) {
        throw new ForbiddenException('API key does not belong to this organization');
      }
      return true;
    }

    const membership = await this.prisma.orgMembership.findUnique({
      where: { orgId_userId: { orgId, userId: principal.userId } },
    });
    if (!membership) {
      throw new ForbiddenException('Not a member of this organization');
    }

    const required = this.reflector.getAllAndOverride<OrgRole | undefined>(ROLES_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (required && ROLE_RANK[membership.role] < ROLE_RANK[required]) {
      throw new ForbiddenException(`Requires ${required} role`);
    }

    req.membership = membership;
    return true;
  }
}
