<?php

namespace Core;

class Validator
{
    /**
     * @var array The data to be validated.
     */
    protected array $data = [];

    /**
     * @var array The validation errors.
     */
    protected array $errors = [];

    /**
     * Static method to create a new Validator instance with data.
     * Use this when you have an array of data ready.
     * @param array $data
     * @return static
     */
    public static function validate(array $data): static
    {
        $instance = new static();
        $instance->data = $data;
        return $instance;
    }

    /**
     * Static method to create a new empty Validator instance.
     * Use this when you want to add fields one by one.
     * @return static
     */
    public static function create(): static
    {
        return new static();
    }

    /**
     * Adds a single variable to the validator instance.
     * @param string $key
     * @param mixed $value
     * @return $this
     */
    public function add(string $key, $value): self
    {
        $this->data[$key] = $value;
        return $this;
    }

    /**
     * Validates that the specified fields are not empty.
     * @param array $fields
     * @param string|null $message
     * @return $this
     */
    public function empty(array $fields, ?string $message = null): self
    {
        foreach ($fields as $field) {
            if (!isset($this->data[$field]) || trim((string) $this->data[$field]) === '') {
                $msg = $message ?? "$field should not be empty.";
                $this->addError($field, $msg);
            }
        }
        return $this;
    }
    
    /**
     * Validates that the specified fields are valid emails.
     * @param array $fields
     * @param string|null $message
     * @return $this
     */
    public function email(array $fields, ?string $message = null): self
    {
        foreach ($fields as $field) {
            if (isset($this->data[$field]) && !filter_var($this->data[$field], FILTER_VALIDATE_EMAIL)) {
                $msg = $message ?? "$field is not a valid email.";
                $this->addError($field, $msg);
            }
        }
        return $this;
    }
    
    /**
     * Validates the length of specified fields.
     * @param array $rules An associative array of fields and their length rules.
     * @return $this
     */
    public function length(array $rules): self
    {
        foreach ($rules as $field => $range) {
            $value = isset($this->data[$field]) ? trim((string) $this->data[$field]) : '';
            $min = $range['min'] ?? 0;
            $max = $range['max'] ?? PHP_INT_MAX;
            $length = strlen($value);

            if ($length < $min || $length > $max) {
                $msg = $range['message'] ?? "$field must be between $min and $max characters long.";
                $this->addError($field, $msg);
            }
        }
        return $this;
    }
    

    /**
     * Validates that a field meets a minimum length.
     *
     * @param string $field
     * @param int $min
     * @param string|null $message
     * @return $this
     */
    public function min(string $field, int $min, ?string $message = null): self
    {
        $value = isset($this->data[$field]) ? trim((string) $this->data[$field]) : '';
        $length = strlen($value);

        if ($length < $min) {
            $msg = $message ?? "$field must be at least $min characters.";
            $this->addError($field, $msg);
        }

        return $this;
    }



    /**
     * Validates that the specified fields are numeric.
     * @param array $fields
     * @param string|null $message
     * @return $this
     */
    public function numeric(array $fields, ?string $message = null): self
    {
        foreach ($fields as $field) {
            if (isset($this->data[$field]) && !is_numeric($this->data[$field])) {
                $msg = $message ?? "$field must be a number.";
                $this->addError($field, $msg);
            }
        }
        return $this;
    }

    public function alpha(array $fields, ?string $message = null): self
    {
        foreach ($fields as $field) {
            if (isset($this->data[$field]) && !ctype_alpha($this->data[$field])) {
                $msg = $message ?? "$field must contain only alphabetic characters.";
                $this->addError($field, $msg);
            }
        }
        return $this;
    }

    public function alphanumeric(array $fields, ?string $message = null): self
    {
        foreach ($fields as $field) {
            if (isset($this->data[$field]) && !ctype_alnum($this->data[$field])) {
                $msg = $message ?? "$field must contain only letters and numbers.";
                $this->addError($field, $msg);
            }
        }
        return $this;
    }
    
    public function matches(string $field, string $matchField, ?string $message = null): self
    {
        if (isset($this->data[$field]) && isset($this->data[$matchField]) && $this->data[$field] !== $this->data[$matchField]) {
            $msg = $message ?? "$field must match $matchField.";
            $this->addError($field, $msg);
        }
        return $this;
    }

    public function url(array $fields, ?string $message = null): self
    {
        foreach ($fields as $field) {
            if (isset($this->data[$field]) && !filter_var($this->data[$field], FILTER_VALIDATE_URL)) {
                $msg = $message ?? "$field is not a valid URL.";
                $this->addError($field, $msg);
            }
        }
        return $this;
    }


    /**
     * Checks if all validation rules passed.
     * @return bool
     */
    public function passes(): bool
    {
        return empty($this->errors);
    }

    /**
     * Gets the validation errors.
     * @return array
     */
    public function errors(): array
    {
        return $this->errors;
    }

    /**
     * Helper method to add an error to the errors array.
     * @param string $field
     * @param string $message
     * @return void
     */
    protected function addError(string $field, string $message): void
    {
        $this->errors[$field][] = $message;
    }
}