<?php

namespace Application\Service\Extension\ValidatorV2;
use Application\Service\Extension\Helper\ArrayHelper;
use Application\Service\Extension\ValidatorV2\exceptions\InvalidValidatorArgumentException;
use Application\Service\Extension\ValidatorV2\rules\Required;
use Application\Service\Extension\ValidatorV2\validators\BaseValidator;
use Application\Service\Extension\ValidatorV2\validators\DefaultValidator;
use Application\Service\Extension\ValidatorV2\validators\FunctionValidator;
use Application\Service\Extension\ValidatorV2\validators\MatchValidator;
use Application\Service\Extension\ValidatorV2\validators\NumberValidator;
use Application\Service\Extension\ValidatorV2\validators\RegexValidator;
use Application\Service\Extension\ValidatorV2\validators\RequiredValidator;
use Application\Service\Extension\ValidatorV2\validators\StringValidator;
use Application\Service\Extension\ValidatorV2\validators\UppercaseValidator;

/**
 * @method RequiredValidator required($field)
 * @method StringValidator string($field)
 * @method NumberValidator number($field)
 * @method DefaultValidator default($field)
 * @method FunctionValidator function($field)
 * @method MatchValidator match($field)
 */
class Validator
{
    /**
     * Container.
     * @var array
     */
    public array $container = [];

    private string $errorString = '';

    public array $attributes = [];

    protected array $attributeLabels = [];

    /**
     * 验证器列表
     * @var array
     */
    public array $validatorProviders = [
        'required' => RequiredValidator::class,
        'string' => StringValidator::class,
        'number' => NumberValidator::class,
        'default' => DefaultValidator::class,
        'function' => FunctionValidator::class,
        'match' => MatchValidator::class,
        'uppercase' => UppercaseValidator::class,
    ];

    /**
     * @param $name
     * @param $arguments
     * @return BaseValidator|mixed
     */
    public function __call($name, $arguments)
    {
        if (!isset($this->container[$name])) {
            if (!isset($this->validatorProviders[$name])) {
                throw new InvalidValidatorArgumentException("不支持的验证规则 `$name`, 你配置了吗?");
            }

            /** @var BaseValidator $validation */
            $validation = new $this->validatorProviders[$name]();

            // 设置验证属性.
            $validation->setValidationAttributes($arguments[0]);

            $name = spl_object_hash($validation);

            $this->container[$name] = $validation;
        }
        return $this->container[$name];
    }

    /**
     * 设置验证属性
     * @param array $values
     */
    public function setAttributes(array $values)
    {
        $this->attributes = $values;
    }

    /**
     * 设置属性标签
     * @param array $labels
     */
    public function setAttributeLabels(array $labels = [])
    {
        $this->attributeLabels = ArrayHelper::merge($this->attributeLabels, $labels);
    }

    /**
     * @return bool
     */
    public function isValid(): bool
    {
        foreach ($this->container as $item) {
            /** @var BaseValidator $item */
            if (($error = $item->isValid($this->attributes)) !== true) {
                $this->errorString = $this->formatErrorMessage(...$error);
                return false;
            }
        }

        return true;
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError(): string
    {
        return $this->errorString;
    }

    /**
     * 格式化错误
     * @param $message
     * @param array $params
     * @return mixed|string
     */
    protected function formatErrorMessage($message, array $params = [])
    {
        $placeholders = [];
        foreach ($params as $name => $value) {
            // 字段名称转译
            if ($name === 'attribute') {
                $value = $this->attributeLabels[$value];
            }
            $placeholders['{' . $name . '}'] = $value;
        }

        return ($placeholders === []) ? $message : strtr($message, $placeholders);
    }

    public function message(string $message)
    {
        var_dump($message);die;
    }
}