<?php

namespace Openguru\OpenCartFramework\Validator;

use Openguru\OpenCartFramework\Validator\ValidationRules\ValidationRuleInterface;

class Validator implements ValidatorInterface
{
    private array $input;
    private array $rules;
    private ErrorBag $errors;
    private array $customMessages;
    private array $fieldNames;
    private array $validationRules;

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

    public function make(array $input, array $rules, array $fieldNames = []): Validator
    {
        $this->input = $input;
        $this->rules = $rules;
        $this->errors = new ErrorBag();
        $this->fieldNames = $fieldNames;

        return $this;
    }

    /**
     * @throws ValidationRuleNotFoundException
     */
    private function validate(): void
    {
        foreach ($this->rules as $field => $rule) {
            $parts = $this->extractParts($rule);

            foreach ($parts as $part) {
                $validationRule = $this->resolveRuleClass($part);

                $validationRule->validate($field, $this->input, function ($message) use ($part, $field) {
                    $this->errors->put($field, $this->formatMessage($message, $part, $field));
                });
            }
        }
    }

    /**
     * @throws ValidationRuleNotFoundException
     */
    public function fails(): bool
    {
        $this->validate();

        return $this->errors->count() > 0;
    }

    private function extractParts($rule): array
    {
        return explode('|', $rule);
    }

    /**
     * @throws ValidationRuleNotFoundException
     */
    private function resolveRuleClass($part): ValidationRuleInterface
    {
        $lazyClass = $this->validationRules[$part] ?? null;

        if ($lazyClass === null) {
            throw new ValidationRuleNotFoundException('Unknown rule "' . $part);
        }

        return $lazyClass();
    }

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

    private function formatMessage(string $message, string $rule, string $field): string
    {
        $message = $this->customMessages[$rule] ?? $message;
        $field = $this->fieldNames[$field] ?? $field;

        return str_replace(':field', $field, $message);
    }
}
