import { CanActivate, ExecutionContext, ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { AuthContextService, type AuthenticatedRequest } from './auth-context.service';
import { PERMISSION_METADATA_KEY, type RequiredPermissionMetadata } from './permission.decorator';
import { PermissionService } from './permission.service';

@Injectable()
export class PermissionGuard implements CanActivate {
  constructor(
    private readonly reflector: Reflector,
    private readonly authContext: AuthContextService,
    private readonly permissions: PermissionService,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const metadata = this.reflector.getAllAndOverride<RequiredPermissionMetadata>(PERMISSION_METADATA_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);
    if (!metadata) return true;

    const request = context.switchToHttp().getRequest<AuthenticatedRequest & {
      params?: Record<string, string>;
      body?: Record<string, unknown>;
    }>();
    const user = await this.authContext.resolveCurrentUser(request);
    if (!user) throw new UnauthorizedException('Authentication required');

    const allowed = await this.permissions.can(user, metadata.permission, {
      productId: scopedValue(request, metadata.productIdParam, metadata.productIdBody),
      projectId: scopedValue(request, metadata.projectIdParam, metadata.projectIdBody),
      versionId: scopedValue(request, metadata.versionIdParam, metadata.versionIdBody),
    });
    if (!allowed) throw new ForbiddenException(`Missing permission: ${metadata.permission}`);
    return true;
  }
}

function scopedValue(
  request: { params?: Record<string, string>; body?: Record<string, unknown> },
  paramKey?: string,
  bodyKey?: string,
): string | undefined {
  const value = (paramKey ? request.params?.[paramKey] : undefined) ?? (bodyKey ? request.body?.[bodyKey] : undefined);
  return typeof value === 'string' && value ? value : undefined;
}
