package com.hzya.frame.sys.apireflection.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.Method;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hzya.frame.sys.apireflection.entity.ApiReflectionDTO;
import com.hzya.frame.sys.apireflection.entity.ApiReflectionDetailEntity;
import com.hzya.frame.sys.apireflection.entity.ParameterPropertyDTO;
import com.hzya.frame.sys.apireflection.service.IApiReflectionDetailService;
import com.hzya.frame.sys.apireflection.service.IApiReflectionService;
import com.hzya.frame.sys.appApi.entity.AppApi;
import com.hzya.frame.sys.apireflection.service.IApiHandleService;
import com.hzya.frame.sys.appApi.service.IAppApiService;
import com.hzya.frame.sys.service.IExcApiService;
import com.hzya.frame.sys.sysenum.SysEnum;
import com.hzya.frame.web.entity.BaseResult;
import com.hzya.frame.web.entity.JsonResultEntity;
import org.apache.commons.collections.CollectionUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mockito.stubbing.ValidableAnswer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description
 * @Author xiangerlin
 * @Date 2023/7/26 11:53
 **/
@Service(value = "apiHandleService")
public class ApiHandleServiceImpl implements IApiHandleService {

    @Autowired
    private IAppApiService appApiService;
    @Autowired
    private IApiReflectionService apiReflectionService;
    @Autowired
    private IExcApiService excApiService;
    @Autowired
    private IApiReflectionDetailService apiReflectionDetailService;
    public static final String AUTHPORT = SysEnum.AUTHPORT.getValue();//授权参数
    public static final String EXAMPLE = SysEnum.EXAMPLE.getValue();//示例值
    public static final String PARAMETERNAME = SysEnum.PARAMETERNAME.getValue();
    public static final String PARAMETERTYPE = SysEnum.PARAMETERTYPE.getValue();

    Logger logger = LogManager.getLogger(getClass());


    /**
     * 这个方法用的是 JSONObject的putIfAbsent方法实现的
     * 左边主表字段对应右边子表的情况 暂不支持
     *
     * @param json
     * @return
     * @throws Exception
     */
    @Override
    public JsonResultEntity executeApi(JSONObject json) throws Exception {
        JSONObject jsonObject = checkParam(json);
        if (StrUtil.isNotEmpty(jsonObject.getString("msg"))){
            return BaseResult.getFailureMessageEntity(jsonObject.getString("msg"));
        }
        //场景
        ApiReflectionDTO apiReflectionDTO = jsonObject.getObject("main",ApiReflectionDTO.class);
        if (null == apiReflectionDTO) {
            return BaseResult.getFailureMessageEntity("根据场景id查询不到场景");
        }
        //源接口
        String source_res = null;
        try {
            apiReflectionDTO.setSource_expand_param(json.getJSONObject("jsonStr").getString("source_expand_param"));
            JSONObject sourceRes = excApiService.execute(apiReflectionDTO.getSource_api_id());
            //source_res = sourceApi(apiReflectionDTO);
            if (null != sourceRes && sourceRes.getBoolean("flag")){
                source_res = sourceRes.getString("msg");
            }
        } catch (Exception e) {
            return BaseResult.getFailureMessageEntity(String.format("源接口:%s调用失败", apiReflectionDTO.getSource_api()), e.getMessage());
        }
        //目标接口
        try{
            if (StrUtil.isEmpty(source_res)){
                return BaseResult.getFailureMessageEntity(String.format("源接口:%s调用失败", apiReflectionDTO.getSource_api()), "没有返回参数");
            }
            JSONObject targetRes = targetApi(jsonObject, source_res);
            if (targetRes.getBoolean("flag")){
                return BaseResult.getSuccessMessageEntity("执行成功", targetRes.getString("msg"));
            }else {
                return BaseResult.getFailureMessageEntity(String.format("目标接口:%s调用失败", apiReflectionDTO.getTarget_api()), targetRes.getString("msg"));
            }
        }catch (Exception e){
            return BaseResult.getFailureMessageEntity(String.format("目标接口:%s调用失败", apiReflectionDTO.getTarget_api()), e.getMessage());
        }
    }

    //目标接口
    private JSONObject targetApi(JSONObject jsonObject, String source_res) {
        ApiReflectionDTO apiReflectionDTO = jsonObject.getObject("main",ApiReflectionDTO.class);
        //配置的字段映射关系
        List<ApiReflectionDetailEntity> apiRefDetailList = jsonObject.getObject("details",List.class);
        //目标接口body参数
        List<ParameterPropertyDTO> paramProList = JSON.parseArray(apiReflectionDTO.getTarget_body_in(), ParameterPropertyDTO.class);//目标接口body_in参数
        JSONObject param = new JSONObject();//最终的参数
        JSONObject data = new JSONObject();//body参数
        for (ApiReflectionDetailEntity ref : apiRefDetailList) {//循环取配置参数
            int index = 0;
            String[] pathSegments = ref.getTarget_path().split("\\.");//分割path
            if (pathSegments.length > 1) {//如果大于1说明是有层级的
                JSONObject sub = data;
                String preSegment = null;
                for (int i = 0; i < pathSegments.length - 1; i++) {
                    String segment = pathSegments[i];
                    sub.putIfAbsent(segment, new JSONObject());
                    sub = sub.getJSONObject(segment);
                    preSegment = segment;
                    index = i;
                }
                String lastSegment = pathSegments[pathSegments.length - 1];//最后一个节点
                Object valObj = getByPath(source_res, ref.getSource_path());//获取源接口返回的数据
                String varStr = JSONUtil.toJsonStr(valObj);
                if (!JSONUtil.isTypeJSONArray(varStr)) {//判断一下是返回值是否为数组。 这里不能根据返回值来判断是否数组，要根据节点的属性来判断
                    sub.put(lastSegment, valObj);
                } else {
                    String currentPath = String.join(".", Arrays.copyOf(pathSegments, pathSegments.length - (index + 1)));//
                    String preData = data.getJSONObject(currentPath).getString(preSegment);//
                    if (StrUtil.isNotEmpty(preData)){
                        JSONArray jsonArray;
                        if (JSONUtil.isTypeJSONObject(preData)) {
                            jsonArray = new JSONArray();
                            data.getJSONObject(currentPath).put(preSegment, jsonArray);
                        } else {
                            jsonArray = data.getJSONObject(currentPath).getJSONArray(preSegment);
                        }
                        String val = JSONUtil.toJsonStr(valObj);
                        if (StrUtil.isNotEmpty(val)) {
                            if (JSONUtil.isTypeJSONArray(val)) {
                                JSONArray array = JSONObject.parseArray(val);
                                for (int i = 0; i < array.size(); i++) {
                                    String detailVal = array.getString(i);
                                    if (!JSONUtil.isTypeJSON(detailVal)) {
                                        JSONObject detailObj = null;
                                        if (jsonArray.size() > 0 && jsonArray.size() > i) {
                                            detailObj = (JSONObject) jsonArray.get(i);
                                        }
                                        if (detailObj == null) {
                                            detailObj = new JSONObject();
                                            detailObj.put(lastSegment, detailVal);
                                            jsonArray.add(detailObj);
                                        } else {
                                            detailObj.put(lastSegment, detailVal);
                                        }
                                    }
                                }
                            }
                        }
                    }else {
                        //判断一下是不是主表返回数组的情况
                        JSONArray jsonArray = JSON.parseArray(varStr);
                        if (CollectionUtils.isNotEmpty(jsonArray)){

                        }
                    }
                }
            } else {
                if (pathSegments.length == 1) {
                    param.put(ref.getTarget_field(), getByPath(source_res, ref.getSource_path()));
                }
            }
        }

        JSONObject targetObj = new JSONObject();
        targetObj.put("api_id",apiReflectionDTO.getTarget_api_id());
        targetObj.put("expand_param",data.toJSONString());
        try {
            //调用目标接口
            JSONObject targetRes = excApiService.execute(targetObj);
            return targetRes;
        }catch (Exception e){
            logger.error("执行api失败:{}",apiReflectionDTO.getTarget_api(),e.getMessage());
            JSONObject error = new JSONObject();
            error.put("flag",false);
            error.put("msg",e.getMessage());
            return error;
        }
    }




    //解构json对象
    private static void traverseJSONObject(JSONObject jsonObject, String currentPath, JSONObject result) {
        for (String key : jsonObject.keySet()) {
            Object value = jsonObject.get(key);
            String newPath = currentPath.isEmpty() ? key : currentPath + "." + key;
            if (value instanceof JSONObject) {
                traverseJSONObject((JSONObject) value, newPath, result);
            } else {
                result.put(newPath, value);
            }
        }
    }


    //检查目标接口是否需要登陆，登陆完成后设置token参数
    private void checkLogin(ApiReflectionDTO apiReflectionDTO, HttpRequest target_api, JSONObject param) {
        if (SysEnum.NEED_LOGIN.getValue().equals(apiReflectionDTO.getNeed_login())) {
            //找到登陆接口
            AppApi loginApi = appApiService.getByid(apiReflectionDTO.getAuthentication_port());
            if (null != loginApi) {
                //构造登陆接口
                HttpRequest login_api = HttpRequest.of(loginApi.getDestinationAddress());
                login_api.method(Method.valueOf(loginApi.getRequestMethod().toUpperCase()));
                login_api.addHeaders(getHeaders(loginApi.getHeaderIn()));
                login_api.body(getBodys(loginApi.getBodyIn()));
                String login_res = login_api.timeout(60000).execute().body();
                cn.hutool.json.JSONObject loginJson = JSONUtil.parseObj(login_res);
                //找到目标接口请求头中的认证参数
                JSONObject auth = getAuth(apiReflectionDTO.getTarget_header());
                //取token
                String token = (String) JSONUtil.getByPath(loginJson, auth.getString(EXAMPLE));
                target_api.header(auth.getString(PARAMETERNAME), token);
                //判断目标body参数是否有认证参数
                if (param.containsKey(AUTHPORT)) {
                    param.remove(AUTHPORT);//移除这个节点
                    param.put(auth.getString(PARAMETERNAME), token);//给body添加授权参数
                }
            }
        }
    }

    //检查参数
    private JSONObject checkParam(JSONObject json){
        JSONObject res = new JSONObject();
        boolean flag =true;
        String res_msg = null;
        if (null == json) {
            res_msg="参数不能为空";
            flag = false;
        }
        if (StrUtil.isEmpty(json.getString("jsonStr"))) {
            res_msg="参数不能为空";
            flag = false;
        }
        if (StrUtil.isEmpty(json.getJSONObject("jsonStr").getString("ref_id"))) {
            res_msg="场景id不能为空";
            flag = false;
        }
        JSONObject jsonStr = json.getJSONObject("jsonStr");
        String ref_id = jsonStr.getString("ref_id");
        ApiReflectionDTO apiReflectionDTO = apiReflectionService.queryApiRefById(ref_id);
        if (null == apiReflectionDTO) {
            res_msg="根据场景id查询不到场景";
            flag = false;
        }else {
            res.put("main",apiReflectionDTO);
        }
        //查询配置的字段映射关系
        ApiReflectionDetailEntity apiRefDetail = new ApiReflectionDetailEntity();
        apiRefDetail.setMainid(ref_id);
        List<ApiReflectionDetailEntity> apiRefDetailList = apiReflectionDetailService.query(apiRefDetail);
        if (CollectionUtils.isEmpty(apiRefDetailList)) {
            res_msg="没有查询到场景明细数据";
            flag = false;
        }else {
            res.put("details",apiRefDetailList);
        }
        res.put("msg",res_msg);
        res.put("flag",flag);
        return res;
    }

    //调用源接口
    private String sourceApi(ApiReflectionDTO apiReflectionDTO) {
        HttpRequest source_api = HttpRequest.of(apiReflectionDTO.getSource_api()).timeout(Integer.valueOf(apiReflectionDTO.getSource_timeout()));
        source_api.method(Method.valueOf(apiReflectionDTO.getSource_method().toUpperCase()));
        source_api.addHeaders(getHeaders(apiReflectionDTO.getSource_header()));//请求头
        JSONObject param = new JSONObject();
        checkLogin(apiReflectionDTO, source_api, param);
        String bodys = getBodys(apiReflectionDTO.getSource_body_in());
        param.fluentPutAll(JSON.parseObject(bodys));
        if (StrUtil.isNotEmpty(apiReflectionDTO.getSource_expand_param())) {
            JSONObject expandParam = JSONObject.parseObject(apiReflectionDTO.getSource_expand_param());
            param.fluentPutAll(expandParam);
        }
        source_api.body(param.toJSONString());//body
        logger.info("源接口:{} , 请求参数:{}", source_api.getUrl(), param);
        String source_res = source_api.execute().body();
        logger.info("源接口:{} , 响应参数:{}", source_api.getUrl(), source_res);
        return source_res;
    }

    /**
     * 根据层级获取json数据
     *
     * @param str  json字符串
     * @param path 层级
     * @return
     */
    private Object getByPath(String str, String path) {
        if (StrUtil.isNotEmpty(str) && StrUtil.isNotEmpty(path)) {
            Object byPath = JSONUtil.getByPath(JSONUtil.parse(str), path);//如果是数组会返回[value,value]这种格式
            return byPath;
        }
        return null;
    }

    //请求头信息
    private Map<String, String> getHeaders(String haders) {
        if (StrUtil.isNotEmpty(haders)) {
            if (JSONUtil.isTypeJSONArray(haders)) {
                Map<String, String> headerMap = new LinkedHashMap<>();
                JSONArray headerArray = JSON.parseArray(haders);
                for (int i = 0; i < headerArray.size(); i++) {
                    JSONObject heade = (JSONObject) headerArray.get(i);
                    headerMap.put(heade.getString(PARAMETERNAME), heade.getString(EXAMPLE));
                }
                return headerMap;
            }
        }
        return null;
    }

    //body参数
    private String getBodys(String bodyStr) {
        if (StrUtil.isNotEmpty(bodyStr)) {
            if (JSONUtil.isTypeJSONArray(bodyStr)) {
                JSONObject bodyParam = new JSONObject(new LinkedHashMap<>());
                JSONArray bodyArray = JSON.parseArray(bodyStr);
                for (int i = 0; i < bodyArray.size(); i++) {
                    JSONObject body = (JSONObject) bodyArray.get(i);
                    String parameterType = body.getString(PARAMETERTYPE);
                    if (!AUTHPORT.equals(parameterType)) {//判断body中是否包含认证参数
                        bodyParam.put(body.getString(PARAMETERNAME), body.getString(EXAMPLE));
                    } else {//如有存在认证参数，特殊处理
                        JSONObject authport = new JSONObject();
                        authport.put(body.getString(PARAMETERNAME), body.getString(EXAMPLE));
                        bodyParam.put(AUTHPORT, authport);
                    }
                }
                return bodyParam.toJSONString();
            }
        }
        return null;
    }

    //认证参数
    private JSONObject getAuth(String headers) {
        if (StrUtil.isNotEmpty(headers)) {
            if (JSONUtil.isTypeJSONArray(headers)) {
                Map<String, String> headerMap = new LinkedHashMap<>();
                JSONArray headerArray = JSON.parseArray(headers);
                for (int i = 0; i < headerArray.size(); i++) {
                    JSONObject heade = (JSONObject) headerArray.get(i);
                    if (AUTHPORT.equals(heade.getString(PARAMETERTYPE))) {
                        //判断一下 认证参数的层级
                        if (JSONUtil.isTypeJSONArray(heade.getString(EXAMPLE))) {
                            List<String> list = JSONObject.parseArray(heade.getString(EXAMPLE), String.class);
                            if (CollectionUtils.isNotEmpty(list)) {
                                String join = StrUtil.join(".", list);
                                heade.put(EXAMPLE, join);
                            }
                        }
                        return heade;
                    }
                }
            }
        }
        return null;
    }
}
