import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { Request } from 'express';

/**
 * AuthGuard - Mandatory authentication guard for protected routes
 *
 * This guard enforces JWT token validation for protected endpoints:
 * - Requires valid Bearer token in Authorization header
 * - Validates token signature and expiration
 * - Attaches decoded user payload to request object
 * - Rejects requests with missing, invalid, or expired tokens
 *
 * Use cases:
 * - Protected API endpoints requiring authentication
 * - User-specific resources and operations
 * - Administrative functions
 * - Any route where authentication is mandatory
 *
 * @example
 * // Apply to controller or route
 * @UseGuards(AuthGuard)
 * @Get('profile')
 * getProfile(@Request() req) {
 *   // req.user is guaranteed to exist here
 *   return this.userService.getProfile(req.user.sub);
 * }
 */
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private jwtService: JwtService,
    private configService: ConfigService,
  ) {}

  /**
   * Determines if the request should be allowed to proceed
   *
   * Enforces mandatory authentication by:
   * 1. Extracting token from Authorization header
   * 2. Validating token presence and format
   * 3. Verifying token signature and expiration
   * 4. Attaching decoded payload to request.user
   *
   * @param context - Execution context containing request/response
   * @returns true if authentication succeeds
   * @throws UnauthorizedException if token is missing, invalid, or expired
   */
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();

    // Step 1: Extract token from Authorization header
    const token = this.extractTokenFromHeader(request);

    // Step 2: Validate token presence
    if (!token) {
      throw new UnauthorizedException('Authentication token is required');
    }

    try {
      // Step 3: Verify token signature and expiration
      const payload = await this.jwtService.verifyAsync(token, {
        secret: this.configService.get('JWT_SECRET'),
      });

      // Step 4: Attach validated payload to request object
      // 💡 Route handlers can now access user data via req.user
      request.user = payload;
    } catch (error) {
      // Token verification failed (expired, invalid signature, malformed)
      throw new UnauthorizedException(
        'Invalid or expired authentication token',
      );
    }

    // Authentication successful - allow request to proceed
    return true;
  }

  /**
   * Extracts Bearer token from Authorization header
   *
   * Parses the Authorization header in the format:
   * "Bearer <token>" and returns the token portion.
   *
   * @param request - Express request object
   * @returns Token string if valid Bearer token present, undefined otherwise
   *
   * @note Only supports Bearer token scheme (RFC 6750)
   * @note Case-insensitive 'Bearer' check
   */
  private extractTokenFromHeader(request: Request): string | undefined {
    // Split "Bearer <token>" format
    const [type, token] = request.headers.authorization?.split(' ') ?? [];

    // Validate that it's a Bearer token and return token portion
    return type?.toLowerCase() === 'bearer' ? token : undefined;
  }
}
