import * as plugins from './plugins.js';
import type { IPortProxySettings } from './classes.pp.interfaces.js';

/**
 * Handles security aspects like IP tracking, rate limiting, and authorization
 */
export class SecurityManager {
  private connectionsByIP: Map<string, Set<string>> = new Map();
  private connectionRateByIP: Map<string, number[]> = new Map();
  
  constructor(private settings: IPortProxySettings) {}
  
  /**
   * Get connections count by IP
   */
  public getConnectionCountByIP(ip: string): number {
    return this.connectionsByIP.get(ip)?.size || 0;
  }
  
  /**
   * Check and update connection rate for an IP
   * @returns true if within rate limit, false if exceeding limit
   */
  public checkConnectionRate(ip: string): boolean {
    const now = Date.now();
    const minute = 60 * 1000;

    if (!this.connectionRateByIP.has(ip)) {
      this.connectionRateByIP.set(ip, [now]);
      return true;
    }

    // Get timestamps and filter out entries older than 1 minute
    const timestamps = this.connectionRateByIP.get(ip)!.filter((time) => now - time < minute);
    timestamps.push(now);
    this.connectionRateByIP.set(ip, timestamps);

    // Check if rate exceeds limit
    return timestamps.length <= this.settings.connectionRateLimitPerMinute!;
  }
  
  /**
   * Track connection by IP
   */
  public trackConnectionByIP(ip: string, connectionId: string): void {
    if (!this.connectionsByIP.has(ip)) {
      this.connectionsByIP.set(ip, new Set());
    }
    this.connectionsByIP.get(ip)!.add(connectionId);
  }
  
  /**
   * Remove connection tracking for an IP
   */
  public removeConnectionByIP(ip: string, connectionId: string): void {
    if (this.connectionsByIP.has(ip)) {
      const connections = this.connectionsByIP.get(ip)!;
      connections.delete(connectionId);
      if (connections.size === 0) {
        this.connectionsByIP.delete(ip);
      }
    }
  }
  
  /**
   * Check if an IP is allowed using glob patterns
   */
  public isIPAuthorized(ip: string, allowedIPs: string[], blockedIPs: string[] = []): boolean {
    // Skip IP validation if allowedIPs is empty
    if (!ip || (allowedIPs.length === 0 && blockedIPs.length === 0)) {
      return true;
    }
    
    // First check if IP is blocked
    if (blockedIPs.length > 0 && this.isGlobIPMatch(ip, blockedIPs)) {
      return false;
    }
    
    // Then check if IP is allowed
    return this.isGlobIPMatch(ip, allowedIPs);
  }
  
  /**
   * Check if the IP matches any of the glob patterns
   */
  private isGlobIPMatch(ip: string, patterns: string[]): boolean {
    if (!ip || !patterns || patterns.length === 0) return false;

    const normalizeIP = (ip: string): string[] => {
      if (!ip) return [];
      if (ip.startsWith('::ffff:')) {
        const ipv4 = ip.slice(7);
        return [ip, ipv4];
      }
      if (/^\d{1,3}(\.\d{1,3}){3}$/.test(ip)) {
        return [ip, `::ffff:${ip}`];
      }
      return [ip];
    };

    const normalizedIPVariants = normalizeIP(ip);
    if (normalizedIPVariants.length === 0) return false;

    const expandedPatterns = patterns.flatMap(normalizeIP);
    return normalizedIPVariants.some((ipVariant) =>
      expandedPatterns.some((pattern) => plugins.minimatch(ipVariant, pattern))
    );
  }
  
  /**
   * Check if IP should be allowed considering connection rate and max connections
   * @returns Object with result and reason
   */
  public validateIP(ip: string): { allowed: boolean; reason?: string } {
    // Check connection count limit
    if (
      this.settings.maxConnectionsPerIP &&
      this.getConnectionCountByIP(ip) >= this.settings.maxConnectionsPerIP
    ) {
      return {
        allowed: false,
        reason: `Maximum connections per IP (${this.settings.maxConnectionsPerIP}) exceeded`
      };
    }

    // Check connection rate limit
    if (
      this.settings.connectionRateLimitPerMinute && 
      !this.checkConnectionRate(ip)
    ) {
      return {
        allowed: false,
        reason: `Connection rate limit (${this.settings.connectionRateLimitPerMinute}/min) exceeded`
      };
    }
    
    return { allowed: true };
  }
  
  /**
   * Clears all IP tracking data (for shutdown)
   */
  public clearIPTracking(): void {
    this.connectionsByIP.clear();
    this.connectionRateByIP.clear();
  }
}