import {
  Injectable,
  ExecutionContext,
  UnauthorizedException,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
import { ApiKeysService } from '../../modules/api-keys/api-keys.service';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(
    private reflector: Reflector,
    private readonly apiKeysService: ApiKeysService,
  ) {
    super();
  }

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

    // Additive API-key auth: a `corr_`-prefixed bearer token (or X-API-Key
    // header) authenticates programmatically AS the license owner. JWTs are
    // `eyJ...` and never collide with the `corr_` prefix, so the standard JWT
    // path below is left completely untouched — zero login regression risk.
    const request = context.switchToHttp().getRequest();
    const rawKey = this.extractApiKey(request);
    if (rawKey) {
      const result = await this.apiKeysService.validateKey(rawKey);
      if (!result) {
        throw new UnauthorizedException('Invalid or revoked API key');
      }
      // Shape the principal like a JWT user so @CurrentTenant / @CurrentUser and
      // the permission layer behave identically. is_api_key grants full access
      // to THIS license (see PermissionsGuard) — a key is full programmatic
      // access to your own license, always tenant-scoped by license_id.
      request.user = {
        sub: result.user_id ?? undefined,
        license_id: result.license_id,
        is_super_admin: false,
        is_api_key: true,
        permissions: {},
      };
      return true;
    }

    return (await super.canActivate(context)) as boolean;
  }

  /** Pull a `corr_`-prefixed key from `Authorization: Bearer` or `X-API-Key`. */
  private extractApiKey(request: any): string | null {
    const auth = request.headers?.authorization;
    if (typeof auth === 'string' && auth.startsWith('Bearer ')) {
      const token = auth.slice(7).trim();
      if (token.startsWith('corr_')) return token;
    }
    const headerKey = request.headers?.['x-api-key'];
    if (typeof headerKey === 'string' && headerKey.startsWith('corr_')) {
      return headerKey.trim();
    }
    return null;
  }
}
