import { Injectable, UnauthorizedException, Logger } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ConfigService } from '@nestjs/config';
import { Strategy, ExtractJwt } from 'passport-jwt';
import { passportJwtSecret } from 'jwks-rsa';
import { AuthService } from '../auth.service';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';

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

  constructor(
    private configService: ConfigService,
    private authService: AuthService,
    private httpService: HttpService,
  ) {
    super({
      secretOrKeyProvider: passportJwtSecret({
        cache: true,
        rateLimit: true,
        jwksRequestsPerMinute: 5,
        jwksUri: `${configService.get('AUTH0_ISSUER')}.well-known/jwks.json`,
      }),
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      audience: configService.get('AUTH0_AUDIENCE'),
      issuer: configService.get('AUTH0_ISSUER'),
      algorithms: ['RS256'],
      passReqToCallback: true, // We need the request to get the token
    });
  }

  async validate(req: any, payload: any) {
    // Extract token from Authorization header
    const token = req.headers.authorization?.replace('Bearer ', '');

    // Fetch user info from Auth0 /userinfo endpoint
    try {
      const userInfoUrl = `${this.configService.get('AUTH0_ISSUER')}userinfo`;

      const response = await firstValueFrom(
        this.httpService.get(userInfoUrl, {
          headers: {
            Authorization: `Bearer ${token}`,
          },
        }),
      );

      // Merge userinfo data into payload
      const userInfo = response.data;

      payload.email = userInfo.email || payload.email;
      payload.name = userInfo.name || payload.name;
      payload.picture = userInfo.picture || payload.picture;
      payload.email_verified = userInfo.email_verified;
    } catch (error) {
      this.logger.warn(`Failed to fetch user info: ${error.message}`);
      // Continue with payload-only data (fallbacks will apply)
    }

    // Get or create user from Auth0 token
    const user = await this.authService.validateUser(payload);

    if (!user) {
      throw new UnauthorizedException('User not found');
    }

    if (!user.isApproved) {
      throw new UnauthorizedException('User account pending approval');
    }

    return user;
  }
}
