import { Injectable, Logger } from '@nestjs/common';
import { ApiKeyRepository } from '../repositories/api-key.repository';
import { InjectRepository } from '@nestjs/typeorm';
import { ApiKey } from '../entities/api-key.entity';
import { Repository } from 'typeorm';

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

  constructor(
    @InjectRepository(ApiKey)
    private apiKeyRepo: Repository<ApiKey>,
    private apiKeyRepository: ApiKeyRepository,
  ) {}

  // Method to get current usage statistics for an API key
  async getUsageStats(apiKeyId: string) {
    const apiKey = await this.apiKeyRepo.findOne({ where: { id: apiKeyId } });
    if (!apiKey) {
      return null;
    }

    return {
      minuteUsage: apiKey.minuteUsage,
      minuteLimit: apiKey.minuteLimit,
      dailyUsage: apiKey.dailyUsage,
      dailyLimit: apiKey.dailyLimit,
      monthlyUsage: apiKey.monthlyUsage,
      monthlyLimit: apiKey.monthlyLimit,
      totalUsage: apiKey.usageCount,
      lastUsed: apiKey.lastUsedAt,
    };
  }

  // Method to reset usage counters manually if needed
  async resetUsageCounters(apiKeyId: string) {
    const apiKey = await this.apiKeyRepo.findOne({ where: { id: apiKeyId } });
    if (!apiKey) {
      return false;
    }

    apiKey.minuteUsage = 0;
    apiKey.dailyUsage = 0;
    apiKey.monthlyUsage = 0;
    apiKey.lastMinuteReset = new Date();
    apiKey.lastDailyReset = new Date();
    apiKey.lastMonthlyReset = new Date();

    await this.apiKeyRepo.save(apiKey);
    return true;
  }

  async checkAndIncrementUsage(apiKeyId: string): Promise<boolean> {
    const apiKey = await this.apiKeyRepo.findOne({ where: { id: apiKeyId } });
    if (!apiKey) {
      return false;
    }

    const now = new Date();

    // Reset counters if needed
    this.resetCountersIfNeeded(apiKey, now);

    // Check if any limit is exceeded
    if (apiKey.minuteUsage >= apiKey.minuteLimit || apiKey.dailyUsage >= apiKey.dailyLimit || apiKey.monthlyUsage >= apiKey.monthlyLimit) {
      return false;
    }

    // Increment usage counters
    apiKey.minuteUsage += 1;
    apiKey.dailyUsage += 1;
    apiKey.monthlyUsage += 1;
    apiKey.usageCount += 1;
    apiKey.lastUsedAt = now;

    // Save updated usage stats
    await this.apiKeyRepo.save(apiKey);

    return true;
  }

  private resetCountersIfNeeded(apiKey: ApiKey, now: Date): void {
    // Reset minute counter if a minute has passed
    if (this.minutesPassed(apiKey.lastMinuteReset, now) >= 1) {
      apiKey.minuteUsage = 0;
      apiKey.lastMinuteReset = now;
    }

    // Reset daily counter if a day has passed
    if (this.daysPassed(apiKey.lastDailyReset, now) >= 1) {
      apiKey.dailyUsage = 0;
      apiKey.lastDailyReset = now;
    }

    // Reset monthly counter if a month has passed
    if (this.monthsPassed(apiKey.lastMonthlyReset, now) >= 1) {
      apiKey.monthlyUsage = 0;
      apiKey.lastMonthlyReset = now;
    }
  }

  private minutesPassed(date1: Date, date2: Date): number {
    const diffMs = date2.getTime() - date1.getTime();
    return Math.floor(diffMs / (1000 * 60));
  }

  private daysPassed(date1: Date, date2: Date): number {
    const d1 = new Date(date1);
    const d2 = new Date(date2);
    d1.setHours(0, 0, 0, 0);
    d2.setHours(0, 0, 0, 0);
    return Math.floor((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24));
  }

  private monthsPassed(date1: Date, date2: Date): number {
    return (date2.getFullYear() - date1.getFullYear()) * 12 + date2.getMonth() - date1.getMonth();
  }
}
