/**
 * Antibot Utilities
 * Provides various bot detection mechanisms
 */

export interface BrowserFingerprint {
  userAgent: string;
  language: string;
  platform: string;
  screenResolution: string;
  timezone: string;
  cookieEnabled: boolean;
  doNotTrack: string | null;
  hardwareConcurrency: number;
  deviceMemory: number | undefined;
  canvasFingerprint: string;
  webglFingerprint: string;
}

export interface MouseMovement {
  x: number;
  y: number;
  timestamp: number;
}

export interface FormTiming {
  pageLoadTime: number;
  formStartTime: number | null;
  formSubmitTime: number | null;
  timeOnPage: number;
  timeToFill: number | null;
  mouseMovements: MouseMovement[];
  clickCount: number;
  keyPressCount: number;
}

/**
 * Generate browser fingerprint
 */
export const generateBrowserFingerprint = (): BrowserFingerprint => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  let canvasFingerprint = '';
  
  if (ctx) {
    ctx.textBaseline = 'top';
    ctx.font = '14px Arial';
    ctx.textBaseline = 'alphabetic';
    ctx.fillStyle = '#f60';
    ctx.fillRect(125, 1, 62, 20);
    ctx.fillStyle = '#069';
    ctx.fillText('Browser fingerprint', 2, 15);
    ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
    ctx.fillText('Browser fingerprint', 4, 17);
    canvasFingerprint = canvas.toDataURL();
  }

  // WebGL fingerprint
  let webglFingerprint = '';
  try {
    // Try WebGL2 first (modern approach - uses RENDERER constant directly)
    const canvas = document.createElement('canvas');
    let gl: WebGL2RenderingContext | WebGLRenderingContext | null = 
      canvas.getContext('webgl2');
    
    if (gl) {
      // WebGL2: Use RENDERER and VENDOR constants directly (non-deprecated)
      // These are standard WebGL2 constants: 0x1F00 (VENDOR) and 0x1F01 (RENDERER)
      try {
        const vendor = gl.getParameter(0x1F00); // gl.VENDOR
        const renderer = gl.getParameter(0x1F01); // gl.RENDERER
        if (vendor && renderer) {
          webglFingerprint = `${vendor}|${renderer}`;
        }
      } catch (e) {
        // Fallback if direct access fails
      }
    } else {
      // Fallback to WebGL1 with extension (deprecated in Firefox, but still works in other browsers)
      gl = canvas.getContext('webgl');
      if (gl) {
        // Only use deprecated extension if WebGL2 is not available
        // This will show deprecation warning in Firefox but is necessary for WebGL1 compatibility
        const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
        if (debugInfo) {
          try {
            webglFingerprint = `${gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL)}|${gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL)}`;
          } catch (e) {
            // Extension may not be available
          }
        }
      }
    }
  } catch (e) {
    // WebGL not supported
  }

  return {
    userAgent: navigator.userAgent,
    language: navigator.language,
    platform: navigator.platform,
    screenResolution: `${screen.width}x${screen.height}`,
    timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    cookieEnabled: navigator.cookieEnabled,
    doNotTrack: navigator.doNotTrack || null,
    hardwareConcurrency: navigator.hardwareConcurrency || 0,
    deviceMemory: (navigator as Navigator & { deviceMemory?: number }).deviceMemory,
    canvasFingerprint: canvasFingerprint.substring(0, 100), // Truncate for storage
    webglFingerprint,
  };
};

/**
 * Hash a string (simple hash function)
 */
export const hashString = (str: string): string => {
  let hash = 0;
  for (let i = 0; i < str.length; i++) {
    const char = str.charCodeAt(i);
    hash = ((hash << 5) - hash) + char;
    hash = hash & hash; // Convert to 32bit integer
  }
  return Math.abs(hash).toString(36);
};

/**
 * Generate a simple fingerprint hash
 */
export const getFingerprintHash = (): string => {
  const fingerprint = generateBrowserFingerprint();
  const fingerprintString = JSON.stringify(fingerprint);
  return hashString(fingerprintString);
};

/**
 * Check if form was filled too quickly (bot indicator)
 */
export const isFormFilledTooQuickly = (timeToFill: number | null, minTimeSeconds: number = 3): boolean => {
  if (timeToFill === null) return false;
  return timeToFill < minTimeSeconds * 1000;
};

/**
 * Check if user spent enough time on page
 */
export const hasSpentEnoughTimeOnPage = (timeOnPage: number, minTimeSeconds: number = 5): boolean => {
  return timeOnPage >= minTimeSeconds * 1000;
};

/**
 * Check if mouse movements are suspicious (too linear, too few, etc.)
 */
export const areMouseMovementsSuspicious = (movements: MouseMovement[]): boolean => {
  if (movements.length < 3) return true; // Too few movements
  
  // Check for linear movement (bot indicator)
  if (movements.length >= 3) {
    const first = movements[0];
    const last = movements[movements.length - 1];
    const totalDistance = Math.sqrt(
      Math.pow(last.x - first.x, 2) + Math.pow(last.y - first.y, 2)
    );
    
    // Calculate actual path distance
    let pathDistance = 0;
    for (let i = 1; i < movements.length; i++) {
      const prev = movements[i - 1];
      const curr = movements[i];
      pathDistance += Math.sqrt(
        Math.pow(curr.x - prev.x, 2) + Math.pow(curr.y - prev.y, 2)
      );
    }
    
    // If path is too close to straight line, it's suspicious
    if (totalDistance > 0 && pathDistance / totalDistance < 1.1) {
      return true;
    }
  }
  
  return false;
};

/**
 * Check if interaction patterns are suspicious
 */
export const areInteractionPatternsSuspicious = (
  clickCount: number,
  keyPressCount: number,
  timeOnPage: number
): boolean => {
  // If user has been on page for a while but has no interactions
  if (timeOnPage > 10000 && clickCount === 0 && keyPressCount === 0) {
    return true;
  }
  
  // If too many clicks/keypresses in short time (automation)
  const totalInteractions = clickCount + keyPressCount;
  const interactionsPerSecond = totalInteractions / (timeOnPage / 1000);
  if (interactionsPerSecond > 10) {
    return true;
  }
  
  return false;
};

/**
 * Check rate limit status without recording an attempt (read-only)
 */
export const checkRateLimitStatus = (
  action: string,
  maxAttempts: number = 5,
  windowMs: number = 15 * 60 * 1000 // 15 minutes
): { allowed: boolean; remainingAttempts: number; resetTime: number } => {
  const key = `antibot_rate_limit_${action}`;
  const now = Date.now();
  
  try {
    const stored = localStorage.getItem(key);
    if (stored) {
      const data = JSON.parse(stored);
      const attempts = data.attempts.filter((timestamp: number) => now - timestamp < windowMs);
      
      if (attempts.length >= maxAttempts) {
        const oldestAttempt = Math.min(...attempts);
        const resetTime = oldestAttempt + windowMs;
        return {
          allowed: false,
          remainingAttempts: 0,
          resetTime,
        };
      }
      
      return {
        allowed: true,
        remainingAttempts: maxAttempts - attempts.length,
        resetTime: now + windowMs,
      };
    } else {
      return {
        allowed: true,
        remainingAttempts: maxAttempts,
        resetTime: now + windowMs,
      };
    }
  } catch (e) {
    // If localStorage fails, allow the action
    console.error('Rate limit check failed:', e);
    return {
      allowed: true,
      remainingAttempts: maxAttempts,
      resetTime: now + windowMs,
    };
  }
};

/**
 * Rate limiting check using localStorage (records an attempt)
 */
export const checkRateLimit = (
  action: string,
  maxAttempts: number = 5,
  windowMs: number = 15 * 60 * 1000 // 15 minutes
): { allowed: boolean; remainingAttempts: number; resetTime: number } => {
  const key = `antibot_rate_limit_${action}`;
  const now = Date.now();
  
  try {
    const stored = localStorage.getItem(key);
    if (stored) {
      const data = JSON.parse(stored);
      const attempts = data.attempts.filter((timestamp: number) => now - timestamp < windowMs);
      
      if (attempts.length >= maxAttempts) {
        const oldestAttempt = Math.min(...attempts);
        const resetTime = oldestAttempt + windowMs;
        return {
          allowed: false,
          remainingAttempts: 0,
          resetTime,
        };
      }
      
      // Only record attempt if allowed
      attempts.push(now);
      localStorage.setItem(key, JSON.stringify({ attempts }));
      
      return {
        allowed: true,
        remainingAttempts: maxAttempts - attempts.length,
        resetTime: now + windowMs,
      };
    } else {
      localStorage.setItem(key, JSON.stringify({ attempts: [now] }));
      return {
        allowed: true,
        remainingAttempts: maxAttempts - 1,
        resetTime: now + windowMs,
      };
    }
  } catch (e) {
    // If localStorage fails, allow the action
    console.error('Rate limit check failed:', e);
    return {
      allowed: true,
      remainingAttempts: maxAttempts,
      resetTime: now + windowMs,
    };
  }
};

/**
 * Clear rate limit for an action
 */
export const clearRateLimit = (action: string): void => {
  const key = `antibot_rate_limit_${action}`;
  try {
    localStorage.removeItem(key);
  } catch (e) {
    console.error('Failed to clear rate limit:', e);
  }
};

/**
 * Validate antibot data before form submission
 */
export interface AntibotValidationResult {
  isValid: boolean;
  errors: string[];
  warnings: string[];
}

export const validateAntibotData = (
  timing: FormTiming,
  honeypotValue: string,
  recaptchaToken: string | null,
  options: {
    minTimeOnPage?: number;
    minTimeToFill?: number;
    requireRecaptcha?: boolean;
    checkMouseMovements?: boolean;
  } = {}
): AntibotValidationResult => {
  const errors: string[] = [];
  const warnings: string[] = [];
  
  const {
    minTimeOnPage = 5000,
    minTimeToFill = 3000,
    requireRecaptcha = false,
    checkMouseMovements = true,
  } = options;
  
  // Check honeypot
  if (honeypotValue && honeypotValue.trim() !== '') {
    errors.push('Bot detected: Honeypot field was filled');
  }
  
  // Check time on page
  if (!hasSpentEnoughTimeOnPage(timing.timeOnPage, minTimeOnPage / 1000)) {
    errors.push(`Please spend at least ${minTimeOnPage / 1000} seconds on this page`);
  }
  
  // Check time to fill form
  if (timing.timeToFill !== null && isFormFilledTooQuickly(timing.timeToFill, minTimeToFill / 1000)) {
    errors.push('Form was filled too quickly. Please take your time.');
  }
  
  // Check mouse movements
  if (checkMouseMovements && areMouseMovementsSuspicious(timing.mouseMovements)) {
    warnings.push('Unusual mouse movement pattern detected');
  }
  
  // Check interaction patterns
  if (areInteractionPatternsSuspicious(timing.clickCount, timing.keyPressCount, timing.timeOnPage)) {
    warnings.push('Unusual interaction pattern detected');
  }
  
  // Check reCAPTCHA if required
  if (requireRecaptcha && !recaptchaToken) {
    errors.push('Please complete the reCAPTCHA verification');
  }
  
  return {
    isValid: errors.length === 0,
    errors,
    warnings,
  };
};

