<?php namespace App\Http\Requests;

// 表单验证

use App\Exceptions\Api\ValidException;
use Illuminate\Foundation\Http\FormRequest;
// 验证器
use Illuminate\Contracts\Validation\Validator;

/**
 * 基础验证器
 * 
 * 此验证器增加了验证场景的移除与添加等功能
 * 
 */
class BaseRequest extends FormRequest
{
    // 场景列表
    protected $scenes = [];
    // 当前场景
    protected $currentScene;
    // 是否注入之后自动验证
    protected $autoValidate = false;
    // 场景需要移除的验证规则
    protected $remove = [];
    // 场景需要追加的验证规则
    protected $append = [];

    /**
     * 自动认证
     */
    public function authorize()
    {
        return true;
    }

    /**
     * 设置场景
     * @param   string    $scene
     * @return  $this
     */
    public function scene($scene)
    {
        $this->currentScene = $scene;
        return $this;
    }

    /**
     * 覆盖自动验证方法
     */
    public function validateResolved()
    {
        // 注入之后自动验证开启则执行最终验证方法
        if ($this->autoValidate) $this->handleValidate();
    }

    /**
     * 验证方法
     * @param string $scene
     */
    public function validate($scene = '')
    {
        // 有场景参数。设置场景参数
        if ($scene) $this->currentScene = $scene;
        // 调用最终验证方法
        $this->handleValidate();
    }

    /**
     * 根据场景获取规则
     * @return array|mixed
     */
    public function getRules()
    {
        // 获取验证规则
        $rules      = $this->container->call([$this, 'rules']);
        // 创建新规则
        $newRules   = [];
        // 如果当前有场景并且存在场景字段，使用场景规则
        if ( $this->currentScene && isset($this->scenes[$this->currentScene]) ) {
            // 如果需要验证的场景字段是数组格式直接使用，否则做字符串切割成数组
            $sceneFields = is_array($this->scenes[$this->currentScene]) ? $this->scenes[$this->currentScene] : explode(',', $this->scenes[$this->currentScene]);
            // 循环场景字段
            foreach ($sceneFields as $field) {
                // 如果场景字段存在于验证规则中，
                if (array_key_exists($field, $rules)) $newRules[$field] = $rules[$field];
            }
            // 返回新规则
            return $this->handleRules($newRules);
        }
        // 没有场景定义数据，使用原规则
        return $this->handleRules($rules);
    }

    /**
     * 处理规则数据
     * 
     * @return array|mixed
     */
    protected function handleRules($rules){
        // 循环需要增加的验证字段
        foreach ($this->append as $key => $rule) {
            // 规则中不存在需要增加的验证字段
            if ( !isset($rules[$key]) ) {
                // 添加到验证规则中
                $rules[$key] = $rule;
            }else{
                // 循环要添加规则数据
                foreach ( $rule as $string ) {
                    // 要添加的数据
                    $append     = explode(':',$string);
                    // 临时中转已存在的规则
                    $tempRules  = [];
                    // 循环规则中的数据
                    foreach (explode('|',$rules[$key]) as $tkey => $string) {
                        // 切割规则与参数
                        $temp = explode(':',$string);
                        $tempRules[$temp['0']] = isset($temp['1']) ? $temp['1'] : null;
                    }
                    // 不存在的话，增加参数
                    $tempRules[$append[0]] = isset($append['1']) ? $append['1'] : null;
                    // 重新写入
                    $r   = [];
                    // 循环规则中的数据
                    foreach ( $tempRules as $tkey => $string) {
                        $r[] = $tkey.':'.$string;
                    }
                    // 数据组合
                    $rules[$key] = implode('|',$r);
                }
            }
            // 添加后删除
            unset($this->append[$key]);
        }
        // 循环删除的字段
        foreach ($this->remove as $key => $rule) {
            // 规则中存在字段才需要删除
            if ( !isset($rules[$key]) )  continue;
            // 如果字段为空，表示删除所有的验证规则
            if( !$rule ) {
                // 删除掉当前的字段
                unset($rules[$key]);
                // 跳出本次循环
                continue;
            }
            // 当前规则
            $newRule       = explode('|',$rules[$key]);
            // 循环要删除规则数据
            foreach ( $rule as $remove ) {
                // 要移除的规则
                $remove     = explode(':',$remove);
                // 循环当前规则
                foreach ( $newRule as $tkey => $string) {
                    // 切割成数组
                    $string = explode(':',$string);
                    // 如果是要删除的规则
                    if( $string[0]  == $remove[0]) unset($newRule[$tkey]);
                }
            }
            // 重新赋值到规则
            $rules[$key]  = implode('|',$newRule);
            // 添加后删除
            unset($this->remove[$key]);
        }
        // 返回最终规则
        return $rules;
    }

    /**
     * 移除某个字段的验证规则
     * @access public
     * @param string|array $field 字段名
     * @param mixed        $rule  验证规则 null 移除所有规则
     * @return $this
     */
    public function remove($field, $rule = null)
    {
        if (is_array($field)) {
            foreach ($field as $key => $rule) {
                if (is_int($key)) {
                    $this->remove($rule);
                } else {
                    $this->remove($key, $rule);
                }
            }
        } else {
            if (is_string($rule)) {
                $rule = explode('|', $rule);
            }

            $this->remove[$field] = $rule;
        }
        return $this;
    }

    /**
     * 追加某个字段的验证规则
     * @access public
     * @param string|array $field 字段名
     * @param mixed        $rule  验证规则
     * @return $this
     */
    public function append($field, $rule = null)
    {
        if (is_array($field)) {
            foreach ($field as $key => $rule) {
                $this->append($key, $rule);
            }
        } else {
            if (is_string($rule)) {
                $rule = explode('|', $rule);
            }

            $this->append[$field] = $rule;
        }

        return $this;
    }

    /**
     * 覆盖设置 自定义验证器
     * @param $factory
     * @return mixed
     */
    public function validator($factory)
    {
        return $factory->make(
            $this->validationData(),
            $this->getRules(),
            $this->messages(),
            $this->attributes()
        );
    }

    /**
     * 最终验证方法
     * 
     */
    protected function handleValidate()
    {
        // 验证前准备
        $this->prepareForValidation();
        // 判断用户是否有请求权限，默认为有，如果没有权限，告知失败
        if (!$this->passesAuthorization()) $this->failedAuthorization();
        // 获取验证对象实例
        $instance = $this->getValidatorInstance();
        // 如果验证失败
        if ($instance->fails()) $this->failedValidation($instance);
        // 验证后处理
        $this->passedValidation();
    }

    /**
     * Handle a failed validation attempt.
     *
     * @param  \Illuminate\Contracts\Validation\Validator  $validator
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    protected function failedValidation(Validator $validator)
    {
        // 获取提示信息
        $error             = empty($validator->errors()->all()[0]) ? '' : $validator->errors()->all()[0];
        // 响应抛出
        throw               new ValidException($error,$validator->errors()->toArray());
    }

}
