/**
 * Submission Rate Limiter
 * 
 * Client-side rate limiting for submission creation to prevent
 * abuse and accidental duplicate submissions.
 * 
 * Part of Sacred Pipeline Phase 3: Enhanced Error Handling
 */

import { logger } from './logger';

interface RateLimitConfig {
  maxSubmissionsPerMinute: number;
  maxSubmissionsPerHour: number;
  cooldownAfterLimit: number; // milliseconds
}

interface RateLimitRecord {
  timestamps: number[];
  lastAttempt: number;
  blockedUntil?: number;
}

const DEFAULT_CONFIG: RateLimitConfig = {
  maxSubmissionsPerMinute: 5,
  maxSubmissionsPerHour: 20,
  cooldownAfterLimit: 60000, // 1 minute
};

// Store rate limit data in memory (per session)
const rateLimitStore = new Map<string, RateLimitRecord>();

/**
 * Clean up old timestamps from rate limit record
 */
function cleanupTimestamps(record: RateLimitRecord, now: number): void {
  const oneHourAgo = now - 60 * 60 * 1000;
  record.timestamps = record.timestamps.filter(ts => ts > oneHourAgo);
}

/**
 * Get or create rate limit record for user
 */
function getRateLimitRecord(userId: string): RateLimitRecord {
  if (!rateLimitStore.has(userId)) {
    rateLimitStore.set(userId, {
      timestamps: [],
      lastAttempt: 0,
    });
  }
  return rateLimitStore.get(userId)!;
}

/**
 * Check if user can submit based on rate limits
 * 
 * @param userId - User ID to check
 * @param config - Optional rate limit configuration
 * @returns Object indicating if allowed and retry information
 */
export function checkSubmissionRateLimit(
  userId: string,
  config: Partial<RateLimitConfig> = {}
): {
  allowed: boolean;
  reason?: string;
  retryAfter?: number; // seconds
  remaining?: number;
} {
  const cfg = { ...DEFAULT_CONFIG, ...config };
  const now = Date.now();
  const record = getRateLimitRecord(userId);

  // Clean up old timestamps
  cleanupTimestamps(record, now);

  // Check if user is currently blocked
  if (record.blockedUntil && now < record.blockedUntil) {
    const retryAfter = Math.ceil((record.blockedUntil - now) / 1000);
    
    logger.warn('[SubmissionRateLimiter] User blocked', {
      userId,
      retryAfter,
    });

    return {
      allowed: false,
      reason: `Rate limit exceeded. Please wait ${retryAfter} seconds before submitting again`,
      retryAfter,
    };
  }

  // Check per-minute limit
  const oneMinuteAgo = now - 60 * 1000;
  const submissionsLastMinute = record.timestamps.filter(ts => ts > oneMinuteAgo).length;

  if (submissionsLastMinute >= cfg.maxSubmissionsPerMinute) {
    record.blockedUntil = now + cfg.cooldownAfterLimit;
    const retryAfter = Math.ceil(cfg.cooldownAfterLimit / 1000);

    logger.warn('[SubmissionRateLimiter] Per-minute limit exceeded', {
      userId,
      submissionsLastMinute,
      limit: cfg.maxSubmissionsPerMinute,
      retryAfter,
    });

    return {
      allowed: false,
      reason: `Too many submissions in a short time. Please wait ${retryAfter} seconds`,
      retryAfter,
    };
  }

  // Check per-hour limit
  const submissionsLastHour = record.timestamps.length;

  if (submissionsLastHour >= cfg.maxSubmissionsPerHour) {
    record.blockedUntil = now + cfg.cooldownAfterLimit;
    const retryAfter = Math.ceil(cfg.cooldownAfterLimit / 1000);

    logger.warn('[SubmissionRateLimiter] Per-hour limit exceeded', {
      userId,
      submissionsLastHour,
      limit: cfg.maxSubmissionsPerHour,
      retryAfter,
    });

    return {
      allowed: false,
      reason: `Hourly submission limit reached. Please wait ${retryAfter} seconds`,
      retryAfter,
    };
  }

  // Calculate remaining submissions
  const remainingMinute = cfg.maxSubmissionsPerMinute - submissionsLastMinute;
  const remainingHour = cfg.maxSubmissionsPerHour - submissionsLastHour;
  const remaining = Math.min(remainingMinute, remainingHour);

  return {
    allowed: true,
    remaining,
  };
}

/**
 * Record a submission attempt
 * 
 * @param userId - User ID
 */
export function recordSubmissionAttempt(userId: string): void {
  const now = Date.now();
  const record = getRateLimitRecord(userId);

  record.timestamps.push(now);
  record.lastAttempt = now;

  // Clean up immediately to maintain accurate counts
  cleanupTimestamps(record, now);

  logger.info('[SubmissionRateLimiter] Recorded submission', {
    userId,
    totalLastHour: record.timestamps.length,
  });
}

/**
 * Clear rate limit for user (useful for testing or admin override)
 * 
 * @param userId - User ID to clear
 */
export function clearUserRateLimit(userId: string): void {
  rateLimitStore.delete(userId);
  logger.info('[SubmissionRateLimiter] Cleared rate limit', { userId });
}

/**
 * Get current rate limit status for user
 * 
 * @param userId - User ID
 * @returns Current status information
 */
export function getRateLimitStatus(userId: string): {
  submissionsLastMinute: number;
  submissionsLastHour: number;
  isBlocked: boolean;
  blockedUntil?: Date;
} {
  const now = Date.now();
  const record = getRateLimitRecord(userId);

  cleanupTimestamps(record, now);

  const oneMinuteAgo = now - 60 * 1000;
  const submissionsLastMinute = record.timestamps.filter(ts => ts > oneMinuteAgo).length;

  return {
    submissionsLastMinute,
    submissionsLastHour: record.timestamps.length,
    isBlocked: !!(record.blockedUntil && now < record.blockedUntil),
    blockedUntil: record.blockedUntil ? new Date(record.blockedUntil) : undefined,
  };
}
