<?php
//  ---------------------------------------------------------------------------------
//    _____   _             _                   _   _____
//   |  __ \ | |           | |    /\           (_) |  __ \
//   | |__) || |__    __ _ | |   /  \    _ __   _  | |__) |_ __  ___
//   |  ___/ | '_ \  / _` || |  / /\ \  | '_ \ | | |  ___/| '__|/ _ \
//   | |     | | | || (_| || | / ____ \ | |_) || | | |    | |  | (_) |
//   |_|     |_| |_| \__,_||_|/_/    \_\| .__/ |_| |_|    |_|   \___/
//                                      | |
//                                      |_|
//   PhalApi Pro 专业版
//   广州果创网络科技有限公司
//
//  ---------------------------------------------------------------------------------
//
//   一、协议的许可和权利
//    1. 您可以在完全遵守本协议的基础上，将本软件应用于商业用途；
//    2. 您可以在协议规定的约束和限制范围内修改本产品源代码或界面风格以适应您的要求；
//    3. 您拥有使用本产品中的全部内容资料、商品信息及其他信息的所有权，并独立承担与其内容相关的
//       法律义务；
//    4. 获得商业授权之后，您可以将本软件应用于商业用途，自授权时刻起，在技术支持期限内拥有通过
//       指定的方式获得指定范围内的技术支持服务；
//
//   二、协议的约束和限制
//    1. 未获商业授权之前，禁止将本软件用于商业用途（包括但不限于企业法人经营的产品、经营性产品
//       以及以盈利为目的或实现盈利产品）；
//    2. 未获商业授权之前，禁止在本产品的整体或在任何部分基础上发展任何派生版本、修改版本或第三
//       方版本用于重新开发；
//    3. 如果您未能遵守本协议的条款，您的授权将被终止，所被许可的权利将被收回并承担相应法律责任；
//
//   三、有限担保和免责声明
//    1. 本软件及所附带的文件是作为不提供任何明确的或隐含的赔偿或担保的形式提供的；
//    2. 用户出于自愿而使用本软件，您必须了解使用本软件的风险，在尚未获得商业授权之前，我们不承
//       诺提供任何形式的技术支持、使用担保，也不承担任何因使用本软件而产生问题的相关责任；
//    3. 广州果创网络科技有限公司不对使用本产品构建的商城中的内容信息承担责任，但在不侵犯用户隐
//       私信息的前提下，保留以任何方式获取用户信息及商品信息的权利；
//
//   有关本产品最终用户授权协议、商业授权与技术服务的详细内容，均由广州果创网络科技有限公司独家
//   提供。广州果创网络科技有限公司拥有在不事先通知的情况下，修改授权协议的权力，修改后的协议对
//   改变之日起的新授权用户生效。电子文本形式的授权协议如同双方书面签署的协议一样，具有完全的和
//   等同的法律效力。您一旦开始修改、安装或使用本产品，即被视为完全理解并接受本协议的各项条款，
//   在享有上述条款授予的权力的同时，受到相关的约束和限制。协议许可范围以外的行为，将直接违反本
//   授权协议并构成侵权，我们有权随时终止授权，责令停止损害，并保留追究相关责任的权力。
//
//  ---------------------------------------------------------------------------------

namespace Base\Domain;

use Base\Model\TestLog;
use Base\Model\TestSample as TestSampleModel;
use PhalApi\CUrl;
use PhalApi\Exception\InternalServerErrorException;

class TestSample {

    public function getTestResult() {
        $resArr = array(
            'success' => 0,
            'fail' => 0,
            'noTest' => 0,
            'all' => 0,
            'percent' => "100%",
        );
        $model = new TestSampleModel();
        $arr = $model->getTestTotal();
        foreach ($arr as $item){
            if($item['sample_status'] == 0){
                $resArr['noTest'] = $item['num'];
            }elseif ($item['sample_status'] == 1){
                $resArr['success'] = $item['num'];
            }else{
                $resArr['fail'] += $item['num'];
            }
        }
        $resArr['all'] = $resArr['noTest'] + $resArr['success'] + $resArr['fail'];
        $resArr['percent'] = round($resArr['success'] * 100 / $resArr['all'] )."％";
        return $resArr;
    }

    public function updateTestSample($id, $uid, $sampleName, $apiName, $sampleRequest, $sampleResponse, $sampleHost,  $accessTokenConfig){
        $model = new TestSampleModel();
        $beforeData = $model->get($id);
        $updateData = array();
        if($uid !== null){
            $updateData['uid'] = $uid;
        }
        if($apiName !== null){
            $updateData['api_name'] = $apiName;
        }
        if($sampleName !== null){
            $updateData['sample_name'] = $sampleName;
        }
        if($sampleRequest !== null){
            $updateData['sample_request'] = $sampleRequest;
        }
        if($sampleResponse !== null){
            $updateData['sample_response'] = $sampleResponse;
        }
        if($sampleHost !== null){
            $updateData['sample_host'] = $sampleHost;
        }
        if($accessTokenConfig !== null){
            $updateData['access_token'] = $accessTokenConfig;
            $atDomain = new GlobalAtConfig();
            // 原先的使用次数-1
            $atDomain->incrUseNum($beforeData['access_token'], 2);
            $atDomain->incrUseNum($accessTokenConfig, 1);
        }
        if(empty($updateData)){
            return false;
        }else{
            return $model->update($id, $updateData);
        }
    }

    public function getTestSampleListTotal($sampleName, $apiName , $sampleStatus, $page, $perpage){
        $whereParams = array();
        if($sampleName !== null){
            $whereParams["sample_name LIKE ?"] = '%' . $sampleName . '%';
        }
        if($apiName !== null){
            $whereParams["api_name LIKE ?"] = '%' . $apiName . '%';
        }
        if($sampleStatus !== null){
            $whereParams["sample_status"] = $sampleStatus;
        }
        $select = array("id", "uid", "api_name", "sample_name", "uid", "sample_status", "last_time", "access_token");
        $model = new TestSampleModel();
        $data = $model->getListEx($whereParams, array(), $select, 'id DESC', $page, $perpage);
        // 将uid转为username
        if(!empty($data['items'])){
            $ids = array_column($data['items'], 'uid');
            $domain = new User();
            $map = $domain->getUserNameByUid($ids);
            foreach ($data['items'] as &$item){
                $item['uid'] = isset($map[$item['uid']]) ? $map[$item['uid']] : \PhalApi\T('unknow');
                $atDomain = new GlobalAtConfig();
                $item['access_token_name'] = $atDomain->getName($item['access_token']);
            }
        }
        return $data;
    }

    public function getTestSample($id){
        $model = new TestSampleModel();
        $data = $model->getBy('id', $id, array(), "*");
        if(!empty($data)){
            $domain = new User();
            $map = $domain->getUserNameByUid(array($data['uid']));
            $data['uid'] = isset($map[$data['uid']]) ? $map[$data['uid']] : \PhalApi\T('unknow');
            $atDomain = new GlobalAtConfig();
            $data['access_token_name'] = $atDomain->getName($data['access_token']);
        }
        $data['percent'] = 0;
        $data['last_test_res'] = "";
        $data['success'] = 0;
        $data['fail'] = 0;
        $logModel = new TestLog();
        $logData = $logModel->getListEx(array('sample_id' => $id), array(), 'test_time, test_status, test_result, test_detail', 'test_time DESC');
        if(!empty($logData['items'])){
            $data['last_test_res'] = $logData['items'][0]['test_result'];
            $success = $fail = 0;
            foreach ($logData['items'] as &$item){
                if($item['test_status'] == 1){
                    $success++;
                }else{
                    $fail++;
                }
                $item['test_detail'] = json_decode($item['test_detail'], true);
                $formatDetail = array();
                if(!empty($item['test_detail'])){
                    foreach ($item['test_detail'] as $k => $v){
                        $formatDetail[] = array('field' => $k, 'err' => $v);
                    }
                }
                $item['test_detail'] = $formatDetail;
                unset($item);
            }
            $data['log'] = $logData['items'];
            $data['percent'] = round($success * 100 / ($success + $fail))."％";
            $data['success'] = $success;
            $data['fail'] = $fail;
        }
        return $data;
    }

    public function checkSample($id){
        $model = new TestSampleModel();
        $sampleData = $model->get($id);
        if(empty($sampleData)){
            return false;
        }
        $resArr = $this->generateRequest($sampleData);
        $checkRes = array();
        if(!$resArr){
            // 更新TestSample表
            $model->update($id, array(
                'sample_status' => 4, 'last_time' => date("Y-m-d H:i:s")
            ));
            return array('state' => 4, 'checkRes' => json_encode(array(), JSON_UNESCAPED_UNICODE));
        }
        if(empty($resArr)){
            // 没任何返回的异常
            $testState = 3;
        }else{
            $checkRes = $this->checkResponse($resArr, json_decode($sampleData['sample_response'], true));
            if(empty($checkRes)){
                // 通过测试
                $testState = 1;
            }else{
                $testState = 2;
            }
        }
        // 更新TestSample表
        $model->update($id, array(
            'sample_status' => $testState, 'last_time' => date("Y-m-d H:i:s")
        ));
        // 记录log
        $logModel = new TestLog();
        $logModel->insert([
            'sample_id' => $id, 'test_time' => date("Y-m-d H:i:s"),
            'test_status' => $testState, 'test_result' => json_encode($resArr, JSON_UNESCAPED_UNICODE),
            'test_detail' => json_encode($checkRes, JSON_UNESCAPED_UNICODE)
        ]);
        return array('state' => $testState, 'checkRes' => json_encode($checkRes, JSON_UNESCAPED_UNICODE));
    }

    public function testAllSample(){
        // 加入config配置
        $domain = new Config();
        $configName = "sample_test_switch";
        $data = $domain->getConfig($configName);
        if(empty($data)){
            $domain->addConfig($configName, 1, \PhalApi\T("test case crontab switch"));
        }else{
            $domain->editConfig($configName,1, \PhalApi\T("test case crontab switch"));
        }
        return array('code' => true, 'msg' => '');
    }

    // 测试创建的全部接口
    public function beginTestAllSamples(){
        // 清空test_detail
        $handle = fopen(API_ROOT."/bin/test/test_detail.txt",'w+');
        $domain = new Config();
        $configName = "sample_test_switch";
        $data = $domain->getConfig($configName);
        if(empty($data) || $data['config_value'] !== "1"){
            $data = json_encode(array('total' => 0, 'test' => 0));
            fwrite($handle, $data."+");
            fclose($handle);
            return false;
        }
        $model = new TestSampleModel();
        $testSamples = $model->getListEx();
        if(empty($testSamples['items'])){
            $data = json_encode(array('total' => 0, 'test' => 0));
            fwrite($handle, $data."+");
            fclose($handle);
            return false;
        }
        $total = count($testSamples['items']);
        $i = 0;
        foreach ($testSamples['items'] as $sample){
            $id = $sample['id'];
            $i++;
            $result = $this->checkSample($id);
            $data = json_encode(array('total' => $total, 'test' => $i));
            fwrite($handle, $data."+");
        }
        fclose($handle);
        // 关掉config
        $domain->editConfig($configName, 0, \PhalApi\T("test case crontab switch"));
        return true;
    }

    // -------------------- 内部方法 ------------------------//

    /**
     * @param   array $sampleData   测试用例
     * @return  array&bool
     */
    protected function generateRequest($sampleData){
        $url = $sampleData['sample_host']. "?s=". $sampleData['api_name'];
        // 获取配置
        $domain = new GlobalAtConfig();
        $accessToken = $domain->getAccessToken($sampleData['access_token'], $sampleData['sample_host']);
        if(!$accessToken){
            return false;
        }
        $url .= "&access_token=".$accessToken;
        $data = json_decode($sampleData['sample_request'], true);
        $postData = array();
        if(!empty($data)){
            foreach ($data as $item){
                $key = $item['name'];
                $postData[$key] = isset($item['value']) ? $item['value'] : '';
            }
        }
        try {
            $curl = new CUrl();
            $reqRes = $curl->post($url, $postData);
            $reqArr = json_decode($reqRes, true);
            if (!is_array($reqArr)) {
                $reqArr = array();
            }
        } catch (InternalServerErrorException $ex) {
            // log
            $reqArr = array();
        }
        return $reqArr;
    }

    /**
     * 结果验证结果
     * @param array $reqRes     接口返回
     * @param array $response   预期接口返回
     * empty($verifyResult)     没报错就是正常
     */
    protected function checkResponse($reqRes, $response){
        // 验证结果
        $verifyResult = array();
        foreach ($response as $item) {
            // 匹配规则
            $keyPath = explode('.', $item['name']);
            $realValue = null;

            $tmp = $reqRes;
            // $keyPath ['data', 'list', 'id']
            foreach ($keyPath as $p) {
                if (isset($tmp[$p])) {
                    $realValue = $tmp[$p];
                    $tmp = $tmp[$p];
                } else {
                    $verifyResult[$item['name']] = $item['name'] . \PhalApi\T('field not exists');
                    break;
                }
            }

            $expectedValue = $item['expect'];
            switch($item['operate']) {
                case '=':
                    if ($expectedValue == $realValue) {
                    } else {
                        $verifyResult[$item['name']] = \PhalApi\T('FAIL, Expected: ') . $expectedValue.' == '.\PhalApi\T('Actual: ') . $realValue;
                    }
                    break;
                case '>=':
                    if ($expectedValue >= $realValue) {
                        if(is_bool($expectedValue) || is_bool($realValue)){
                            $verifyResult[$item['name']] = \PhalApi\T("fail to verify, can not tell true or not");
                        }
                    } else {
                        $verifyResult[$item['name']] = \PhalApi\T('FAIL, Expected: ') . $expectedValue.' <= '.\PhalApi\T('Actual: ') . $realValue;
                    }
                    break;
                case '>':
                    if ($expectedValue > $realValue) {
                        if(is_bool($expectedValue) || is_bool($realValue)){
                            $verifyResult[$item['name']] = \PhalApi\T("fail to verify, can not tell true or not");
                        }
                    } else {
                        $verifyResult[$item['name']] = \PhalApi\T('FAIL, Expected: ') . $expectedValue.' <= '.\PhalApi\T('Actual: ') . $realValue;
                    }
                    break;
                case '<=':
                    if ($expectedValue <= $realValue) {
                        if(is_bool($expectedValue) || is_bool($realValue)){
                            $verifyResult[$item['name']] = \PhalApi\T("fail to verify, can not tell true or not");
                        }
                    } else {
                        $verifyResult[$item['name']] = \PhalApi\T('FAIL, Expected: ') . $expectedValue.' > '.\PhalApi\T('Actual: ') . $realValue;
                    }
                    break;
                case '<':
                    if ($expectedValue < $realValue) {
                        if(is_bool($expectedValue) || is_bool($realValue)){
                            $verifyResult[$item['name']] = \PhalApi\T("fail to verify, can not tell true or not");
                        }
                    } else {
                        $verifyResult[$item['name']] = \PhalApi\T('FAIL, Expected: ') . $expectedValue.' >= '.\PhalApi\T('Actual: ') . $realValue;
                    }
                    break;
                case '!=':
                    if ($expectedValue != $realValue) {
                    } else {
                        $verifyResult[$item['name']] = \PhalApi\T('FAIL, Expected: ') . $expectedValue.' == '.\PhalApi\T('Actual: ') . $realValue;
                    }
                    break;
                default:
                    break;
            }
        }
        return $verifyResult;
    }

}
