/**
 * URL Validator Utility
 *
 * Validates URLs to prevent SSRF attacks by ensuring:
 * - Valid URL format
 * - Whitelisted protocols (http/https)
 * - No access to private/internal IP addresses
 */

/**
 * Validation result
 */
export interface UrlValidationResult {
  valid: boolean;
  error?: string;
}

/**
 * Validate a URL for security concerns
 * @param url URL to validate
 * @param allowLocalhost Whether to allow localhost (for development)
 * @returns Validation result
 */
export function validateUrl(url: string, allowLocalhost = false): UrlValidationResult {
  // Check if URL is provided
  if (!url || url.trim() === "") {
    return {
      valid: false,
      error: "Orchestrator URL is required",
    };
  }

  // Parse URL
  let parsedUrl: URL;
  try {
    parsedUrl = new URL(url);
  } catch {
    return {
      valid: false,
      error: "Invalid orchestrator URL format",
    };
  }

  // Validate protocol (only http and https allowed)
  if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
    return {
      valid: false,
      error: "Invalid orchestrator URL protocol (only http and https allowed)",
    };
  }

  // Validate hostname (prevent SSRF to private addresses)
  const hostname = parsedUrl.hostname.toLowerCase();

  // Check for private/internal addresses
  if (isPrivateOrInternalAddress(hostname, allowLocalhost)) {
    return {
      valid: false,
      error: "Orchestrator URL cannot point to private/internal addresses",
    };
  }

  return { valid: true };
}

/**
 * Check if hostname is a private or internal address
 * @param hostname Hostname to check
 * @param allowLocalhost Whether localhost/loopback is allowed
 * @returns True if private/internal
 */
function isPrivateOrInternalAddress(hostname: string, allowLocalhost = false): boolean {
  const isLocalhost =
    hostname === "localhost" ||
    hostname === "0.0.0.0" ||
    hostname.endsWith(".localhost") ||
    hostname.endsWith(".local");

  // Check if it's an IP address
  const ipv4Regex = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
  const ipv4Match = ipv4Regex.exec(hostname);

  if (ipv4Match) {
    const [, octet1, octet2, octet3, octet4] = ipv4Match;
    const o1 = parseInt(octet1 ?? "0", 10);
    const o2 = parseInt(octet2 ?? "0", 10);
    const o3 = parseInt(octet3 ?? "0", 10);
    const o4 = parseInt(octet4 ?? "0", 10);

    // Validate octets are in range
    if (o1 > 255 || o2 > 255 || o3 > 255 || o4 > 255) {
      return true; // Invalid IP, treat as suspicious
    }

    // Check for loopback (127.0.0.0/8) and 0.0.0.0
    const isLoopback = o1 === 127 || (o1 === 0 && o2 === 0 && o3 === 0 && o4 === 0);

    if (isLoopback && allowLocalhost) {
      return false; // Allow loopback in development
    }

    if (isLoopback) {
      return true;
    }

    // Check for private IP ranges (always blocked)
    // 10.0.0.0/8
    if (o1 === 10) {
      return true;
    }

    // 172.16.0.0/12
    if (o1 === 172 && o2 >= 16 && o2 <= 31) {
      return true;
    }

    // 192.168.0.0/16
    if (o1 === 192 && o2 === 168) {
      return true;
    }

    // 169.254.0.0/16 (link-local)
    if (o1 === 169 && o2 === 254) {
      return true;
    }
  }

  // Check for IPv6 localhost (URL class removes brackets)
  const isIpv6Localhost =
    hostname === "::1" || hostname === "::" || hostname === "[::1]" || hostname === "[::]";
  if (isIpv6Localhost && allowLocalhost) {
    return false;
  }

  if (isIpv6Localhost) {
    return true;
  }

  // Check for localhost domain names
  if (isLocalhost && allowLocalhost) {
    return false;
  }

  if (isLocalhost) {
    return true;
  }

  return false;
}
