<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Support\Facades\Log;

class SafeDomain implements Rule
{
    /**
     * List of blacklisted IP ranges in CIDR notation
     */
    protected array $blacklistedIpRanges = [
        // Internal Networks
        '10.0.0.0/8',      // RFC1918 private network
        '172.16.0.0/12',   // RFC1918 private network
        '192.168.0.0/16',  // RFC1918 private network 
        '127.0.0.0/8',     // Localhost
        '169.254.0.0/16',  // Link-local addresses
        'fc00::/7',        // IPv6 unique local addresses
        'fe80::/10',       // IPv6 link-local addresses
        '::1/128',         // IPv6 localhost
    ];

    /**
     * List of blacklisted specific IP addresses
     */
    protected array $blacklistedIps = [
        // Cloud Metadata Services
        '169.254.169.254',  // AWS/GCP/Azure instance metadata
        '169.254.170.2',    // AWS container metadata
        '168.63.129.16',    // Azure platform IP
        // Other sensitive IPs
        '0.0.0.0',
    ];

    /**
     * List of blacklisted domain patterns (regex)
     */
    protected array $blacklistedDomainPatterns = [
        // Localhost domains
        '/^localhost$/i',
        '/.*\.localhost$/i',
        '/.*\.local$/i',
        '/.*\.securelayer/i',
        
        // Internal domains
        '/.*\.internal$/i',
        '/.*\.intranet$/i',
        '/.*\.corp$/i',
        '/.*\.lan$/i',
        
        // Service discovery
        '/.*\.service\.consul$/i',
        '/.*consul\.io$/i',
        '/.*\.svc\.cluster\.local$/i',
                
        // Cloud service internals
        '/metadata\.google\.internal$/i',
        '/metadata\.internal$/i',
        '/.*\.internal\.elasticloadbalancing\.amazonaws\.com$/i',
        
        // AWS Internals
        '/.*\.amazonaws\.com\.internal$/i',
        '/.*\.internal\.amazonaws\.com$/i',
        
        // K8s/Container Orchestration
        '/kubernetes\.default\.svc$/i',
        '/.*\.kube-system\.svc\.cluster\.local$/i',
        '/kube-dns\.kube-system$/i',
        
        // Docker and other container services
        '/docker\.internal$/i',
        '/host\.docker\.internal$/i',
        '/gateway\.docker\.internal$/i',
    ];
    
    /**
     * The validation failure reason.
     */
    protected string $failureReason = '';

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        // If value is not a string or is empty, let other validation rules handle it
        if (!is_string($value) || empty($value)) {
            return false;
        }

        // Parse the URL
        $parsedUrl = parse_url($value);
        
        // If URL can't be parsed or doesn't have a host, fail
        if ($parsedUrl === false || !isset($parsedUrl['host'])) {
            $this->failureReason = 'The :attribute must be a valid URL with a host.';
            return false;
        }

        $host = $parsedUrl['host'];

        // Check IP address
        if (filter_var($host, FILTER_VALIDATE_IP)) {
            // Check specific blacklisted IPs
            if (in_array($host, $this->blacklistedIps)) {
                $this->failureReason = 'The :attribute contains a blacklisted IP address.';
                return false;
            }

            // Check CIDR ranges
            foreach ($this->blacklistedIpRanges as $cidrRange) {
                if ($this->ipInCidrRange($host, $cidrRange)) {
                    $this->failureReason = 'The :attribute contains an IP address in a blacklisted range.';
                    return false;
                }
            }
        } else {
            // Check domain patterns
            foreach ($this->blacklistedDomainPatterns as $pattern) {
                if (preg_match($pattern, $host)) {
                    $this->failureReason = 'The :attribute contains a blacklisted domain.';
                    return false;
                }
            }
        }
        
        return true;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return $this->failureReason;
    }

    /**
     * Check if an IP is within a CIDR range
     */
    protected function ipInCidrRange(string $ip, string $cidr): bool
    {
        // Handle IPv6
        if (strpos($ip, ':') !== false) {
            return $this->ipv6InCidrRange($ip, $cidr);
        }

        // IPv4 implementation
        list($subnet, $bits) = explode('/', $cidr);
        $ip = ip2long($ip);
        $subnet = ip2long($subnet);
        $mask = -1 << (32 - $bits);
        $subnet &= $mask;
        
        return ($ip & $mask) == $subnet;
    }

    /**
     * Check if an IPv6 is within a CIDR range
     * Simple implementation for common cases
     */
    protected function ipv6InCidrRange(string $ip, string $cidr): bool
    {
        // Simple check for ::1
        if ($cidr === '::1/128' && $ip === '::1') {
            return true;
        }
        
        // Simplified check for common cases
        if ($cidr === 'fc00::/7' && (str_starts_with($ip, 'fc') || str_starts_with($ip, 'fd'))) {
            return true;
        }
        
        if ($cidr === 'fe80::/10' && (
            str_starts_with($ip, 'fe8') || str_starts_with($ip, 'fe9') || 
            str_starts_with($ip, 'fea') || str_starts_with($ip, 'feb')
        )) {
            return true;
        }
        
        // For more complex cases, log a warning
        Log::warning("Complex IPv6 CIDR check: $ip against $cidr - consider using a dedicated IP library");
        
        return false;
    }
}