import {
  Injectable,
  CanActivate,
  ExecutionContext,
  UnauthorizedException,
} from '@nestjs/common';
import { GlobalService } from 'src/modules/backend/global/global.service';
import { RedisService } from 'src/modules/backend/redis/redis.service';

/**
 * SessionGuard
 * ------------
 * This guard ensures that a request is backed by a valid session stored in Redis.
 *
 * Responsibilities:
 * 1. Extract session ID from cookie or Authorization header
 * 2. Validate session existence in Redis
 * 3. Refresh session TTL on every valid request
 * 4. Attach session data to the request object
 */
@Injectable()
export class SessionGuard implements CanActivate {
  constructor(private readonly globalService: GlobalService) {}

  /**
   * canActivate
   * -----------
   * Executed before the route handler.
   * Allows the request to proceed only if a valid session exists.
   *
   * @param context NestJS execution context
   * @returns boolean indicating whether the request is authorized
   * @throws UnauthorizedException when session validation fails
   */
  async canActivate(context: ExecutionContext): Promise<boolean> {
    // return true;
    // Retrieve the incoming HTTP request
    const request = context.switchToHttp().getRequest();

    // Extract session ID from request (cookie or Authorization header)
    const sessionId = this.extractSessionId(request);

    // Reject request if no session identifier is provided
    if (!sessionId) {
      throw new UnauthorizedException('Session ID not found');
    }

    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 authenticateUser
      // 💡 Route handlers can now access user data via req.user
      // request.user = payload;
      const authenticated =
        await this.globalService.authenticateUser(sessionId);
      if (!authenticated) {
        throw new UnauthorizedException('Invalid authentication token');
      }
      // console.log({ authenticated });
      const user = await this.globalService.getUser(authenticated.user_id);

      request.user = user;
      // console.log({ request });
      return !!user;
    } catch (error) {
      console.log('In SessionGuard - error:', error);
      // Token verification failed (expired, invalid signature, malformed)
      throw new UnauthorizedException(
        'Invalid or expired authentication token',
      );
    }
  }

  /**
   * extractSessionId
   * ----------------
   * Attempts to retrieve the session ID from:
   * 1. HTTP cookies (session_id)
   * 2. Authorization header (Bearer token)
   *
   * @param request Incoming HTTP request
   * @returns session ID string or null if not found
   */
  private extractSessionId(request: any): string | null {
    // Attempt to extract session ID from cookies
    const cookies = request.headers.cookie;

    if (cookies) {
      const sessionCookie = cookies
        .split(';')
        .find((cookie) => cookie.trim().startsWith('session_id='));

      if (sessionCookie) {
        return sessionCookie.split('=')[1];
      }
    }

    // Attempt to extract session ID from Authorization header
    const authHeader = request.headers.authorization;
    if (authHeader && authHeader.startsWith('Bearer ')) {
      return authHeader.substring(7);
    }

    // No session identifier found
    return null;
  }
}
