<?php

namespace Velis\Mvc\Validators;

use Velis\Mvc\Validators\Rules\Required;
use Velis\Mvc\Validators\Rules\RuleInterface;

abstract class FormValidator implements ValidatorInterface
{
    private array $errors = [];

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

    public function hasErrors(): bool
    {
        return !empty($this->errors);
    }

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

        foreach ($this->rules() as $fieldName => $rules) {
            // Checks if rules includes Required Rule, if yes, then it will omit the rest of the rules
            $requiredRule = null;
            foreach ($rules as $rule) {
                if ($rule instanceof Required) {
                    $requiredRule = $rule;
                    break;
                }
            }

            if ($requiredRule && $requiredRule->validate($data, $fieldName) !== true) {
                $this->errors[] = $this->getErrorMessage($requiredRule, $fieldName);
                continue;
            }

            foreach ($rules as $rule) {
                if ($rule instanceof Required) {
                    continue;
                }

                if ($rule->validate($data, $fieldName) !== true) {
                    $this->errors[] = $this->getErrorMessage($rule, $fieldName);
                }
            }
        }
    }

    /**
     * Returns map
     * [
     *      'fieldname1' => array of Rules,
     *      'fieldname2' => array of Rules,
     *      ...
     * ]
     */
    abstract protected function rules(): array;

    /**
     * Customize messages (optional)
     *
     * returns map
     * [
     *      'filedname1' => 'Custom message on error',
     *      'filedname2' => 'Custom message on error',
     *      ...
     * ]
     */
    protected function messages(): array
    {
        return [];
    }

    private function getErrorMessage(RuleInterface $rule, string $fieldName)
    {
        $class = explode("\\", get_class($rule));
        $identifier = strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', end($class)));

        $messages = $this->messages();
        $fieldMessage = $messages[$fieldName] ?? null;

        if (is_string($fieldMessage)) {
            return $fieldMessage;
        }

        if (is_array($fieldMessage) && isset($messages[$fieldName][$identifier])) {
            return $messages[$fieldName][$identifier];
        }

        return $rule->getErrorMessage($fieldName);
    }
}
