<?php

namespace Growfund\Validation\Rules;

defined( 'ABSPATH' ) || exit;

use Exception;
use Growfund\Constants\Validation;
use Growfund\Contracts\Rule;

/**
 * Abstract base class for validation rules.
 *
 * @since 1.0.0
 */
abstract class BaseRule implements Rule
{
    /**
     * check for strict data type 
     * 
     * @var bool
     */
    protected $check_strict_data_type = false;


    /**
     * The field key being validated.
     *
     * @var string|null
     */
    protected $key;

    /**
     * The value to validate.
     *
     * @var mixed
     */
    protected $value;

    /**
     * The value/values for specific rule.
     *
     * @var mixed
     */
    protected $rule_value;

    /**
     * All applied rules for validation.
     *
     * @var mixed
     */
    protected $all_applied_rules;

    /**
     * The input data.
     *
     * @var array
     */
    protected $data;

    /**
     * Create a new rule instance.
     *
     * @param string|null $key   The field name.
     * @param mixed       $value The value to validate.
     * @param mixed       $rule_value The value to that can be passed to the rule.
     * @param array       $data  The input data.
     * @param array       $all_applied_rules  All applied rules
     */
    public function __construct($key = null, $value = null, $rule_value = null, $data = [], $all_applied_rules = [])
    {
        $this->key = $key;
        $this->value = $value;
        $this->rule_value = $rule_value;
        $this->data = $data;
        $this->all_applied_rules = $all_applied_rules;
    }

    /**
     * Set the field key.
     *
     * @param string $key
     * @return void
     */
    public function set_key(string $key)
    {
        $this->key = $key;
    }

    /**
     * Set the value to validate.
     *
     * @param mixed $value
     * @return void
     */
    public function set_value($value)
    {
        $this->value = $value;
    }

    /**
     * Get the value.
     *
     * @return string
     */
    public function get_value()
    {
        return $this->value;
    }

    /**
     * Check if the rule is for a specific data type.
     * 
     * @return bool
     */
    public function is_check_strict_data_type()
    {
        return $this->check_strict_data_type;
    }

    /**
     * Determine if the value is valid.
     *
     * @return bool
     */
    public function is_valid()
    {
        if ($this->ignore_rule_check()) {
            return true;
        }

        return $this->validate_rule();
    }

    /**
     * ignore rule check if value is empty
     * 
     * @return bool
     */
    protected function ignore_rule_check()
    {
        return $this->value === null;
    }

    /**
     * Validate the rule with desired value.
     * @return bool
     */
    abstract public function validate_rule();

    /**
     * Get the error message for this rule.
     *
     * @return string
     */
    public function get_error_message()
    {
        /* translators: %s: field name */
        return sprintf(__('The value provided for %s is invalid.', 'growfund'), str_replace(['_', '.'], ' ', $this->key));
    }

    /**
     * Check if the rule is applied to the value.
     * 
     * @param string $rule Rule name 
     * @return bool
     */
    protected function is_rule_applied(string $rule)
    {
        if (!array_key_exists($rule, Validation::RULE_MAP)) {
            throw new Exception(esc_html__('Rule not found', 'growfund'));
        }

        $rules = array_map(function ($rule) {
            if (is_string($rule)) {
                $rule_name_value_array = explode(':', $rule, 2);
                $rule_name = $rule_name_value_array[0];
                return $rule_name;
            }

            return $rule;
        }, $this->all_applied_rules);

        return in_array($rule, $rules, true) 
            || in_array(Validation::RULE_MAP[$rule], $rules, true);
    }
}
