<?php

declare(strict_types=1);

namespace ServerManager\Core;

class Validator
{
    private array $errors = [];
    private array $data;
    private array $rules = [];

    public function __construct(array $data = [])
    {
        $this->data = $data;
    }

    public function validate(array $data, array $rules): bool
    {
        $this->data = $data;
        $this->errors = [];
        $this->rules = $rules;

        foreach ($rules as $field => $fieldRules) {
            $fieldRules = is_string($fieldRules) ? explode('|', $fieldRules) : $fieldRules;
            $value = $data[$field] ?? null;

            foreach ($fieldRules as $rule) {
                $params = [];
                if (str_contains($rule, ':')) {
                    [$rule, $paramStr] = explode(':', $rule, 2);
                    $params = explode(',', $paramStr);
                }

                $method = 'validate' . ucfirst($rule);
                if (method_exists($this, $method)) {
                    $this->{$method}($field, $value, $params);
                }
            }
        }

        return empty($this->errors);
    }

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

    public function getFirstError(): ?string
    {
        if (empty($this->errors)) {
            return null;
        }
        $first = reset($this->errors);
        return is_array($first) ? reset($first) : $first;
    }

    public function addError(string $field, string $message): void
    {
        $this->errors[$field][] = $message;
    }

    private function validateRequired(string $field, mixed $value, array $params): void
    {
        if ($value === null || $value === '' || (is_array($value) && empty($value))) {
            $this->errors[$field][] = "The {$field} field is required.";
        }
    }

    private function validateEmail(string $field, mixed $value, array $params): void
    {
        if ($value !== null && $value !== '' && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
            $this->errors[$field][] = "The {$field} field must be a valid email address.";
        }
    }

    private function validateMin(string $field, mixed $value, array $params): void
    {
        $min = (int) ($params[0] ?? 0);
        if (is_string($value) && mb_strlen($value) < $min) {
            $this->errors[$field][] = "The {$field} field must be at least {$min} characters.";
        } elseif (is_numeric($value) && $value < $min) {
            $this->errors[$field][] = "The {$field} field must be at least {$min}.";
        }
    }

    private function validateMax(string $field, mixed $value, array $params): void
    {
        $max = (int) ($params[0] ?? 0);
        if (is_string($value) && mb_strlen($value) > $max) {
            $this->errors[$field][] = "The {$field} field must not exceed {$max} characters.";
        } elseif (is_numeric($value) && $value > $max) {
            $this->errors[$field][] = "The {$field} field must not exceed {$max}.";
        }
    }

    private function validatesNumeric(string $field, mixed $value, array $params): void
    {
        if ($value !== null && $value !== '' && !is_numeric($value)) {
            $this->errors[$field][] = "The {$field} field must be a number.";
        }
    }

    private function validatesInteger(string $field, mixed $value, array $params): void
    {
        if ($value !== null && $value !== '' && filter_var($value, FILTER_VALIDATE_INT) === false) {
            $this->errors[$field][] = "The {$field} field must be an integer.";
        }
    }

    private function validatesIp(string $field, mixed $value, array $params): void
    {
        if ($value !== null && $value !== '' && !filter_var($value, FILTER_VALIDATE_IP)) {
            $this->errors[$field][] = "The {$field} field must be a valid IP address or hostname.";
        }
    }

    private function validatesPort(string $field, mixed $value, array $params): void
    {
        if ($value !== null && $value !== '') {
            $port = (int) $value;
            if ($port < 1 || $port > 65535) {
                $this->errors[$field][] = "The {$field} field must be a valid port (1-65535).";
            }
        }
    }

    private function validatesAlphaNum(string $field, mixed $value, array $params): void
    {
        if ($value !== null && $value !== '' && !preg_match('/^[a-zA-Z0-9_\-]+$/', $value)) {
            $this->errors[$field][] = "The {$field} field may only contain letters, numbers, dashes and underscores.";
        }
    }

    private function validatesMatch(string $field, mixed $value, array $params): void
    {
        $otherField = $params[0] ?? null;
        if ($otherField && ($this->data[$otherField] ?? null) !== $value) {
            $this->errors[$field][] = "The {$field} field does not match {$otherField}.";
        }
    }

    private function validatesIn(string $field, mixed $value, array $params): void
    {
        if ($value !== null && $value !== '' && !in_array($value, $params, true)) {
            $this->errors[$field][] = "The selected {$field} is invalid.";
        }
    }

    private function validatesJson(string $field, mixed $value, array $params): void
    {
        if ($value !== null && $value !== '' && is_string($value)) {
            json_decode($value);
            if (json_last_error() !== JSON_ERROR_NONE) {
                $this->errors[$field][] = "The {$field} field must be valid JSON.";
            }
        }
    }
}
