/**
 * Comprehensive Input Validation Utility
 * Implements whitelist-based validation for all user inputs
 */

const validator = require('validator');

/**
 * Validation Rules Configuration
 */
const VALIDATION_RULES = {
  // User-related
  username: {
    minLength: 3,
    maxLength: 50,
    pattern: /^[a-zA-Z0-9_-]+$/,
    sanitize: true
  },
  email: {
    maxLength: 255,
    sanitize: true
  },
  password: {
    minLength: 8,
    maxLength: 128
  },
  // Content-related
  playlistName: {
    minLength: 1,
    maxLength: 200,
    pattern: /^[a-zA-Z0-9\s\-_.,()!]+$/,
    sanitize: true
  },
  channelName: {
    minLength: 1,
    maxLength: 200,
    sanitize: true
  },
  url: {
    maxLength: 2048,
    protocols: ['http', 'https', 'rtmp', 'rtsp', 'udp', 'rtp']
  },
  // Generic text fields
  description: {
    maxLength: 1000,
    sanitize: true
  },
  // File names
  filename: {
    maxLength: 255,
    pattern: /^[a-zA-Z0-9\s\-_.,()]+$/,
    sanitize: true
  },
  // Settings keys
  settingKey: {
    maxLength: 100,
    pattern: /^[a-zA-Z0-9_.-]+$/
  }
};

/**
 * Sanitize string input to prevent XSS
 */
function sanitizeString(str) {
  if (typeof str !== 'string') return str;
  
  // Remove HTML tags
  str = str.replace(/<[^>]*>/g, '');
  
  // Remove script-related content
  str = str.replace(/javascript:/gi, '');
  str = str.replace(/on\w+\s*=/gi, '');
  
  // Escape special characters
  return validator.escape(str);
}

// Export sanitizeString
module.exports.sanitizeString = sanitizeString;

/**
 * Validate username
 */
function validateUsername(username) {
  const errors = [];
  const rules = VALIDATION_RULES.username;
  
  if (!username || typeof username !== 'string') {
    errors.push('Username is required');
    return { valid: false, errors, sanitized: null };
  }
  
  const trimmed = username.trim();
  
  if (trimmed.length < rules.minLength) {
    errors.push(`Username must be at least ${rules.minLength} characters`);
  }
  
  if (trimmed.length > rules.maxLength) {
    errors.push(`Username must not exceed ${rules.maxLength} characters`);
  }
  
  if (!rules.pattern.test(trimmed)) {
    errors.push('Username can only contain letters, numbers, hyphens, and underscores');
  }
  
  const sanitized = rules.sanitize ? sanitizeString(trimmed) : trimmed;
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized
  };
}

/**
 * Validate email
 */
function validateEmail(email) {
  const errors = [];
  const rules = VALIDATION_RULES.email;
  
  if (!email || typeof email !== 'string') {
    errors.push('Email is required');
    return { valid: false, errors, sanitized: null };
  }
  
  const trimmed = email.trim().toLowerCase();
  
  if (!validator.isEmail(trimmed)) {
    errors.push('Invalid email format');
  }
  
  if (trimmed.length > rules.maxLength) {
    errors.push(`Email must not exceed ${rules.maxLength} characters`);
  }
  
  const sanitized = rules.sanitize ? sanitizeString(trimmed) : trimmed;
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized
  };
}

/**
 * Validate URL
 */
function validateUrl(url, allowLocalhost = false) {
  const errors = [];
  const rules = VALIDATION_RULES.url;
  
  if (!url || typeof url !== 'string') {
    errors.push('URL is required');
    return { valid: false, errors, sanitized: null };
  }
  
  const trimmed = url.trim();
  
  if (trimmed.length > rules.maxLength) {
    errors.push(`URL must not exceed ${rules.maxLength} characters`);
  }
  
  // Check if URL is valid and uses allowed protocols
  const options = {
    protocols: rules.protocols,
    require_protocol: true,
    allow_underscores: true
  };
  
  if (!allowLocalhost) {
    options.disallow_auth = false;
  }
  
  if (!validator.isURL(trimmed, options)) {
    errors.push('Invalid URL format');
  }
  
  // Additional security checks
  if (trimmed.includes('javascript:')) {
    errors.push('URL contains invalid content');
  }
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized: trimmed
  };
}

/**
 * Validate playlist name
 */
function validatePlaylistName(name) {
  const errors = [];
  const rules = VALIDATION_RULES.playlistName;
  
  if (!name || typeof name !== 'string') {
    errors.push('Playlist name is required');
    return { valid: false, errors, sanitized: null };
  }
  
  const trimmed = name.trim();
  
  if (trimmed.length < rules.minLength) {
    errors.push(`Playlist name must be at least ${rules.minLength} character`);
  }
  
  if (trimmed.length > rules.maxLength) {
    errors.push(`Playlist name must not exceed ${rules.maxLength} characters`);
  }
  
  if (!rules.pattern.test(trimmed)) {
    errors.push('Playlist name contains invalid characters');
  }
  
  const sanitized = rules.sanitize ? sanitizeString(trimmed) : trimmed;
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized
  };
}

/**
 * Validate channel name
 */
function validateChannelName(name) {
  const errors = [];
  const rules = VALIDATION_RULES.channelName;
  
  if (!name || typeof name !== 'string') {
    errors.push('Channel name is required');
    return { valid: false, errors, sanitized: null };
  }
  
  const trimmed = name.trim();
  
  if (trimmed.length < rules.minLength) {
    errors.push(`Channel name must be at least ${rules.minLength} character`);
  }
  
  if (trimmed.length > rules.maxLength) {
    errors.push(`Channel name must not exceed ${rules.maxLength} characters`);
  }
  
  const sanitized = rules.sanitize ? sanitizeString(trimmed) : trimmed;
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized
  };
}

/**
 * Validate description/text field
 */
function validateDescription(description) {
  const errors = [];
  const rules = VALIDATION_RULES.description;
  
  if (!description) {
    return { valid: true, errors: [], sanitized: '' };
  }
  
  if (typeof description !== 'string') {
    errors.push('Description must be a string');
    return { valid: false, errors, sanitized: null };
  }
  
  const trimmed = description.trim();
  
  if (trimmed.length > rules.maxLength) {
    errors.push(`Description must not exceed ${rules.maxLength} characters`);
  }
  
  const sanitized = rules.sanitize ? sanitizeString(trimmed) : trimmed;
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized
  };
}

/**
 * Validate filename
 */
function validateFilename(filename) {
  const errors = [];
  const rules = VALIDATION_RULES.filename;
  
  if (!filename || typeof filename !== 'string') {
    errors.push('Filename is required');
    return { valid: false, errors, sanitized: null };
  }
  
  const trimmed = filename.trim();
  
  if (trimmed.length > rules.maxLength) {
    errors.push(`Filename must not exceed ${rules.maxLength} characters`);
  }
  
  if (!rules.pattern.test(trimmed)) {
    errors.push('Filename contains invalid characters');
  }
  
  // Check for path traversal attempts
  if (trimmed.includes('..') || trimmed.includes('/') || trimmed.includes('\\')) {
    errors.push('Filename contains invalid path characters');
  }
  
  const sanitized = rules.sanitize ? sanitizeString(trimmed) : trimmed;
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized
  };
}

/**
 * Validate setting key
 */
function validateSettingKey(key) {
  const errors = [];
  const rules = VALIDATION_RULES.settingKey;
  
  if (!key || typeof key !== 'string') {
    errors.push('Setting key is required');
    return { valid: false, errors, sanitized: null };
  }
  
  const trimmed = key.trim();
  
  if (trimmed.length > rules.maxLength) {
    errors.push(`Setting key must not exceed ${rules.maxLength} characters`);
  }
  
  if (!rules.pattern.test(trimmed)) {
    errors.push('Setting key contains invalid characters');
  }
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized: trimmed
  };
}

/**
 * Validate integer
 */
function validateInteger(value, min = Number.MIN_SAFE_INTEGER, max = Number.MAX_SAFE_INTEGER) {
  const errors = [];
  
  const num = parseInt(value, 10);
  
  if (isNaN(num)) {
    errors.push('Must be a valid integer');
    return { valid: false, errors, sanitized: null };
  }
  
  if (num < min) {
    errors.push(`Must be at least ${min}`);
  }
  
  if (num > max) {
    errors.push(`Must not exceed ${max}`);
  }
  
  return {
    valid: errors.length === 0,
    errors,
    sanitized: num
  };
}

/**
 * Validate boolean
 */
function validateBoolean(value) {
  if (typeof value === 'boolean') {
    return { valid: true, errors: [], sanitized: value };
  }
  
  if (value === 'true' || value === '1' || value === 1) {
    return { valid: true, errors: [], sanitized: true };
  }
  
  if (value === 'false' || value === '0' || value === 0) {
    return { valid: true, errors: [], sanitized: false };
  }
  
  return {
    valid: false,
    errors: ['Must be a valid boolean'],
    sanitized: null
  };
}

/**
 * Validate JSON
 */
function validateJSON(value, maxSize = 10000) {
  const errors = [];
  
  if (typeof value === 'object') {
    const jsonString = JSON.stringify(value);
    if (jsonString.length > maxSize) {
      errors.push(`JSON data exceeds maximum size of ${maxSize} characters`);
    }
    return {
      valid: errors.length === 0,
      errors,
      sanitized: value
    };
  }
  
  if (typeof value !== 'string') {
    errors.push('Must be valid JSON');
    return { valid: false, errors, sanitized: null };
  }
  
  try {
    const parsed = JSON.parse(value);
    if (value.length > maxSize) {
      errors.push(`JSON data exceeds maximum size of ${maxSize} characters`);
    }
    return {
      valid: errors.length === 0,
      errors,
      sanitized: parsed
    };
  } catch (e) {
    errors.push('Invalid JSON format');
    return { valid: false, errors, sanitized: null };
  }
}

/**
 * Sanitize object with multiple fields
 */
function sanitizeObject(obj, schema) {
  const sanitized = {};
  const errors = {};
  let hasErrors = false;
  
  for (const [key, validator] of Object.entries(schema)) {
    const value = obj[key];
    const result = validator(value);
    
    if (!result.valid) {
      errors[key] = result.errors;
      hasErrors = true;
    } else {
      sanitized[key] = result.sanitized;
    }
  }
  
  return {
    valid: !hasErrors,
    errors,
    sanitized
  };
}

module.exports = {
  validateUsername,
  validateEmail,
  validateUrl,
  validatePlaylistName,
  validateChannelName,
  validateDescription,
  validateFilename,
  validateSettingKey,
  validateInteger,
  validateBoolean,
  validateJSON,
  sanitizeString,
  sanitizeObject,
  VALIDATION_RULES
};
