import { CanActivate, ExecutionContext, Injectable, Logger } from '@nestjs/common';
import { Request } from 'express';
import { RateLimitService } from 'modules/feature/api-key/rate-limit/rate-limit.service';
import { ApiKeyRepository } from 'modules/feature/api-key/repositories/api-key.repository';

@Injectable()
export class OptionalApiKeyGuard implements CanActivate {
  private readonly logger = new Logger(OptionalApiKeyGuard.name);

  constructor(
    private apiKeyRepository: ApiKeyRepository,
    private rateLimitService: RateLimitService,
  ) {}

  private isWebsiteRequest(req: Request): boolean {
    // Same implementation as in the controller
    const referer = req.headers.referer;
    this.logger.log('Checking referer', referer);
    const allowedOrigins = process.env.CORS_ORIGIN?.split(',') || [];
    if (referer && allowedOrigins.some(origin => referer.includes(origin))) {
      return true;
    }

    const sessionCookie = req.cookies['Authentication'];
    this.logger.log('Checking session cookie', sessionCookie);
    if (sessionCookie) {
      return true;
    }

    const websiteToken = req.headers['x-website-token'];
    if (websiteToken === process.env.WEBSITE_SECRET_TOKEN) {
      return true;
    }

    return false;
  }

  async canActivate(context: ExecutionContext): Promise<boolean> {
    this.logger.log('Checking API key');
    const request = context.switchToHttp().getRequest<Request>();
    const apiKey = this.extractApiKey(request);

    // Check if this is a website request
    const isWebsite = this.isWebsiteRequest(request);
    request['isWebsiteRequest'] = isWebsite;

    // For website requests, allow access without API key
    if (isWebsite) {
      this.logger.log('Website request detected - allowing as free tier');
      request['apiKey'] = null;
      request['user'] = null;
      request['tier'] = 'free';
      request['source'] = 'website';
      return true;
    }

    // For third-party API requests, validate the API key
    if (!apiKey) {
      // Allowing requests without API key, but marking them differently
      // The controller will decide how to handle them
      this.logger.log('Third-party request without API key');
      request['apiKey'] = null;
      request['user'] = null;
      request['tier'] = 'unauthorized';
      request['source'] = 'api';
      return true;
    }

    try {
      // Find the API key in the database
      const apiKeyEntity = await this.apiKeyRepository.findOne({
        where: { key: apiKey, isActive: true },
        relations: ['user'],
      });

      if (!apiKeyEntity) {
        this.logger.log('Invalid API key provided');
        request['apiKey'] = null;
        request['user'] = null;
        request['tier'] = 'unauthorized';
        request['source'] = 'api';
        return true;
      }

      // Update usage statistics and check rate limits
      const isWithinLimits = await this.rateLimitService.checkAndIncrementUsage(apiKeyEntity.id);
      if (!isWithinLimits) {
        this.logger.log('Rate limit exceeded');
        request['apiKey'] = null;
        request['user'] = null;
        request['tier'] = 'rate_limited';
        request['source'] = 'api';
        return true;
      }

      // Valid API key with limits not exceeded
      request['apiKey'] = apiKeyEntity;
      request['user'] = apiKeyEntity.user;
      request['tier'] = 'premium';
      request['source'] = 'api';
      this.logger.log('Valid API key - allowing as premium tier access');

      return true;
    } catch (error) {
      this.logger.error('Error validating API key', error);
      request['apiKey'] = null;
      request['user'] = null;
      request['tier'] = 'error';
      request['source'] = 'api';
      return true;
    }
  }

  private extractApiKey(request: Request): string | null {
    // Same implementation as before
    const authHeader = request.headers.authorization;
    if (authHeader && authHeader.split(' ')[0] === 'Bearer') {
      return authHeader.split(' ')[1];
    }

    const apiKeyHeader = request.headers['x-api-key'];
    if (apiKeyHeader) {
      return Array.isArray(apiKeyHeader) ? apiKeyHeader[0] : apiKeyHeader;
    }

    if (request.query.apiKey) {
      return request.query.apiKey as string;
    }

    return null;
  }
}
