const REGEX_PATTERNS = {
  IPV4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
  IPV6: /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/,
  MACHINE_NAME: /[^a-zA-Z0-9\-_\s]/g,
  USERNAME: /[^a-z0-9@._-]/g,
  CONTROL_CHARS: /[\x00-\x1f\x7f]/,
  TEXT_SANITIZE: /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g,
} as const;

const VALIDATION_LIMITS = {
  MACHINE_NAME: { MAX: 50 },
  USERNAME: { MIN: 3, MAX: 50 },
  PASSWORD: { MIN: 8, MAX: 128 },
  TEXT: { MAX: 1000 },
  PORT: { MIN: 1, MAX: 65535 },
} as const;

const ALLOWED_PROTOCOLS = ['rdp', 'vnc', 'ssh', 'telnet'] as const;
type AllowedProtocol = typeof ALLOWED_PROTOCOLS[number];

export class InputSanitizer {
  /**
   * Sanitize and validate IP address
   */
  static sanitizeIpAddress(ip: string): string {
    const cleanIp = ip.trim().replace(/[^0-9.:a-fA-F]/g, '');
    
    if (REGEX_PATTERNS.IPV4.test(cleanIp) || REGEX_PATTERNS.IPV6.test(cleanIp)) {
      return cleanIp;
    }
    
    throw new Error('Invalid IP address format');
  }

  /**
   * Sanitize machine name
   */
  static sanitizeMachineName(name: string): string {
    this.validateStringInput(name, 'Machine name');

    const sanitized = name.trim().replace(REGEX_PATTERNS.MACHINE_NAME, '');
    
    if (sanitized.length === 0) {
      throw new Error('Machine name cannot be empty');
    }
    
    if (sanitized.length > VALIDATION_LIMITS.MACHINE_NAME.MAX) {
      throw new Error(`Machine name too long (max ${VALIDATION_LIMITS.MACHINE_NAME.MAX} characters)`);
    }
    
    return sanitized;
  }

  /**
   * Sanitize username
   */
  static sanitizeUsername(username: string): string {
    this.validateStringInput(username, 'Username');

    const sanitized = username.trim().toLowerCase().replace(REGEX_PATTERNS.USERNAME, '');
    
    if (sanitized.length < VALIDATION_LIMITS.USERNAME.MIN) {
      throw new Error(`Username too short (min ${VALIDATION_LIMITS.USERNAME.MIN} characters)`);
    }
    
    if (sanitized.length > VALIDATION_LIMITS.USERNAME.MAX) {
      throw new Error(`Username too long (max ${VALIDATION_LIMITS.USERNAME.MAX} characters)`);
    }
    
    return sanitized;
  }

  /**
   * Validate password (no sanitization, only validation)
   */
  static validatePassword(password: string): boolean {
    this.validateStringInput(password, 'Password');

    if (password.length < VALIDATION_LIMITS.PASSWORD.MIN) {
      throw new Error(`Password too short (min ${VALIDATION_LIMITS.PASSWORD.MIN} characters)`);
    }
    
    if (password.length > VALIDATION_LIMITS.PASSWORD.MAX) {
      throw new Error(`Password too long (max ${VALIDATION_LIMITS.PASSWORD.MAX} characters)`);
    }
    
    if (REGEX_PATTERNS.CONTROL_CHARS.test(password)) {
      throw new Error('Password contains invalid characters');
    }
    
    return true;
  }

  /**
   * Sanitize URL for Guacamole
   */
  static sanitizeUrl(url: string): string {
    if (!url || typeof url !== 'string') {
      throw new Error('URL is required');
    }

    try {
      const parsedUrl = new URL(url.trim());
      
      // Allow only HTTP/HTTPS
      if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
        throw new Error('Invalid protocol. Only HTTP and HTTPS are allowed');
      }
      
      // Check that host is not localhost in production
      if (process.env.NODE_ENV === 'production' && 
          ['localhost', '127.0.0.1', '0.0.0.0'].includes(parsedUrl.hostname)) {
        throw new Error('Localhost URLs not allowed in production');
      }
      
      return parsedUrl.toString();
    } catch (error) {
      if (error instanceof Error && error.message.includes('Invalid URL')) {
        throw new Error('Invalid URL format');
      }
      throw error;
    }
  }

  /**
   * Sanitize general purpose text fields
   */
  static sanitizeText(text: string, maxLength: number = VALIDATION_LIMITS.TEXT.MAX): string {
    if (!text || typeof text !== 'string') {
      return '';
    }

    return text
      .replace(REGEX_PATTERNS.TEXT_SANITIZE, '')
      .trim()
      .slice(0, maxLength);
  }

  /**
   * Validate port number
   */
  static validatePort(port: string | number): number {
    const portNum = typeof port === 'string' ? parseInt(port, 10) : port;
    
    if (
      isNaN(portNum) || 
      portNum < VALIDATION_LIMITS.PORT.MIN || 
      portNum > VALIDATION_LIMITS.PORT.MAX
    ) {
      throw new Error(
        `Invalid port number (must be ${VALIDATION_LIMITS.PORT.MIN}-${VALIDATION_LIMITS.PORT.MAX})`
      );
    }
    
    return portNum;
  }

  /**
   * Sanitize connection protocol
   */
  static sanitizeProtocol(protocol: string): AllowedProtocol {
    this.validateStringInput(protocol, 'Protocol');

    const sanitized = protocol.toLowerCase().trim();
    
    if (!ALLOWED_PROTOCOLS.includes(sanitized as AllowedProtocol)) {
      throw new Error(`Invalid protocol. Allowed: ${ALLOWED_PROTOCOLS.join(', ')}`);
    }
    
    return sanitized as AllowedProtocol;
  }

  /**
   * Validate string input
   */
  private static validateStringInput(value: string, fieldName: string): void {
    if (!value || typeof value !== 'string') {
      throw new Error(`${fieldName} is required`);
    }
  }
}

// React hook for form sanitization
export const useSanitizedForm = () => {
  const sanitizeFormData = (data: Record<string, unknown>) => {
    const sanitized: Record<string, unknown> = {};
    
    for (const [key, value] of Object.entries(data)) {
      if (typeof value === 'string') {
        try {
          switch (key) {
            case 'ip':
            case 'ipAddress':
              sanitized[key] = InputSanitizer.sanitizeIpAddress(value);
              break;
            case 'name':
            case 'machineName':
            case 'displayName':
              sanitized[key] = InputSanitizer.sanitizeMachineName(value);
              break;
            case 'username':
            case 'login':
              sanitized[key] = InputSanitizer.sanitizeUsername(value);
              break;
            case 'password':
              InputSanitizer.validatePassword(value);
              sanitized[key] = value; // Password is not sanitized, only validated
              break;
            case 'url':
            case 'connectionUrl':
              sanitized[key] = InputSanitizer.sanitizeUrl(value);
              break;
            case 'port':
              sanitized[key] = InputSanitizer.validatePort(value);
              break;
            case 'protocol':
              sanitized[key] = InputSanitizer.sanitizeProtocol(value);
              break;
            default:
              // For other fields, apply basic sanitization
              sanitized[key] = InputSanitizer.sanitizeText(value);
          }
        } catch (error) {
          // Propagate validation errors up
          throw new Error(`${key}: ${error instanceof Error ? error.message : 'Invalid value'}`);
        }
      } else {
        sanitized[key] = value;
      }
    }
    
    return sanitized;
  };
  
  return { sanitizeFormData };
}; 