<?php

namespace WPUltimateSecurity\UltimateSecurity\Support\Validation\Rules;

/**
 * IP Address Rule
 *
 * Validates that a value is a valid IP address.
 *
 * @package WPUltimateSecurity\UltimateSecurity\Support\Validation\Rules
 * @since 2.0.0
 */
class IpAddressRule extends AbstractRule
{
    /**
     * IP version to validate (4, 6, or null for both).
     *
     * @var int|null
     */
    private ?int $version = null;

    /**
     * Whether to allow CIDR notation.
     *
     * @var bool
     */
    private bool $allowCidr = false;

    /**
     * Only allow IPv4 addresses.
     *
     * @return static
     */
    public function ipv4(): static
    {
        $clone = clone $this;
        $clone->version = 4;

        return $clone;
    }

    /**
     * Only allow IPv6 addresses.
     *
     * @return static
     */
    public function ipv6(): static
    {
        $clone = clone $this;
        $clone->version = 6;

        return $clone;
    }

    /**
     * Allow CIDR notation (e.g., 192.168.1.0/24).
     *
     * @return static
     */
    public function allowCidr(): static
    {
        $clone = clone $this;
        $clone->allowCidr = true;

        return $clone;
    }

    /**
     * {@inheritDoc}
     */
    public function validate(mixed $value, string $field, array $data = []): bool
    {
        if ($value === null || $value === '') {
            return true;
        }

        if (!is_string($value)) {
            return false;
        }

        // Handle CIDR notation
        if ($this->allowCidr && str_contains($value, '/')) {
            [$ip, $cidr] = explode('/', $value, 2);

            // Validate CIDR part
            if (!is_numeric($cidr)) {
                return false;
            }

            $cidrNum = (int) $cidr;

            // Validate based on IP version
            if ($this->version === 4) {
                if ($cidrNum < 0 || $cidrNum > 32) {
                    return false;
                }
            } elseif ($this->version === 6) {
                if ($cidrNum < 0 || $cidrNum > 128) {
                    return false;
                }
            } else {
                // Auto-detect: if it looks like IPv6, use 128 max
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
                    if ($cidrNum < 0 || $cidrNum > 128) {
                        return false;
                    }
                } elseif ($cidrNum < 0 || $cidrNum > 32) {
                    return false;
                }
            }

            $value = $ip;
        }

        // Determine filter flags
        $flags = 0;

        if ($this->version === 4) {
            $flags = FILTER_FLAG_IPV4;
        } elseif ($this->version === 6) {
            $flags = FILTER_FLAG_IPV6;
        }

        return filter_var($value, FILTER_VALIDATE_IP, $flags) !== false;
    }

    /**
     * {@inheritDoc}
     */
    public function getName(): string
    {
        return 'ip_address';
    }

    /**
     * {@inheritDoc}
     */
    protected function getDefaultMessage(string $field): string
    {
        $type = match ($this->version) {
            4 => 'IPv4',
            6 => 'IPv6',
            default => 'IP',
        };

        return sprintf(
            /* translators: 1: field name, 2: IP type */
            __('The %1$s must be a valid %2$s address.', 'ultimate-security'),
            $field,
            $type
        );
    }
}
