<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class PartialAddress implements Rule
{
    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        // Convert to string to handle edge cases
        $value = (string) $value;

        // Check if it could be an IPv4 partial address
        if ((new Cidr)->passes($attribute, $value) || $this->isValidPartialIPv4($value)) {
            return true;
        }
        
        // Check if it could be an IPv6 partial address
        if ($this->isValidPartialIPv6($value)) {
            return true;
        }
        
        return false;
    }
    
    /**
     * Validate partial IPv4 addresses
     *
     * @param string $value
     * @return bool
     */
    private function isValidPartialIPv4($value)
    {
        // Handle empty string or just a dot
        if ($value === '' || $value === '.') {
            return true;
        }
        
        // Check for consecutive dots
        if (strpos($value, '..') !== false) {
            return false;
        }
        
        // Remove leading dot if present
        $cleanValue = ltrim($value, '.');
        
        // If it was only a dot, we already handled it
        if ($cleanValue === '') {
            return true;
        }
        
        // Remove trailing dot if present
        $cleanValue = rtrim($cleanValue, '.');
        
        // Split by dots
        $parts = explode('.', $cleanValue);
        
        // Can have 1-4 parts
        if (count($parts) > 4) {
            return false;
        }
        
        // Validate each part
        foreach ($parts as $part) {
            // Empty parts are not allowed (except for leading/trailing dots which we already handled)
            if ($part === '') {
                return false;
            }
            
            // Each part must be numeric
            if (!ctype_digit($part)) {
                return false;
            }
            
            // Check if the numeric value is valid for an IP octet (0-255)
            $numValue = (int)$part;
            if ($numValue > 255) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Validate partial IPv6 addresses
     *
     * @param string $value
     * @return bool
     */
    private function isValidPartialIPv6($value)
    {
        // Must contain at least one colon to be IPv6
        if (strpos($value, ':') === false) {
            return false;
        }
        
        // Handle special cases
        if ($value === ':' || $value === '::') {
            return true;
        }
        
        // IPv6 can only contain hex digits, colons, and dots (for IPv4 mapped addresses)
        // For partial addresses, we're more lenient
        if (!preg_match('/^[0-9a-fA-F:]*$/', $value)) {
            return false;
        }
        
        // Check for more than 2 consecutive colons
        if (strpos($value, ':::') !== false) {
            return false;
        }
        
        // Split by :: to handle compressed notation
        $parts = explode('::', $value);
        if (count($parts) > 2) {
            return false; // Can only have one :: in an address
        }
        
        // Validate each segment
        foreach ($parts as $part) {
            if ($part === '') {
                continue; // Empty is ok for :: notation
            }
            
            $segments = explode(':', $part);
            foreach ($segments as $segment) {
                if ($segment === '') {
                    continue; // Empty segments are ok in partial addresses
                }
                
                // Each segment should be 1-4 hex digits
                if (!preg_match('/^[0-9a-fA-F]{1,4}$/', $segment)) {
                    return false;
                }
            }
        }
        
        return true;
    }
    
    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The :attribute must be a valid partial IP address.';
    }
}