<?php

// +----------------------------------------------------------------------
// | ThinkLibrary 6.0 for ThinkPhP 6.0
// +----------------------------------------------------------------------
// | 版权所有 2017~2020 [ https://www.dtapp.net ]
// +----------------------------------------------------------------------
// | 官方网站: https://gitee.com/liguangchun/ThinkLibrary
// +----------------------------------------------------------------------
// | 开源协议 ( https://mit-license.org )
// +----------------------------------------------------------------------
// | gitee 仓库地址 ：https://gitee.com/liguangchun/ThinkLibrary
// | github 仓库地址 ：https://github.com/GC0202/ThinkLibrary
// | Packagist 地址 ：https://packagist.org/packages/liguangchun/think-library
// +----------------------------------------------------------------------

namespace DtApp\ThinkLibrary;

use DtApp\ThinkLibrary\helper\ValidateHelper;
use stdClass;
use think\App;
use think\exception\HttpResponseException;
use think\Request;

/**
 * 标准Api控制器基类
 * Class ApiController
 * @package DtApp\ThinkLibrary
 */
abstract class ApiController extends stdClass
{
    /**
     * 应用容器
     * @var App
     */
    public $app;

    /**
     * 请求对象
     * @var Request
     */
    public $request;

    /**
     * 解密后数据
     * @var
     */
    private $aes_decrypt_data;

    /**
     * 加密相关的东西
     * @var string
     */
    private $aes_md5, $aes_md5_iv = '';

    /**
     * ApiController constructor.
     * @param App $app
     */
    public function __construct(App $app)
    {
        $this->app = $app;
        $this->request = $app->request;
        $this->app->bind('DtApp\ThinkLibrary\ApiController', $this);
        if (in_array($this->request->action(), get_class_methods(__CLASS__))) {
            $this->error('Access without permission.');
        }
        $this->initialize();
    }

    /**
     * 控制器初始化
     */
    protected function initialize()
    {
    }

    /**
     * 返回失败的操作
     * @param mixed $msg 消息内容
     * @param mixed $data 返回数据
     * @param integer $code 返回代码
     */
    public function error($msg = 'error', $code = 1, $data = []): void
    {
        throw new HttpResponseException(json([
            'code' => $code,
            'msg' => $msg,
            'timestamp' => time(),
            'data' => $data,
        ]));
    }

    /**
     * 返回成功的操作
     * @param mixed $msg 消息内容
     * @param mixed $data 返回数据
     * @param integer $code 返回代码
     */
    public function success($data = [], $msg = 'success', $code = 0): void
    {
        throw new HttpResponseException(json([
            'code' => $code,
            'msg' => $msg,
            'timestamp' => time(),
            'data' => $data,
        ]));
    }

    /**
     * key
     * @param string $name 参数名
     * @return $this
     */
    public function setAesMd5($name = 'sniff_h5'): self
    {
        $value = config("dtapp.md5.{$name}");
        $this->aes_md5 = $value;
        return $this;
    }

    /**
     * iv
     * @return $this
     */
    private function setAesMd5Iv(): self
    {
        $value = config("dtapp.md5.bcw");
        $this->aes_md5_iv = $value;
        return $this;
    }

    /**
     * 返回成功的操作
     * @param mixed $data 返回数据
     * @param mixed $msg 消息内容
     * @param integer $code 返回代码
     */
    public function aesSuccess($data = [], $msg = 'success', $code = 0)
    {
        $timestamp = time();
        throw new HttpResponseException(json([
            'code' => $code,
            'msg' => $msg,
            'timestamp' => $timestamp,
            'data' => [
                'aes' => $this->encrypt($data, $timestamp)
            ],
        ]));
    }

    /**
     * URL重定向
     * @param string $url 跳转链接
     * @param integer $code 跳转代码
     */
    public function redirect($url, $code = 301): void
    {
        throw new HttpResponseException(redirect($url, $code));
    }

    /**
     * @param array $rules
     * @param string $type
     * @return mixed
     */
    protected function _vali(array $rules, $type = '')
    {
        return ValidateHelper::instance()
            ->init($rules, $type);
    }

    /**
     * 获取解密后的数据
     * @param string $name
     * @param null $default
     * @return mixed
     */
    public function getAesDecryptData(string $name = '', $default = null)
    {
        if (empty($name)) {
            return $this->aes_decrypt_data;
        }

        return $this->aes_decrypt_data[$name] ?? $default;
    }

    /**
     * 验证接口签名
     */
    public function _judgeSign()
    {
        // 加密的数据参数
        $aes = $this->request->post('aes', '');
        if (empty($aes)) {
            $this->error('数据未签名！', 104);
        }
        // 获取时间数据
        $timestamp = $this->request->get('timestamp', 0);
        // 判断是否有时间
        if (empty($timestamp)) {
            $this->error('数据异常！', 105);
        }
        // 解密
        $aes_decode = $this->decrypt($aes, $timestamp);
        if (empty($aes_decode)) {
            $this->error('解密失败', 106);
        }
        $data = json_decode($aes_decode, true);
        // 判断是不是小于服务器时间
        $before = strtotime('-2minute');
        $rear = strtotime('+2minute');
        if ($timestamp <= $rear && $timestamp >= $before) {
            $this->aes_decrypt_data = $data;
        } else {
            $this->error('已超时，请重新尝试！');
        }
    }

    /**
     * 加密
     * @param $data
     * @param int $timestamp
     * @return bool|string
     */
    private function encrypt($data, int $timestamp)
    {
        if (empty($this->aes_md5)) {
            $this->setAesMd5();
        }
        if (empty($this->aes_md5_iv)) {
            $this->setAesMd5Iv();
        }
        if (!empty(is_array($data))) {
            $data = json_encode($data);
        }
        return urlencode(base64_encode(openssl_encrypt($data, 'AES-128-CBC', $this->aes_md5, 1, $this->aes_md5_iv . $timestamp)));
    }

    /**
     * 解密
     * @param string $data
     * @param int $timestamp
     * @return bool|false|string
     */
    private function decrypt(string $data, int $timestamp)
    {
        if (empty($this->aes_md5)) {
            $this->setAesMd5();
        }
        if (empty($this->aes_md5_iv)) {
            $this->setAesMd5Iv();
        }
        return openssl_decrypt(base64_decode(urldecode($data)), "AES-128-CBC", $this->aes_md5, true, $this->aes_md5_iv . $timestamp);
    }
}
