import { Injectable, Logger } from '@nestjs/common';
import { CacheService } from '../../../common/services/cache.service';

export interface RateLimitCheck {
  allowed: boolean;
  remaining?: number;
  resetAt?: Date;
  reason?: string;
}

export interface SuspiciousActivity {
  userId: string;
  pattern: string;
  count: number;
  firstOccurrence: Date;
  lastOccurrence: Date;
}

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

  // Rate limit tiers
  private readonly FREE_TIER_DAILY_LIMIT = 10;
  private readonly PREMIUM_TIER_DAILY_LIMIT = 200; // Fair use
  private readonly SUSPICIOUS_HOURLY_LIMIT = 1; // When flagged

  constructor(private cacheService: CacheService) {}

  /**
   * Check if user can make an AI query
   */
  async checkRateLimit(
    userId: string,
    isPremium: boolean = false,
  ): Promise<RateLimitCheck> {
    // Check if user is under temporary restriction
    const isRestricted = await this.isTemporarilyRestricted(userId);
    if (isRestricted) {
      const restrictionInfo = await this.getRestrictionInfo(userId);
      return {
        allowed: false,
        resetAt: restrictionInfo.expiresAt,
        reason: 'Account temporarily restricted due to suspicious activity',
      };
    }

    // Check daily limit
    const dailyKey = `ai:ratelimit:${userId}:daily:${this.getTodayKey()}`;
    const dailyCount = await this.cacheService.get<number>(dailyKey);
    const currentCount = dailyCount || 0;

    const dailyLimit = isPremium
      ? this.PREMIUM_TIER_DAILY_LIMIT
      : this.FREE_TIER_DAILY_LIMIT;

    if (currentCount >= dailyLimit) {
      const resetAt = this.getTomorrowMidnight();
      return {
        allowed: false,
        remaining: 0,
        resetAt,
        reason: `Daily limit of ${dailyLimit} queries reached`,
      };
    }

    return {
      allowed: true,
      remaining: dailyLimit - currentCount,
    };
  }

  /**
   * Increment rate limit counter
   */
  async incrementCounter(userId: string): Promise<void> {
    const dailyKey = `ai:ratelimit:${userId}:daily:${this.getTodayKey()}`;
    const ttl = this.getSecondsUntilMidnight();

    const currentCount = (await this.cacheService.get<number>(dailyKey)) || 0;
    await this.cacheService.set(dailyKey, currentCount + 1, ttl);
  }

  /**
   * Track query for suspicious pattern detection
   */
  async trackQuery(
    userId: string,
    query: string,
    trigger?: string,
  ): Promise<void> {
    // Track same question repetition
    const queryHash = this.hashQuery(query);
    const hourKey = `ai:pattern:${userId}:query:${queryHash}:${this.getCurrentHourKey()}`;

    const hourCount = (await this.cacheService.get<number>(hourKey)) || 0;
    await this.cacheService.set(hourKey, hourCount + 1, 3600); // 1 hour TTL

    // Check for suspicious repetition (same query >3 times in 1 hour)
    if (hourCount + 1 > 3) {
      await this.flagSuspiciousActivity(
        userId,
        'repeated_query',
        hourCount + 1,
      );
    }

    // Track emergency/crisis keyword spam
    if (trigger === 'emergency' || trigger === 'crisis') {
      const dayKey = `ai:pattern:${userId}:${trigger}:${this.getTodayKey()}`;
      const dayCount = (await this.cacheService.get<number>(dayKey)) || 0;
      await this.cacheService.set(dayKey, dayCount + 1, 86400); // 24 hours

      // Flag if too many emergency/crisis queries
      if (trigger === 'emergency' && dayCount + 1 > 5) {
        await this.flagSuspiciousActivity(
          userId,
          'emergency_spam',
          dayCount + 1,
        );
      }

      if (trigger === 'crisis' && dayCount + 1 > 5) {
        // Crisis keywords repeated many times - may need help or testing system
        this.logger.warn(
          `User ${userId} triggered crisis keywords ${dayCount + 1} times today - may need urgent support`,
        );
        // Don't flag as abuse - they may genuinely need help
      }
    }

    // Track unusual volume
    const volumeKey = `ai:pattern:${userId}:volume:${this.getTodayKey()}`;
    const volumeCount = (await this.cacheService.get<number>(volumeKey)) || 0;
    await this.cacheService.set(volumeKey, volumeCount + 1, 86400);

    // Flag if volume is extremely high (>100/day even for premium)
    if (volumeCount + 1 > 100) {
      await this.flagSuspiciousActivity(
        userId,
        'unusual_volume',
        volumeCount + 1,
      );
    }
  }

  /**
   * Flag suspicious activity
   */
  private async flagSuspiciousActivity(
    userId: string,
    pattern: string,
    count: number,
  ): Promise<void> {
    const flagKey = `ai:suspicious:${userId}:${pattern}`;
    const existing = await this.cacheService.get<SuspiciousActivity>(flagKey);

    const activity: SuspiciousActivity = {
      userId,
      pattern,
      count,
      firstOccurrence: existing?.firstOccurrence || new Date(),
      lastOccurrence: new Date(),
    };

    await this.cacheService.set(flagKey, activity, 86400); // 24 hours

    this.logger.warn(
      `Suspicious activity detected: ${pattern} for user ${userId} (count: ${count})`,
    );

    // Apply temporary restriction for severe patterns
    if (pattern === 'emergency_spam' || pattern === 'unusual_volume') {
      await this.applyTemporaryRestriction(userId, pattern);
    }

    // TODO: Store in database for manual review
    // await this.securityRepository.save({ userId, pattern, count, timestamp: new Date() });
  }

  /**
   * Apply temporary restriction (1 query per hour for 24 hours)
   */
  private async applyTemporaryRestriction(
    userId: string,
    reason: string,
  ): Promise<void> {
    const restrictionKey = `ai:restricted:${userId}`;
    const restriction = {
      reason,
      appliedAt: new Date(),
      expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000), // 24 hours
    };

    await this.cacheService.set(restrictionKey, restriction, 86400);

    this.logger.warn(
      `Temporary restriction applied to user ${userId}: ${reason}`,
    );

    // TODO: Send email notification to user
    // TODO: Log to audit log
  }

  /**
   * Check if user is temporarily restricted
   */
  private async isTemporarilyRestricted(userId: string): Promise<boolean> {
    const restrictionKey = `ai:restricted:${userId}`;
    const restriction = await this.cacheService.get(restrictionKey);
    return !!restriction;
  }

  /**
   * Get restriction info
   */
  private async getRestrictionInfo(userId: string): Promise<any> {
    const restrictionKey = `ai:restricted:${userId}`;
    return await this.cacheService.get(restrictionKey);
  }

  /**
   * Get remaining queries for user
   */
  async getRemainingQueries(
    userId: string,
    isPremium: boolean = false,
  ): Promise<number> {
    const check = await this.checkRateLimit(userId, isPremium);
    return check.remaining || 0;
  }

  /**
   * Get suspicious activity flags for user
   */
  async getSuspiciousActivityFlags(
    userId: string,
  ): Promise<SuspiciousActivity[]> {
    const patterns = [
      'repeated_query',
      'emergency_spam',
      'crisis_spam',
      'unusual_volume',
    ];
    const flags: SuspiciousActivity[] = [];

    for (const pattern of patterns) {
      const flagKey = `ai:suspicious:${userId}:${pattern}`;
      const activity =
        await this.cacheService.get<SuspiciousActivity>(flagKey);
      if (activity) {
        flags.push(activity);
      }
    }

    return flags;
  }

  /**
   * Clear restriction for user (admin action)
   */
  async clearRestriction(userId: string): Promise<void> {
    const restrictionKey = `ai:restricted:${userId}`;
    await this.cacheService.delete(restrictionKey);
    this.logger.log(`Restriction cleared for user ${userId}`);
  }

  /**
   * Get current usage stats for user
   */
  async getUsageStats(
    userId: string,
    isPremium: boolean = false,
  ): Promise<{
    dailyUsed: number;
    dailyLimit: number;
    remaining: number;
    resetAt: Date;
    isRestricted: boolean;
    suspiciousFlags: SuspiciousActivity[];
  }> {
    const dailyKey = `ai:ratelimit:${userId}:daily:${this.getTodayKey()}`;
    const dailyUsed = (await this.cacheService.get<number>(dailyKey)) || 0;

    const dailyLimit = isPremium
      ? this.PREMIUM_TIER_DAILY_LIMIT
      : this.FREE_TIER_DAILY_LIMIT;

    const isRestricted = await this.isTemporarilyRestricted(userId);
    const suspiciousFlags = await this.getSuspiciousActivityFlags(userId);

    return {
      dailyUsed,
      dailyLimit,
      remaining: Math.max(0, dailyLimit - dailyUsed),
      resetAt: this.getTomorrowMidnight(),
      isRestricted,
      suspiciousFlags,
    };
  }

  /**
   * Hash query for deduplication (simple hash)
   */
  private hashQuery(query: string): string {
    // Normalize: lowercase, remove extra spaces, truncate
    const normalized = query.toLowerCase().replace(/\s+/g, ' ').substring(0, 100);
    // Simple hash for cache key
    let hash = 0;
    for (let i = 0; i < normalized.length; i++) {
      const char = normalized.charCodeAt(i);
      hash = (hash << 5) - hash + char;
      hash = hash & hash; // Convert to 32-bit integer
    }
    return Math.abs(hash).toString(36);
  }

  /**
   * Get today's date key (YYYY-MM-DD)
   */
  private getTodayKey(): string {
    return new Date().toISOString().split('T')[0];
  }

  /**
   * Get current hour key (YYYY-MM-DD-HH)
   */
  private getCurrentHourKey(): string {
    const now = new Date();
    return `${now.toISOString().split('T')[0]}-${now.getHours().toString().padStart(2, '0')}`;
  }

  /**
   * Get midnight tomorrow
   */
  private getTomorrowMidnight(): Date {
    const tomorrow = new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);
    tomorrow.setHours(0, 0, 0, 0);
    return tomorrow;
  }

  /**
   * Get seconds until midnight
   */
  private getSecondsUntilMidnight(): number {
    const now = new Date();
    const midnight = new Date(now);
    midnight.setHours(24, 0, 0, 0);
    return Math.floor((midnight.getTime() - now.getTime()) / 1000);
  }
}
