<?php

namespace app\common\service;

use think\exception\ValidateException;
use think\facade\Request;
use think\Service as ThinkService;
use think\Validate;

/**
 * 服务基类
 */
class Service extends ThinkService
{
    protected $exceptionClass = \Exception::class;
    protected $isExceptionClassChanged = false;
    protected $params = [];

    /**
     * 默认参数
     *
     * @return mixed
     */
    protected function params($validator = null)
    {
        $params = [];
        if ($this->params) {
            $params = $this->params;
        } else {
            $params = Request::instance()->param('', null, 'trim');
        }
        // 尝试校验
        if ($validator) {
            $this->validate($params, $validator);
        }
        return $params;
    }

    /**
     * 使用该方法需要先调用overrideParams
     *
     * @param mixed $default 默认值
     * @param string|int ...$index 数组下标
     * 
     * @return mixed
     */
    protected function pgd($default = null, ...$index)
    {
        $params = $this->params();
        foreach ($index as $i) {
            if (isset($params[$i])) {
                return $params[$i];
            }
        }
        return $default;
    }

    /**
     * 使用该方法需要先调用overrideParams
     *
     * @param string|int ...$index 数组下标
     * 
     * @return mixed
     */
    protected function pg(...$index)
    {
        $params = $this->params();
        foreach ($index as $i) {
            if (isset($params[$i])) {
                return $params[$i];
            }
        }
        return null;
    }

    protected function req(...$index)
    {
        $params = $this->params();
        foreach ($index as $i) {
            if (isset($params[$i])) {
                return $params[$i];
            }
        }
        $desc = implode('或', $index);
        throw $this->exception("缺少参数 $desc");
    }

    /**
     * 获取主键
     *
     * @param string $name
     * 
     * @return mixed
     */
    protected function primaryKey($name = 'id')
    {
        $params = $this->params(\think\facade\Validate::rule([$name => 'require']));
        return $params[$name];
    }

    /**
     * 通过主键获取对象
     *
     * @template T
     * @param class-string<T> $objClass
     * @param string $name
     * 
     * @return T
     */
    protected function one(string $objClass, $name = 'id')
    {
        $id = $this->primaryKey($name);
        $obj = (new $objClass)->find($id);
        if (!$obj) {
            throw $this->exception("未找到对象 $objClass $name=$id", 404);
        }
        return $obj;
    }

    /**
     * 覆盖params
     * - 优先级 function($params) > overrideParams($params) > autoget
     *
     * @param mixed $params
     * 
     * @return Service
     */
    public function overrideParams($params)
    {
        $this->params = $params;
        return $this;
    }

    protected function autoParams($params)
    {
        $this->params = $params ?: $this->params();
        return $this->params;
    }

    /**
     * 分页参数
     *
     * @param int $max
     * 
     * @return array
     */
    public function pageParams($max = 100)
    {
        $this->validate($this->params, \think\facade\Validate::rule([
            'pageParams.page' => '>=:0',
            'pageParams.size' => "<=:{$max}"
        ]));
        $pageParams = $this->params['pageParams'];
        $pageParams['size'] = isset($pageParams['size']) ? $pageParams['size'] : 15;
        $pageParams['page'] = isset($pageParams['page']) ? $pageParams['page'] : 1;
        return $pageParams;
    }

    public function tp6Page($max = 100)
    {
        ['page' => $page, 'size' => $size] = $this->pageParams($max);
        return [
            'page' => $page,
            'list_rows' => $size,
        ];
    }

    /**
     * 校验
     *
     * @param mixed $data
     * @param Validate $validator
     * @param array $msg
     * 
     * @return bool
     */
    public function validate($data, Validate $validator)
    {
        if (!$validator->check($data)) {
            throw new ValidateException($validator->getError());
        }
        return true;
    }

    /**
     * 设置或获取内置异常类
     *
     * @param string $class 异常类
     * 
     * @return self|string
     */
    public function exceptionClass($class = null)
    {
        if (empty($class)) {
            return $this->exceptionClass;
        }
        if ($this->isExceptionClassChanged) {
            return $this;
        }
        $this->exceptionClass = $class;
        return $this;
    }

    /**
     * 锁定异常类
     *
     * @return self
     */
    public function lockException()
    {
        $this->isExceptionClassChanged = true;
        return $this;
    }

    /**
     * 封装异常
     *
     * @param \Exception $e 父异常
     * @param string $prefixMessage 消息前缀
     * @param  $newCode 新的code
     * @param string $class 如有需要使用另一个class
     * 
     * @return \Exception
     */
    public function warpException(\Exception $e, $prefixMessage = "发生异常:\n ", $newCode = null, $class = null)
    {
        $class = $class ?: $this->exceptionClass;
        $newCode = $newCode ?? $e->getCode();
        return new $class("{$prefixMessage}[{$e->getCode()}]:" . $e->getMessage(), $newCode, $e);
    }

    /**
     * 附带新异常
     *
     * @param \Closure $func 函数
     * @param mixed ...$args 参数
     * 
     * @return void
     */
    public function withException(\Closure $func, ...$args)
    {
        try {
            $func(...$args);
        } catch (\Exception $e) {
            throw $this->warpException($e);
        }
    }

    /**
     * 使用内定的异常类
     *
     * @param string $message 消息
     * @param $code code
     * @param \Exception $parent 父异常
     * @param string $class 如有需要使用另一个class
     *  
     * @return \Exception
     */
    public function exception($message = '', $code = -1, $parent = null, $class = null)
    {
        $class = $class ?: $this->exceptionClass;
        return new $class($message, $code, $parent);
    }
}