<?php

namespace Velis\Mvc\Validators\Rules;

use Velis\App;
use Velis\Exception;
use Velis\Filter\Validation;

class Type implements RuleInterface
{
    public const STRING = 'string';
    public const INT = 'int';
    public const NUMERIC = 'numeric';

    private string $type;

    public function __construct($type)
    {
        if (!in_array($type, self::allowedTypes())) {
            throw new Exception("Invalid type {$type}");
        }

        $this->type = $type;
    }

    public function validate(array $data, string $fieldName): bool
    {
        $validation = $this->getValidator($fieldName);

        return !is_array($data[$fieldName]) && $validation->validate($data)->count() === 0;
    }

    public function getErrorMessage($fieldName): string
    {
        return "The {$fieldName} must be of type {$this->type}";
    }

    public static function allowedTypes()
    {
        return [self::STRING, self::INT, self::NUMERIC];
    }

    private function getValidator(string $fieldName): Validation
    {
        /** @var Validation $validation */
        $validation = App::$di->get('validation');

        $validatorOptions = [
            'message' => $this->getErrorMessage($fieldName),
            'allowEmpty' => true
        ];

        switch ($this->type) {
            case self::INT:
                $validation->add($fieldName, Validation::TYPE_DIGIT, $validatorOptions);
                break;
            case self::STRING:
                $validatorOptions['callback'] = fn ($data) => is_string($data[$fieldName]);
                $validation->add($fieldName, Validation::TYPE_CALLBACK, $validatorOptions);
                break;
            case self::NUMERIC:
                $validation->add($fieldName, Validation::TYPE_NUMERICALITY, $validatorOptions);
                break;
        }

        return $validation;
    }
}
