/**
 * Route Validators
 * 
 * This file provides utility functions for validating route configurations.
 * These validators help ensure that route configurations are valid and correctly structured.
 */

import type { IRouteConfig, IRouteMatch, IRouteAction, TPortRange } from '../models/route-types.js';

/**
 * Validates a port range or port number
 * @param port Port number, port range, or port function
 * @returns True if valid, false otherwise
 */
export function isValidPort(port: any): boolean {
  if (typeof port === 'number') {
    return port > 0 && port < 65536; // Valid port range is 1-65535
  } else if (Array.isArray(port)) {
    return port.every(p =>
      (typeof p === 'number' && p > 0 && p < 65536) ||
      (typeof p === 'object' && 'from' in p && 'to' in p &&
       p.from > 0 && p.from < 65536 && p.to > 0 && p.to < 65536)
    );
  } else if (typeof port === 'function') {
    // For function-based ports, we can't validate the result at config time
    // so we just check that it's a function
    return true;
  } else if (typeof port === 'object' && 'from' in port && 'to' in port) {
    return port.from > 0 && port.from < 65536 && port.to > 0 && port.to < 65536;
  }
  return false;
}

/**
 * Validates a domain string
 * @param domain Domain string to validate
 * @returns True if valid, false otherwise
 */
export function isValidDomain(domain: string): boolean {
  // Basic domain validation regex - allows wildcards (*.example.com)
  const domainRegex = /^(\*\.)?([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
  return domainRegex.test(domain);
}

/**
 * Validates a route match configuration
 * @param match Route match configuration to validate
 * @returns { valid: boolean, errors: string[] } Validation result
 */
export function validateRouteMatch(match: IRouteMatch): { valid: boolean; errors: string[] } {
  const errors: string[] = [];

  // Validate ports
  if (match.ports !== undefined) {
    if (!isValidPort(match.ports)) {
      errors.push('Invalid port number or port range in match.ports');
    }
  }

  // Validate domains
  if (match.domains !== undefined) {
    if (typeof match.domains === 'string') {
      if (!isValidDomain(match.domains)) {
        errors.push(`Invalid domain format: ${match.domains}`);
      }
    } else if (Array.isArray(match.domains)) {
      for (const domain of match.domains) {
        if (!isValidDomain(domain)) {
          errors.push(`Invalid domain format: ${domain}`);
        }
      }
    } else {
      errors.push('Domains must be a string or an array of strings');
    }
  }

  // Validate path
  if (match.path !== undefined) {
    if (typeof match.path !== 'string' || !match.path.startsWith('/')) {
      errors.push('Path must be a string starting with /');
    }
  }

  return {
    valid: errors.length === 0,
    errors
  };
}

/**
 * Validates a route action configuration
 * @param action Route action configuration to validate
 * @returns { valid: boolean, errors: string[] } Validation result
 */
export function validateRouteAction(action: IRouteAction): { valid: boolean; errors: string[] } {
  const errors: string[] = [];

  // Validate action type
  if (!action.type) {
    errors.push('Action type is required');
  } else if (!['forward', 'redirect', 'static', 'block'].includes(action.type)) {
    errors.push(`Invalid action type: ${action.type}`);
  }

  // Validate target for 'forward' action
  if (action.type === 'forward') {
    if (!action.target) {
      errors.push('Target is required for forward action');
    } else {
      // Validate target host
      if (!action.target.host) {
        errors.push('Target host is required');
      } else if (typeof action.target.host !== 'string' &&
                !Array.isArray(action.target.host) &&
                typeof action.target.host !== 'function') {
        errors.push('Target host must be a string, array of strings, or function');
      }

      // Validate target port
      if (action.target.port === undefined) {
        errors.push('Target port is required');
      } else if (typeof action.target.port !== 'number' &&
                typeof action.target.port !== 'function') {
        errors.push('Target port must be a number or a function');
      } else if (typeof action.target.port === 'number' && !isValidPort(action.target.port)) {
        errors.push('Target port must be between 1 and 65535');
      }
    }

    // Validate TLS options for forward actions
    if (action.tls) {
      if (!['passthrough', 'terminate', 'terminate-and-reencrypt'].includes(action.tls.mode)) {
        errors.push(`Invalid TLS mode: ${action.tls.mode}`);
      }

      // For termination modes, validate certificate
      if (['terminate', 'terminate-and-reencrypt'].includes(action.tls.mode)) {
        if (action.tls.certificate !== 'auto' && 
            (!action.tls.certificate || !action.tls.certificate.key || !action.tls.certificate.cert)) {
          errors.push('Certificate must be "auto" or an object with key and cert properties');
        }
      }
    }
  }

  // Validate redirect for 'redirect' action
  if (action.type === 'redirect') {
    if (!action.redirect) {
      errors.push('Redirect configuration is required for redirect action');
    } else {
      if (!action.redirect.to) {
        errors.push('Redirect target (to) is required');
      }

      if (action.redirect.status && 
          ![301, 302, 303, 307, 308].includes(action.redirect.status)) {
        errors.push('Invalid redirect status code');
      }
    }
  }

  // Validate static file config for 'static' action
  if (action.type === 'static') {
    if (!action.static) {
      errors.push('Static file configuration is required for static action');
    } else {
      if (!action.static.root) {
        errors.push('Static file root directory is required');
      }
    }
  }

  return {
    valid: errors.length === 0,
    errors
  };
}

/**
 * Validates a complete route configuration
 * @param route Route configuration to validate
 * @returns { valid: boolean, errors: string[] } Validation result
 */
export function validateRouteConfig(route: IRouteConfig): { valid: boolean; errors: string[] } {
  const errors: string[] = [];

  // Check for required properties
  if (!route.match) {
    errors.push('Route match configuration is required');
  }

  if (!route.action) {
    errors.push('Route action configuration is required');
  }

  // Validate match configuration
  if (route.match) {
    const matchValidation = validateRouteMatch(route.match);
    if (!matchValidation.valid) {
      errors.push(...matchValidation.errors.map(err => `Match: ${err}`));
    }
  }

  // Validate action configuration
  if (route.action) {
    const actionValidation = validateRouteAction(route.action);
    if (!actionValidation.valid) {
      errors.push(...actionValidation.errors.map(err => `Action: ${err}`));
    }
  }

  // Ensure the route has a unique identifier
  if (!route.id && !route.name) {
    errors.push('Route should have either an id or a name for identification');
  }

  return {
    valid: errors.length === 0,
    errors
  };
}

/**
 * Validate an array of route configurations
 * @param routes Array of route configurations to validate
 * @returns { valid: boolean, errors: { index: number, errors: string[] }[] } Validation result
 */
export function validateRoutes(routes: IRouteConfig[]): { 
  valid: boolean; 
  errors: { index: number; errors: string[] }[] 
} {
  const results: { index: number; errors: string[] }[] = [];

  routes.forEach((route, index) => {
    const validation = validateRouteConfig(route);
    if (!validation.valid) {
      results.push({
        index,
        errors: validation.errors
      });
    }
  });

  return {
    valid: results.length === 0,
    errors: results
  };
}

/**
 * Check if a route configuration has the required properties for a specific action type
 * @param route Route configuration to check
 * @param actionType Expected action type
 * @returns True if the route has the necessary properties, false otherwise
 */
export function hasRequiredPropertiesForAction(route: IRouteConfig, actionType: string): boolean {
  if (!route.action || route.action.type !== actionType) {
    return false;
  }

  switch (actionType) {
    case 'forward':
      return !!route.action.target && !!route.action.target.host && !!route.action.target.port;
    case 'redirect':
      return !!route.action.redirect && !!route.action.redirect.to;
    case 'static':
      return !!route.action.static && !!route.action.static.root;
    case 'block':
      return true; // Block action doesn't require additional properties
    default:
      return false;
  }
}

/**
 * Throws an error if the route config is invalid, returns the config if valid
 * Useful for immediate validation when creating routes
 * @param route Route configuration to validate
 * @returns The validated route configuration
 * @throws Error if the route configuration is invalid
 */
export function assertValidRoute(route: IRouteConfig): IRouteConfig {
  const validation = validateRouteConfig(route);
  if (!validation.valid) {
    throw new Error(`Invalid route configuration: ${validation.errors.join(', ')}`);
  }
  return route;
}