package com.lemon.cases;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONPath;
import com.lemon.pojo.API;
import com.lemon.pojo.Case;
import com.lemon.pojo.JsonPathVaildate;
import com.lemon.pojo.WriteBackData;
import com.lemon.utils.AuthorizationUtils;
import com.lemon.utils.ExcelUtils;
import com.lemon.utils.HttpUtils;
import io.qameta.allure.Step;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.testng.annotations.AfterSuite;
import org.testng.annotations.BeforeSuite;


import java.io.FileInputStream;
import java.util.List;
import java.util.Objects;
import java.util.Properties;


/**
 * @program: Lemon
 * @description:
 * @author: ray
 * @create: 2022-09-26 22:09
 **/
public class BaseCase {
    // 日志
    public static Logger log = Logger.getLogger(BaseCase.class);

    /**
     * 添加Excel回写对象到回写集合中
     *
     * @param rowNum：回写行
     * @param cellNum：回写列
     * @param body：回写内容
     */
    @Step("添加回写内容")
    public void addWBD(int rowNum, int cellNum, String body) {
        //创建回写内容
        WriteBackData wbd = new WriteBackData(rowNum, cellNum, body);
        ExcelUtils.wbdList.add(wbd);
    }


    /**
     * 调用接口方法
     *
     * @param api：接口信息对象，包含Excel中sheet页信息
     * @param cas：用例信息对象，需要接口请求参数
     * @param isAuthorization：接口是否需要传递token值进行请求
     * @return
     */
    @Step("接口调用方法")
    public String call(API api, Case cas, boolean isAuthorization) {
        log.info("===================调用接口:" + api.getName() + "===================");
        String url = api.getUrl();
        String type = api.getMethod();
        String contentType = api.getContentType();
        String params = cas.getParams();
        String body = HttpUtils.call(url, type, params, contentType, isAuthorization);
        return body;
    }

    /*
     * 接口响应内容断言
     * @param cas
     * @param body
     */
    @Step("响应结果进行断言")
    public Boolean assertResponse(Case cas, String body) {
        // 定义断言是否成功
        boolean flag = false;
        // 获取预期结果
        String expectValue = cas.getExpectValue();
        // 调用parse方法解析JSON
        Object jsonObj = JSONObject.parse(expectValue);
        // 如果Case中expectValue是数组类型的JSON格式，那么采用多字段匹配断言逻辑。
        if (jsonObj instanceof JSONArray) { //  判断jsonObj是否是JSONArray的对象
            // 所有JSON数据转换为集合
            List<JsonPathVaildate> list = JSONObject.parseArray(expectValue, JsonPathVaildate.class);
            //循环，多关键字段匹配断言
            for (JsonPathVaildate jpv : list) {
                //一个断言表达式
                String expression = jpv.getExpression();
                //表达式期望值
                String value = jpv.getValue();
                //对响应结果进行一个JSONPath，寻找实际的值
                String actualValue = JSONPath.read(body, expression) == null ? "" : JSONPath.read(body, expression).toString();
                //期望值和实际值进行断言
                flag = value.equals(actualValue);
                //期望值和实际值进行断言
                System.out.println("实际值：" + actualValue + "，预期值：" + value + "断言结果" + value.equals(actualValue));
                //判断所有匹配字段是否为true
                if (flag == false) {
                    //说明断言失败
                    break;
                }
            }
            //如果Case中expectValue不是数组类型的JSON格式，那么采用等值匹配。
        } else if (jsonObj instanceof JSONObject) {
            flag = body.equals(expectValue);
        }
        return flag;
    }

    public boolean assertSql(String expectValue, String beforeSqlResult, String afterSqlResult) {
        boolean flag = false;
        if (beforeSqlResult == null && Objects.equals(expectValue, afterSqlResult)) {
            flag = true;
        }
        return flag;
    }


    public boolean assertSqlB(String beforeSqlResult,String afterSqlResult) {
        // 判断如果B段SQL执行首次为空，第二次非空
        boolean flag = false;
        if (beforeSqlResult == null && afterSqlResult != null) {
            flag = true;
        }
        return flag;
    }

    /**
     * 适用于非Excel接口和用例类型的调用。
     *
     * @param expectValue
     * @param body
     * @return
     */
    @Step("响应结果断言")
    public Boolean assertRespon(String expectValue, String body) {
        // 定义断言是否成功，默认失败
        boolean flag = false;
        //
        Object jsonObj = JSONObject.parse(expectValue);
        // 如果Case中expectValue是数组类型的JSON格式，那么采用多字段匹配断言逻辑。
        if (jsonObj instanceof JSONArray) { //  判断jsonObj是否是JSONArray的对象
            // 所有JSON数据转换为集合
            List<JsonPathVaildate> list = JSONObject.parseArray(expectValue, JsonPathVaildate.class);
            //循环，多关键字段匹配断言
            for (JsonPathVaildate jpv : list) {
                //一个断言表达式
                String expression = jpv.getExpression();
                //表达式期望值
                String value = jpv.getValue();
                //对响应结果进行一个JSONPath，寻找实际的值
                String actualValue = JSONPath.read(body, expression) == null ? "" : JSONPath.read(body, expression).toString();
                //期望值和实际值进行断言
                flag = value.equals(actualValue);
                //期望值和实际值进行断言
                System.out.println("实际值：" + actualValue + "，预期值：" + value + "断言结果" + value.equals(actualValue));
                //判断所有匹配字段是否为true
                if (flag == false) {
                    //说明断言失败
                    break;
                }
            }
            //如果Case中expectValue不是数组类型的JSON格式，那么采用等值匹配。
        } else if (jsonObj instanceof JSONObject) {
            flag = body.equals(expectValue);
        }
        return flag;
    }


    /**
     * 参数化替换方法
     *
     * @param source：被替换的字符串（params或者sql中包含${xxx}）
     * @return
     */
    @Step("参数化替换")
    public String replace(String source) {
        // 判断如果传入的字符串为空，直接返回
        if (StringUtils.isBlank(source)) {
            return source;
        }
        //遍历环境变量，取出所有的oldStr和newStr
        for (String oldStr : AuthorizationUtils.env.keySet()) {
            //判断source中是否包含oldStr,如果包含就替换newStr
            String newStr = AuthorizationUtils.env.get(oldStr);
            source = source.replace(oldStr, newStr);
        }
        return source;
    }


    /**
     * 在初始化方法中，加载参数化内容，并且存储到env环境变量中
     *
     * @throws Exception
     */
    @BeforeSuite
    @Step("初始化")
    public void init() throws Exception {
        log.info("===================项目启动===================");
        //从params.properties文件中读取参数化内容
        Properties prop = new Properties();
        FileInputStream fis = new FileInputStream("src/test/resources/paramas.properties");
        prop.load(fis);
        //把params.properties文件读取的内容存储到env环境变量中
        for (Object key : prop.keySet()) {
            Object value = prop.get(key);
            //put(String oldStr,String newStr)
            if (key.toString().contains("registerMobilePhone")) {
                AuthorizationUtils.env.put(key.toString(), value.toString());
            } else {
                AuthorizationUtils.env.put(key.toString(), value.toString());
            }
        }
    }


    /**
     * 套件执行完毕之后的操作
     */
    @AfterSuite
    @Step("结束")
    public void finish() {
        //所有的接口都执行完毕后再执行批量回写
        log.info("===================批量回写===================");
        ExcelUtils.batchWrite();
        log.info("===================项目结束===================");
    }


    /*
     * 1、allure
     *  Maven常用命令：
     *      test：执行test文件夹中所有代码
     *      package：把当前项目打包（会生成test）
     *      clean：清空当前项目下的Maven生产的文件
     *      install：把jar包安装到本地仓库（会执行test、package）
     *
     *      1、导入allure坐标
     *      2、导入surefire插件
     *      3、运行clean test（生成报表数据）
     *      4、运行io.qameta.allure:allure-maven:server 启动报表服务器
     *      5、如果你需要加上一些文字说明
     *          （1）@Test(dataProvider = "datas1",description = "充值测试")   // DTO数据传输
     *          （2）@Step("回写Excel") 详细描写自动化程序的每个执行步骤
     *          //其他注释：
     *          Features：标注主要功能模块
     *          Stories：标注Features功能模块下的分支功能
     *          Title：标注Stories下的测试用例名称
     *          Severity：标注测试用例的重要级别
     *          Description：标注测试用例的描述
     *
     * 2、mock-server
     *  当接口尚未开发完毕，创建一个虚拟接口，继续测试；
     *  https://github.com/dreamhead/moco
     *  https://github.com/dreamhead/moco/blob/master/moco-doc/apis.md
     *
     * 3、Jenkins
     *  持续抓取版本库最新代码，定时构建（执行代码），生成对应构建信息，发送邮件，生生报表。
     *
     */


    public static void main(String[] args) {

    }
}
