<?php

namespace Application\Service\Extension\Validator;



use Application\Service\Extension\Laminas;
use Application\Service\Extension\Validator\customer\DefaultValidator;
use Application\Service\Extension\Validator\customer\ExistValidator;
use Application\Service\Extension\Validator\customer\FunctionValidator;
use Application\Service\Extension\Validator\customer\NumberValidator;
use Application\Service\Extension\Validator\customer\RequiredValidator;
use Application\Service\Extension\Validator\customer\StringValidator;
use Exception;
use Laminas\Mvc\Exception\InvalidArgumentException;
use Laminas\Validator\AbstractValidator;

/**
 * 验证器实例
 */
class ValidatorApplication
{
    /**
     * 接收的字段及数据
     * @var array
     */
    public array $attributes = [];

    /**
     * 字段转义映射
     * @var array|string[]
     */
    protected array $attributesLabels = [];

    /**
     * @var array
     */
    private array $validateMap = [];

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

    /**
     * @var array
     */
    protected array $aliasesMap = [
        'required' => RequiredValidator::class,
        'string' => StringValidator::class,
        'integer' => [NumberValidator::class, 'integerOnly' => true],
        'exist' => ExistValidator::class,
        'function' => FunctionValidator::class,
        'default' => DefaultValidator::class,
        'form' => FormValidator::class
    ];

    /**
     * @param array $attributes 接收的数据
     */
    public function __construct(array $attributes = [])
    {
        // 默认使用post数据
        if (!$attributes) {
            if (Laminas::$app->getMvcEvent()) {
                $this->setAttributes(Laminas::$app->getMvcEvent()->getRequest()->getPost()->toArray());
            }
        }else{
            $this->setAttributes($attributes);
        }

        $this->attributeLabels = ValidatorEnum::ATTRIBUTE_LABELS;
    }

    /**
     * 设置验证规则
     * @param mixed ...$rules
     * @throws Exception
     */
    public function attach(array ...$rules)
    {
        foreach ($rules as $rule) {
            $class = $this->getRuleClass($rule);

            // formValidator 特殊处理
            if ($class instanceof FormValidator) {
                $rule[0] = array_keys($class->config);
            }

            $this->validateMap[] = [
                'class' => $class,
                'field' => $rule[0]
            ];
        }
    }

    /**
     * @param $rule
     * @return mixed|object
     * @throws Exception
     */
    private function getRuleClass($rule)
    {
        // 使用别名
        if (!isset($rule['class'])) {
            if (!isset($this->aliasesMap[$rule[1]])) {
                throw new Exception("使用了错误的别名{$rule[1]}");
            }
            if (is_array($this->aliasesMap[$rule[1]])) {
                $className = array_shift($this->aliasesMap[$rule[1]]);
                $class = self::createObejct($className, array_merge($this->aliasesMap[$rule[1]], $this->collectRuleParams($rule)));
            }else{
                $class = self::createObejct($this->aliasesMap[$rule[1]], $this->collectRuleParams($rule));
            }

        }else{
            if (is_string($rule['class'])) {
                $class = self::createObejct($rule['class'], $this->collectRuleParams($rule));
            }else{
                $class = $rule['class'];
            }
        }

        return $class;
    }

    private function getValidatorClass(array $rule)
    {
        $class = null;

        // 使用别名
        if (isset($rule['class'])) {
            $class = $rule['class'];
            if (is_string($class)) {
                $class = self::createObejct($class, $this->collectRuleParams($rule));
            }
        }

        return $class;
    }

    /**
     * 收集参数
     * @param $rule
     * @return mixed
     */
    private function collectRuleParams($rule)
    {
        unset($rule['class']);
        unset($rule[0]);
        unset($rule[1]);
        $rule['attributeLabels'] = $this->attributeLabels;
        return $rule;
    }

    /**
     * 设置接收字段数据
     * @param array $attributes
     */
    public function setAttributes(array $attributes)
    {
        foreach ($attributes as $formName => $formValue) {
            if($formName == 'mobile') $this->validatePhoneNumber($formValue);
            $this->attributes[$formName] = $formValue;
        }
    }

    public function validatePhoneNumber($phoneNumber) {
        $pattern = '/^1[0-9]{10}$/'; // 正则表达式模式
        if(!preg_match($pattern, $phoneNumber)){
            throw new InvalidArgumentException("手机号格式有误!", 60011);
        }
    }

    /**
     * 设置接收字段映射
     * @param array $labels
     */
    public function setAttributesLabels(array $labels)
    {
        $this->attributeLabels = array_merge($this->attributeLabels, $labels);
    }

    public static function createObejct(string $class, array $config = [])
    {
        if (!$config) {
            return (new \ReflectionClass($class))->newInstanceWithoutConstructor();
        }

        return (new \ReflectionClass($class))->newInstance($config);
    }

    private function formatMessage($message, $params)
    {
        $placeholders = [];
        foreach ((array) $params as $name => $value) {
            $placeholders['{' . $name . '}'] = $value;
        }

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

    /**
     * 是否验证通过
     * @return bool
     * @throws Exception
     */
    public function isValid(): bool
    {
        foreach ($this->validateMap as $item) {
            /** @var BaseCustomValidator $validator */
            $validator = $item['class'];
            foreach ($item['field'] as $field) {
                if ($result = $validator->isValid($this->getValue($validator, $this->attributes[$field] ?? ''), $field)) {
                    $this->addError($result, $field);
                }
            }
        }

        if ($this->hasError()) {
            return false;
        }

        return true;
    }

    private function getValue($class, $field)
    {
        if ($class instanceof DefaultValidator) {
            return $this;
        }
        return $field;
    }

    public function hasError()
    {
        return $this->errors !== [];
    }

    public function addError($error, $field)
    {
        $this->errors[$field][] = $error;
    }

    /**
     * 构建验证器错误信息
     * @param mixed $errorMessage 规则
     * @param string $key 验证字段名称
     * @param $value
     * @return string
     */
    private function buildErrorMessage($errorMessage, string $key, $value): string
    {
        return $errorMessage instanceof \Closure ?
            $errorMessage($this->attributeLabels[$key] ?? $key, $value) :
            $errorMessage;
    }

    /**
     * 获取错误信息
     * ```
     *      [
     *          '字段名称' => '错误信息'
     *      ]
     * ```
     * @return array
     */
    public function getErrors(): array
    {
        return $this->errors;
    }

    public function getFirstErrorToString()
    {
        return current($this->errors)[0] ?? '';
    }

    /**
     * 适配不同的验证器
     * @param AbstractValidator $validator
     * @param $item
     * @param null $key
     * @return bool|void
     */
    private function getIsValid(AbstractValidator $validator, $item, $key = null)
    {
        if ($validator instanceof CustomValidator) {
            return $validator->isValid($item['value'], $key);
        }else{
            return $validator->isValid($item['value']);
        }
    }
}