<?php

namespace Application\Validator;

use Application\Common\StatusCode;
use Application\Service\Exceptions\InvalidArgumentException;
use Application\Validator\Config\Config;
use Application\Validator\Config\Map;
use Application\Validator\Config\RegexConfig;
use Laminas\InputFilter\InputFilter;
use Laminas\Validator\Regex;
use Laminas\Validator\StringLength;

class Validator
{
    private InputFilter $inputFilter;
    protected string $formDataName;
    protected array $funParams;
    protected array $validData = [];

    public function __construct(string $formDataName, array $funParams = [])
    {
        $this->formDataName = $formDataName;
        $this->funParams = $funParams;

        $this->inputFilter = new InputFilter();

        $this->validData = $_POST;
        $this->attach();
    }

    public function valid() {

        $this->inputFilter->setData($this->validData);
        if( ! $this->inputFilter->isValid()) {
            throw new InvalidArgumentException(current(current($this->inputFilter->getMessages())), StatusCode::E_FIELD_VALIDATOR['code']);
        }
    }

    private function attach() {
        $formFieldConfig = $this->getFormFieldConfig();

        array_walk($formFieldConfig, function (&$field, $fieldProp) {
            if (!empty($field['rules'] ?? '' )) {
                $this->formatRules($field['rules']);
                $this->inputFilter->add([
                    'name' => $fieldProp,
                    'validators' => array_values($field['rules']),
                ]);
            }

        });
    }

    private function formatRules(&$rules) {
        $formatRules = [];
        foreach($rules as &$rule) {
            list($ruleClass, $ruleName) = $this->getRuleClass($rule);
            if($ruleName == '') continue;

            if($ruleClass === StringLength::class) {
                if(isset($formatRules[$ruleClass])) {
                    $rule['options'] = $formatRules[$ruleClass]['options'];
                }

                $rule['options'][$ruleName] = $rule[$ruleName];
                $rule['options']['messages'][Map::VALIDATOR_MESSAGE_KEY_MAP[$ruleName]] = $rule['message'];
            }else {
                $rule['options']['message'] = $rule['message'];
            }

            if($ruleClass === Regex::class) {
                $rule['options']['pattern'] = RegexConfig::$regex[$ruleName];
            }

            $formatRules[$ruleClass] = [
                'name' => $ruleClass,
                'options' => $rule['options'] ?? [],
            ];

        }

        $rules = $formatRules;
    }

    private function getRuleClass(&$rule): array
    {
        $ruleName = array_intersect_key(Map::VALIDATOR_CLASS_MAP, $rule);
        if(empty($ruleName) && $rule['validator']) {
            if( Map::VALIDATOR_CLASS_MAP[$rule['validator']] ) {
                return [
                    Map::VALIDATOR_CLASS_MAP[$rule['validator']],
                    $rule['validator'],
                ];
            }
        }

        return [
            current($ruleName),
            current(array_flip($ruleName))
        ];
    }

    private function getFormFieldConfig() : array
    {
        $formConfig = include $this->getFormFieldConfigFile();

        if (is_callable($formConfig)) {
            $formConfig = call_user_func($formConfig, $this->funParams);
        }

        return $formConfig;
    }

    private function getFormFieldConfigFile() : string
    {
        $fileName = ucfirst($this->formDataName) . 'Data.php';
        return Config::FORM_FIELD_CONFIG_PATH . $fileName;
    }
}