import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

/**
 * Input length constants for form fields
 */
export const INPUT_LIMITS = {
  // Standard text fields
  NAME_MIN: 2,
  NAME_MAX: 200,
  CODE_MAX: 50,

  // Description/textarea fields
  DESCRIPTION_MAX: 2000,
  ADDRESS_MAX: 500,

  // Contact fields
  EMAIL_MAX: 254, // RFC 5321
  PHONE_MAX: 20,

  // URL fields
  URL_MAX: 2048,

  // Identifiers
  ID_MAX: 100,

  // Large text areas
  NOTES_MAX: 5000,
} as const;

/**
 * Patterns for dangerous content detection
 */
const DANGEROUS_PATTERNS = {
  SCRIPT_TAG: /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
  EVENT_HANDLER: /\bon\w+\s*=/gi,
  JAVASCRIPT_URI: /javascript:/gi,
  DATA_URI: /data:[^,]*;base64/gi,
  NULL_BYTE: /\x00/g,
  HTML_ENTITIES: /&#x?[0-9a-f]+;?/gi,
} as const;

/**
 * Validates that input does not contain script tags or event handlers
 */
export function noScriptValidator(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    if (!control.value || typeof control.value !== 'string') {
      return null;
    }

    const value = control.value;

    if (DANGEROUS_PATTERNS.SCRIPT_TAG.test(value) ||
        DANGEROUS_PATTERNS.EVENT_HANDLER.test(value) ||
        DANGEROUS_PATTERNS.JAVASCRIPT_URI.test(value)) {
      return { dangerousContent: true };
    }

    return null;
  };
}

/**
 * Validates that input does not contain null bytes
 */
export function noNullBytesValidator(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    if (!control.value || typeof control.value !== 'string') {
      return null;
    }

    if (DANGEROUS_PATTERNS.NULL_BYTE.test(control.value)) {
      return { nullBytes: true };
    }

    return null;
  };
}

/**
 * Validates that input does not contain only whitespace
 */
export function notOnlyWhitespaceValidator(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    if (!control.value || typeof control.value !== 'string') {
      return null;
    }

    if (control.value.trim().length === 0 && control.value.length > 0) {
      return { onlyWhitespace: true };
    }

    return null;
  };
}

/**
 * Phone number validator (international format)
 */
export function phoneValidator(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    if (!control.value || typeof control.value !== 'string') {
      return null;
    }

    // Allow: digits, spaces, hyphens, parentheses, plus sign
    // Minimum 6 digits, maximum 15 (E.164 standard)
    const cleaned = control.value.replace(/[\s\-\(\)]/g, '');
    const phoneRegex = /^\+?[0-9]{6,15}$/;

    if (!phoneRegex.test(cleaned)) {
      return { invalidPhone: true };
    }

    return null;
  };
}

/**
 * Validates URL format more strictly
 */
export function strictUrlValidator(requireHttps = false): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    if (!control.value || typeof control.value !== 'string') {
      return null;
    }

    const value = control.value.trim();

    // Check length
    if (value.length > INPUT_LIMITS.URL_MAX) {
      return { urlTooLong: true };
    }

    // Check for dangerous content
    if (DANGEROUS_PATTERNS.JAVASCRIPT_URI.test(value) ||
        DANGEROUS_PATTERNS.DATA_URI.test(value)) {
      return { dangerousUrl: true };
    }

    // Validate URL format
    try {
      const url = new URL(value);

      if (requireHttps && url.protocol !== 'https:') {
        return { httpsRequired: true };
      }

      if (!['http:', 'https:'].includes(url.protocol)) {
        return { invalidProtocol: true };
      }

      return null;
    } catch {
      return { invalidUrl: true };
    }
  };
}

/**
 * Composite validator that applies common security checks
 */
export function secureInputValidator(): ValidatorFn {
  return (control: AbstractControl): ValidationErrors | null => {
    const noScript = noScriptValidator()(control);
    const noNull = noNullBytesValidator()(control);
    const notWhitespace = notOnlyWhitespaceValidator()(control);

    const errors = { ...noScript, ...noNull, ...notWhitespace };
    return Object.keys(errors).length > 0 ? errors : null;
  };
}

/**
 * Sanitizes user input by removing dangerous content
 * Use this before displaying user-generated content
 */
export function sanitizeInput(value: string | null | undefined): string {
  if (!value || typeof value !== 'string') {
    return '';
  }

  let sanitized = value;

  // Remove null bytes
  sanitized = sanitized.replace(DANGEROUS_PATTERNS.NULL_BYTE, '');

  // Encode HTML special characters
  sanitized = sanitized
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#x27;');

  return sanitized;
}

/**
 * Trims and normalizes whitespace in input
 */
export function normalizeWhitespace(value: string | null | undefined): string {
  if (!value || typeof value !== 'string') {
    return '';
  }

  return value.trim().replace(/\s+/g, ' ');
}
