import { Injectable, UnauthorizedException, Logger } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
import { passportJwtSecret } from 'jwks-rsa';
import { JwtPayload } from '../interfaces/jwt-payload.interface';
import { UsersService } from '../../users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  private readonly logger = new Logger(JwtStrategy.name);

  constructor(
    private readonly configService: ConfigService,
    private readonly usersService: UsersService,
  ) {
    const keycloakUrl = configService.get<string>('KEYCLOAK_URL');
    const keycloakRealm = configService.get<string>('KEYCLOAK_REALM');
    const issuer = `${keycloakUrl}/realms/${keycloakRealm}`;

    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      // Validate RS256 tokens via Keycloak's JWKS endpoint
      secretOrKeyProvider: passportJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 10,
        jwksUri: `${issuer}/protocol/openid-connect/certs`,
      }),
      issuer,
      algorithms: ['RS256'],
    });
  }

  async validate(payload: Record<string, unknown>): Promise<JwtPayload> {
    // Extract Keycloak-specific fields
    const sub = payload.sub as string;
    const email = payload.email as string;
    const realmAccess = payload.realm_access as { roles?: string[] } | undefined;
    const roles = realmAccess?.roles || [];

    if (!sub) {
      throw new UnauthorizedException('Invalid token: missing sub');
    }

    // Try to validate against local user DB; if user doesn't exist locally, allow anyway
    // (Keycloak is the source of truth for identity)
    try {
      const user = await this.usersService.findOne(sub);
      if (!user.isActive) {
        throw new UnauthorizedException('User account is deactivated');
      }
    } catch {
      // User not in local DB yet - that's OK, Keycloak token is valid
      this.logger.debug(`User ${sub} not found in local DB, proceeding with Keycloak token`);
    }

    return { sub, email, roles };
  }
}
