<?php
/**
 * Email Address Validator
 *
 * Validates email addresses with configurable options.
 * Implements ValidatorInterface for consistent validation API.
 *
 * @package     WPUltimateSecurity\UltimateSecurity\Support\Validation
 * @since       2.0.0
 * @author      Ultimate Security Team
 * @license     GPL-2.0-or-later
 */

declare(strict_types=1);

namespace WPUltimateSecurity\UltimateSecurity\Support\Validation;

use WPUltimateSecurity\UltimateSecurity\Contracts\ValidatorInterface;

/**
 * Class EmailValidator
 *
 * Validates email addresses with options for domain checking,
 * disposable email detection, and more.
 *
 * @since 2.0.0
 */
class EmailValidator implements ValidatorInterface
{
    /**
     * Validation errors from the last validation
     *
     * @var array<int, string>
     */
    private array $errors = [];

    /**
     * Validation rules and their descriptions
     *
     * @var array<string, string>
     */
    private array $rules = [
        'format' => 'Must be a valid email address format',
        'domain' => 'Domain must have valid MX records',
        'not_disposable' => 'Disposable email addresses are not allowed',
    ];

    /**
     * Whether to check DNS/MX records
     *
     * @var bool
     */
    private bool $checkDns = false;

    /**
     * Whether to reject disposable email addresses
     *
     * @var bool
     */
    private bool $rejectDisposable = false;

    /**
     * List of disposable email domains
     *
     * @var array<string>
     */
    private array $disposableDomains = [
        'tempmail.com',
        'throwaway.email',
        'mailinator.com',
        'guerrillamail.com',
        '10minutemail.com',
        'temp-mail.org',
        'fakeinbox.com',
        'trashmail.com',
        'sharklasers.com',
        'getnada.com',
        'dispostable.com',
        'maildrop.cc',
        'yopmail.com',
    ];

    /**
     * Configure validator to check DNS records
     *
     * @return self
     */
    public function withDnsCheck(): self
    {
        $this->checkDns = true;
        return $this;
    }

    /**
     * Configure validator to reject disposable emails
     *
     * @return self
     */
    public function rejectDisposable(): self
    {
        $this->rejectDisposable = true;
        return $this;
    }

    /**
     * Add custom disposable domains
     *
     * @param array<string> $domains Domains to add
     * @return self
     */
    public function addDisposableDomains(array $domains): self
    {
        $this->disposableDomains = array_merge($this->disposableDomains, $domains);
        return $this;
    }

    /**
     * {@inheritdoc}
     */
    public function validate(mixed $value): bool
    {
        $this->errors = [];

        // Handle null or empty values
        if ($value === null || $value === '') {
            $this->errors[] = __('Email address is required', 'ultimate-security');
            return false;
        }

        // Ensure value is a string
        if (!is_string($value)) {
            $this->errors[] = __('Email address must be a string', 'ultimate-security');
            return false;
        }

        $value = trim($value);

        // Basic format validation
        if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
            $this->errors[] = __('Invalid email address format', 'ultimate-security');
            return false;
        }

        // Extract domain
        $domain = substr(strrchr($value, '@'), 1);

        // Check for disposable emails
        if ($this->rejectDisposable && $this->isDisposable($domain)) {
            $this->errors[] = __('Disposable email addresses are not allowed', 'ultimate-security');
            return false;
        }

        // Check DNS/MX records
        if ($this->checkDns && !$this->hasMxRecord($domain)) {
            $this->errors[] = __('Email domain does not have valid mail records', 'ultimate-security');
            return false;
        }

        return true;
    }

    /**
     * {@inheritdoc}
     */
    public function getErrors(): array
    {
        return $this->errors;
    }

    /**
     * {@inheritdoc}
     */
    public function getRules(): array
    {
        $activeRules = ['format' => $this->rules['format']];

        if ($this->checkDns) {
            $activeRules['domain'] = $this->rules['domain'];
        }

        if ($this->rejectDisposable) {
            $activeRules['not_disposable'] = $this->rules['not_disposable'];
        }

        return $activeRules;
    }

    /**
     * Check if domain is a disposable email provider
     *
     * @param string $domain Domain to check
     * @return bool
     */
    private function isDisposable(string $domain): bool
    {
        return in_array(strtolower($domain), $this->disposableDomains, true);
    }

    /**
     * Check if domain has MX records
     *
     * @param string $domain Domain to check
     * @return bool
     */
    private function hasMxRecord(string $domain): bool
    {
        // Use WordPress-safe DNS lookup
        if (!function_exists('checkdnsrr')) {
            return true; // Can't verify, assume valid
        }

        return checkdnsrr($domain, 'MX') || checkdnsrr($domain, 'A');
    }

    /**
     * Static factory for fluent interface
     *
     * @return self
     */
    public static function create(): self
    {
        return new self();
    }

    /**
     * Quick validation helper
     *
     * @param string $email Email to validate
     * @return bool
     */
    public static function isValid(string $email): bool
    {
        return (new self())->validate($email);
    }
}
