package com.malk.utils;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.webservice.SoapClient;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.malk.server.common.VenR;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.util.Map;


/**
 * HttpUtil [取值详见CatchException]
 * -
 * - 后端方法说明
 * 1. param 使用转码, 避免中文字符导致参数识别异常, 如中文小括号
 * 2. body 注意 .form 是表单内容; .body 才是请求内容 [Restful请求]
 * 3. form 方式, 支持文件上传, 参数包到 map. body 不允许为空, 导致异常
 * 4. response 请求后若需要转为数据类型, 读取请求结果body为JSONString. 若直接获取格式字符串, 不能再转为对象或Map
 * -
 * -  前端请求格式
 * 1. getDefault: url上param, 后端取值@requestParam，也可用request.getParameterMap().getDefault(“key”), 参数会被放入一个集合
 * 2. post: body内json, 后端取值@requestBody, Map 或转为实体
 * 3. form: body内格式为form, 和content-type有关系, 需要为form格式后端才能读取: 不能使用@RequestBody，参数会自动解析到实体; 若不是实体通过方法转Map
 * 4. upload: body-formData, 一般用于文件上传, 追加数据流
 */
@Slf4j
public abstract class UtilHttp {

    public enum METHOD {
        POST,
        GET,
        PUT,
        PATCH,
        DELETE,
        UPLOAD
    }

    /******** 创建请求 ********/

    // todo: 认证格式 - Authorization:Basic base64(“admin:密码”)
    public static String doRequest(METHOD method, String url, Map header, Map<String, Object> param, Object body, Map form, String usr, String pwd) {
        log.debug("请求入参, url = {}, header = {}, param = {}, body = {}, form = {}", url, header, param, body, form);
        String path = HttpUtil.urlWithForm(url, param, CharsetUtil.CHARSET_UTF_8, true);
        HttpRequest request;
        switch (method) {
            case GET:
                request = HttpRequest.get(path);
                break;
            case PUT:
                request = HttpRequest.put(path);
                break;
            case PATCH:
                request = HttpRequest.patch(path);
                break;
            case DELETE:
                request = HttpRequest.delete(path);
                break;
            default:
                request = HttpRequest.post(path);
                break;
        }
        request.addHeaders(header).form(form); // form允许为空
        if (ObjectUtil.isNotNull(body)) {
            // ppExt: 序列号保留null字段, 不做过滤
            request.body(JSON.toJSONString(body, SerializerFeature.WriteMapNullValue));
        }
        if (StringUtils.isNotBlank(usr) && StringUtils.isNotBlank(pwd)) {
            request.basicAuth(usr, pwd);
        }
        HttpResponse out = request.execute();
        log.debug("请求响应, {}, {}", out.getStatus(), out.body()); // http 状态判定
        // ppExt: 外部接口http状态异常, 不直接阻断, 通过 r.assertSuccess(); 校验
        //McException.assertException(out.getStatus() != 200, String.valueOf(out.getStatus()), "ERROR HTTP STATUS EXCEPTION");
        return out.body();
    }

    public static String doRequest(METHOD method, String url, Map header, Map<String, Object> param, Map body, Map form) {
        return doRequest(method, url, header, param, body, form, null, null);
    }

    public static String doRequest(METHOD method, String url, Map header, Map<String, Object> param, Map body) {
        return doRequest(method, url, header, param, body, null);
    }

    /*** ------------ 创建POST请求 ------------ ***/

    public static String doPost(String url, Map header, Map<String, Object> param, Object body, Map form) {
        return doRequest(METHOD.POST, url, header, param, body, form, null, null); // 兼容非 map
    }

    public static String doPost(String url, Map header, Map<String, Object> param, Map body, Map form) {
        return doRequest(METHOD.POST, url, header, param, body, form);
    }

    public static String doPost(String url, Map header, Map<String, Object> param, Map body) {
        return doRequest(METHOD.POST, url, header, param, body);
    }

    public static VenR doPost(String url, Map header, Map<String, Object> param, Map body, Map form, Class rClass) {
        String rsp = doPost(url, header, param, body, form);
        VenR r = (VenR) JSON.parseObject(rsp, rClass);
        r.assertSuccess();
        return r;
    }

    public static VenR doPost(String url, Map header, Map<String, Object> param, Map body, Class rClass) {
        return doPost(url, header, param, body, null, rClass);
    }

    public static VenR doPost(String url, Map header, Map body, Class rClass) {
        return doPost(url, header, null, body, rClass);
    }


    /*** ------------ 创建GET请求 ------------ ***/

    public static String doGet(String url, Map header, Map param) {
        return doRequest(METHOD.GET, url, header, param, null);
    }

    public static VenR doGet(String url, Map header, Map param, Class rClass) {
        String rsp = doGet(url, header, param);
        VenR r = (VenR) JSON.parseObject(rsp, rClass);
        r.assertSuccess();
        return r;
    }

    public static VenR doGet(String url, Map<String, Object> param, Class rClass) {
        return doGet(url, null, param, rClass);
    }

    /*** ------------ 创建PUT请求 ------------ ***/

    public static String doPut(String url, Map header, Map param, Map body) {
        return doRequest(METHOD.PUT, url, header, param, body);
    }

    public static VenR doPut(String url, Map header, Map param, Map body, Class rClass) {
        String rsp = doPut(url, header, param, body);
        VenR r = (VenR) JSON.parseObject(rsp, rClass);
        r.assertSuccess();
        return r;
    }

    public static VenR doPut(String url, Map header, Map body, Class rClass) {
        return doPut(url, header, null, body, rClass);
    }

    public static VenR doPut(String url, Map body, Class rClass) {
        return doPut(url, null, null, body, rClass);
    }

    /*** ------------ 创建DELETE请求 ------------ ***/

    public static String doDelete(String url, Map header, Map param, Map body) {
        return doRequest(METHOD.DELETE, url, header, param, body);
    }

    public static VenR doDelete(String url, Map header, Map param, Class rClass) {
        String rsp = doDelete(url, header, param, (Map) null);
        VenR r = (VenR) JSON.parseObject(rsp, rClass);
        r.assertSuccess();
        return r;
    }

    /*** ------------ 创建PATCH请求 ------------ ***/

    public static String doPatch(String url, Map header, Map param, Map body) {
        return doRequest(METHOD.PATCH, url, header, param, body);
    }

    public static VenR doPatch(String url, Map header, Map param, Map body, Class rClass) {
        String rsp = doPatch(url, header, param, body);
        VenR r = (VenR) JSON.parseObject(rsp, rClass);
        r.assertSuccess();
        return r;
    }

    public static VenR doPatch(String url, Map header, Map body, Class rClass) {
        return doPatch(url, header, null, body, rClass);
    }

    /*** ------------ 文件访问处理 ------------ ***/

    public static String doUpload(String url, Map header, Map<String, Object> param, Map form) {
        return doPost(url, header, param, null, form);
    }

    public static VenR doUpload(String url, Map header, Map<String, Object> param, Map form, Class rClass) {
        return doPost(url, header, param, null, form, rClass);
    }

    public static VenR doUpload(String url, Map header, Map form, Class rClass) {
        return doPost(url, header, null, null, form, rClass);
    }

    public static long doDownload(String url, File file) {
        return HttpUtil.downloadFile(url, file);
    }

    /*** ------------ 创建SOAP请求 ------------ ***/

    public static Map doSoap(String url, Map param, String methodName, String namespaceURI) {
        log.debug("请求入参, url = {}, param = {}, methodName = {}, namespaceURI = {}", url, param, methodName, namespaceURI);
        // 新建客户端
        SoapClient client = SoapClient.create(url)
                // 设置要请求的方法，此接口方法前缀为web，传入对应的命名空间
                .setMethod(methodName, namespaceURI)
                // 设置参数，此处自动添加方法的前缀：web2
                .setParams(param);
        // 发送请求，参数true表示返回一个格式化后的XML内容
        // 返回内容为XML字符串，可以配合XmlUtil解析这个响应
        String rsp = client.send(true);
        log.debug("请求响应, {}", rsp);
        return XmlUtil.xmlToMap(rsp);
    }
}
