package com.microservices.pms.utils;

import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.CacheConstants;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.html.EscapeUtil;
import com.microservices.common.core.utils.uuid.UUID;
import com.microservices.common.httpClient.service.HttpAPIService;
import com.microservices.common.redis.service.RedisService;
import com.microservices.system.api.domain.SysFileBytes;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.microservices.pms.utils.NexusRequestUrl.RequestType;


/**
 * @author otto
 */
@Component
public class NexusRequestHelper {

    private static final Logger logger = LoggerFactory.getLogger(NexusRequestHelper.class);
    @Value("${nexus.proxy.targetAddr}")
    public String nexusUrl;
    @Value("${nexus.proxy.userName}")
    public String nexusUserName;
    @Value("${nexus.proxy.password}")
    public String nexusPassword;
    @Value("${nexus.proxy.authorization}")
    public String nexusAuthorization;
    @Autowired
    private HttpAPIService httpAPIService;
    @Autowired
    private RedisService redisService;

    public Object doRequest(HttpServletRequest request) {
        String requestId = UUID.randomUUID().toString();
        String path = request.getRequestURI();
        String queryString = request.getQueryString();
        if (StringUtils.isNotBlank(queryString)) {
            path = path + "?" + queryString;
        }


        String requestMethod = request.getMethod();
        RequestType requestType = RequestType.valueOf(requestMethod);
        Object body;
        String bodyLog;

        String contentType = request.getContentType();
        try {
            if ((contentType != null) && (contentType.toLowerCase().startsWith("multipart/form-data"))) {
                // 请求类型切换为FormData
                requestType = RequestType.POST_FORM_DATA;

                MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
                String boundary = contentType.split(";")[1];
                // 创建一个MultipartEntityBuilder
                MultipartEntityBuilder builder = MultipartEntityBuilder
                        .create()
                        .setContentType(ContentType.MULTIPART_FORM_DATA)
                        .setBoundary(boundary)
                        .setCharset(StandardCharsets.UTF_8);
                // 记录Form-Data格式Body参数
                JSONObject bodyLogJson = new JSONObject();
                for (Map.Entry<String, String[]> entry : multipartRequest.getParameterMap().entrySet()) {
                    if (!StringUtils.equals(entry.getKey(), "repository")) {
                        builder.addTextBody(entry.getKey(), entry.getValue()[0]);
                        bodyLogJson.put(entry.getKey(), entry.getValue()[0]);
                    }
                }
                // 将所有的文件添加到MultipartEntityBuilder中
                for (Map.Entry<String, MultipartFile> entry : multipartRequest.getFileMap().entrySet()) {
//                builder.addPart(entry.getKey(), new InputStreamBody(entry.getValue().getInputStream(), ContentType.MULTIPART_FORM_DATA, entry.getValue().getOriginalFilename()));
                    String encodedFilename = URLEncoder.encode(Objects.requireNonNull(entry.getValue().getOriginalFilename()), StandardCharsets.UTF_8.toString());
                    builder.addBinaryBody(entry.getKey(), entry.getValue().getBytes()
                            , ContentType.DEFAULT_BINARY, encodedFilename);
                    bodyLogJson.put(entry.getKey(), "[文件名]" + entry.getValue().getOriginalFilename());
                }
                body = builder.build();
                bodyLog = JSONObject.toJSONString(bodyLogJson);
            } else {
                String requestData = request.getReader().lines().collect(Collectors.joining());
                body = JSONObject.parseObject(requestData);
                bodyLog = JSONObject.toJSONString(body);
            }
        } catch (IOException e) {
            logger.error("请求Nexus平台出错，请求内容格式错误：", e);
            throw new ServiceException("请求平台出错，请求内容格式错误！");
        }
        // 去除pms预置路由
        path = path.replace("/productLibrary", "");
        HashMap<String, String> headers = prepareAuthenticationHeaders();
        logger.info("[{}]通过HttpClient向Nexus发起请求，请求方法为：{},请求网站为：{},请求路由为：{},请求载体为：{},请求头为：{}"
                , requestId
                , requestMethod
                , nexusUrl
                , path
                , bodyLog
                , headers);

        JSONObject result;
        try {
            String requestUrl = EscapeUtil.removeExtraSlashOfUrl(nexusUrl + path);
            switch (requestType) {
                case POST:
                    result = httpAPIService.doPost(requestUrl, (JSONObject) body, headers);
                    break;
                case POST_FORM_DATA:
                    result = httpAPIService.doPostFormData(requestUrl, (HttpEntity) body, headers);
                    break;
                case DELETE:
                    result = httpAPIService.doDelete(requestUrl, (JSONObject) body, headers);
                    break;
                case GET:
                    result = httpAPIService.doGet(requestUrl, headers);
                    break;
                case PUT:
                    result = httpAPIService.doPut(requestUrl, (JSONObject) body, headers);
                    break;
//                case PATCH:
//                    result = httpAPIService.doPatch(requestURL, (JSONObject) object, headers);
//                    break;
                default:
                    throw new ServiceException("不支持该请求方式：" + requestType.name());
            }
            logger.info("[{}]请求响应为：{}"
                    , requestId
                    , result);
        } catch (Exception e) {
            logger.error("请求平台出现错误：", e);
            throw new ServiceException("请求平台出现错误：" + e.getMessage());
        }
        if (result == null) {
            throw new ServiceException("请求平台时，未返回内容");
        }
        if (result.size() == 1 && result.containsKey("data")) {
            return result.get("data");
        }
        return result;
    }

    protected Object doRequestToObject(NexusRequestUrl nexusRequestUrl, Object body, JSONObject bodyLog) {
        String requestId = UUID.randomUUID().toString();
        String path = nexusRequestUrl.getPath();

        RequestType requestType = nexusRequestUrl.getRequestType();

        HashMap<String, String> headers = prepareAuthenticationHeaders();
        logger.info("[{}]通过HttpClient向Nexus发起请求，请求方法为：{},请求网站为：{},请求路由为：{},请求载体为：{},请求头为：{}"
                , requestId
                , requestType
                , nexusUrl
                , path
                , bodyLog != null ? bodyLog : body
                , headers);

        JSONObject result;
        try {
            String requestUrl = EscapeUtil.removeExtraSlashOfUrl(nexusUrl + path);
            switch (requestType) {
                case POST:
                    result = httpAPIService.doPost(requestUrl, (JSONObject) body, headers);
                    break;
                case POST_FORM_DATA:
                    result = httpAPIService.doPostFormData(requestUrl, (HttpEntity) body, headers);
                    break;
                case DELETE:
                    result = httpAPIService.doDelete(requestUrl, (JSONObject) body, headers);
                    break;
                case GET:
                    result = httpAPIService.doGet(requestUrl, headers);
                    break;
                case PUT:
                    result = httpAPIService.doPut(requestUrl, (JSONObject) body, headers);
                    break;
//                case PATCH:
//                    result = httpAPIService.doPatch(requestURL, (JSONObject) object, headers);
//                    break;
                default:
                    throw new ServiceException("不支持该请求方式：" + requestType.name());
            }
            logger.info("[{}]请求响应为：{}"
                    , requestId
                    , result);
        } catch (Exception e) {
            logger.error("请求平台出现错误：", e);
            throw new ServiceException("请求平台出现错误：" + e.getMessage());
        }
        if (result == null) {
            throw new ServiceException("请求平台时，未返回内容");
        }
        return result;
    }

    private HashMap<String, String> prepareAuthenticationHeaders() {
        HashMap<String, String> headers = new HashMap<>(1);
//        Enumeration<String> headerNames = request.getHeaderNames();
//        //使用循环遍历请求头，并通过getHeader()方法获取一个指定名称的头字段
//        while (headerNames.hasMoreElements()){
//            String headerName = headerNames.nextElement();
//            headers.put(headerName, request.getHeader(headerName));
//        }
        headers.put("Authorization", nexusAuthorization);
        return headers;
    }

    public JSONObject doRequest(NexusRequestUrl nexusRequestUrl, Object body) {
        return (JSONObject) doRequestToObject(nexusRequestUrl, body, null);
    }

    public JSONObject doRequest(NexusRequestUrl nexusRequestUrl, HttpEntity body, JSONObject bodyLog) {
        return (JSONObject) doRequestToObject(nexusRequestUrl, body, bodyLog);
    }

    public JSONObject doRequest(NexusRequestUrl nexusRequestUrl) {
        return (JSONObject) doRequestToObject(nexusRequestUrl, null, null);
    }

    public <T> T doRequestToJavaObject(NexusRequestUrl nexusRequestUrl, Class<T> tClass) {
        JSONObject res = (JSONObject) doRequestToObject(nexusRequestUrl, null, null);
        if (res == null) {
            return null;
        }
        return res.toJavaObject(tClass);
    }

    public <T> T doRequestToJavaObject(NexusRequestUrl nexusRequestUrl, Type ownerClass, Type rawClass) {
        JSONObject res = (JSONObject) doRequestToObject(nexusRequestUrl, null, null);
        if (res == null) {
            return null;
        }
        return JSONObject.parseObject(res.toJSONString(), buildType(ownerClass, rawClass));
    }

    private static Type buildType(Type... types) {
        ParameterizedTypeImpl beforeType = null;
        if (types != null && types.length > 0) {
            for (int i = types.length - 1; i > 0; i--) {
                beforeType = new ParameterizedTypeImpl(new Type[]{beforeType == null ? types[i] : beforeType}, null, types[i - 1]);
            }
        }
        return beforeType;
    }

    public <T> T doRequestToJavaObject(NexusRequestUrl nexusRequestUrl, Object body, Type ownerClass, Type rawClass) {
        JSONObject res = (JSONObject) doRequestToObject(nexusRequestUrl, body, null);
        if (res == null) {
            return null;
        }
        JSONObject resultJson = res.getJSONObject("result");
        if (resultJson == null) {
            return null;
        }
        if (!resultJson.getBoolean("success")) {
            return null;
        }
        return JSONObject.parseObject(res.toJSONString(), buildType(ownerClass, rawClass));
    }

    public <T> T doRequestToJavaObjectList(NexusRequestUrl nexusRequestUrl, Object body, Type ownerClass, Type rawClass) {
        JSONObject res = (JSONObject) doRequestToObject(nexusRequestUrl, body, null);
        if (res == null) {
            return null;
        }
        return JSONObject.parseObject(res.toJSONString(), buildType(ownerClass, List.class, rawClass));
    }

    public SysFileBytes getFileBytes(String fileUrl) {
        HashMap<String, String> headers = prepareAuthenticationHeaders();
        try {
            return httpAPIService.doGetFileInputStream(fileUrl, headers);
        } catch (Exception e) {
            logger.error("从Nexus下载文件时发生异常(文件地址[{}])：{}", fileUrl, e.getMessage());
            return null;
        }
    }

    public Long getNexusTid() {
        Long tid = 1L;
        String nexusGlobalTidKey = CacheConstants.NEXUS_GLOBAL_TID_KEY;
        if (!redisService.hasKey(nexusGlobalTidKey)) {
            redisService.setCacheObject(nexusGlobalTidKey, tid);
            return tid;
        }
        tid = redisService.getCacheObject(nexusGlobalTidKey);
        if (tid <= 9999999999999999L) {
            tid = tid + 1;
        }
        redisService.setCacheObject(nexusGlobalTidKey, tid);
        return tid;
    }

    public String getNexusIp() {
        // 使用split方法以"://"分割URL，取分割后的第二个元素
        String[] parts = nexusUrl.split("://", 2);
        if (parts.length > 1) {
            // 再以":"分割，取分割后的第一个元素作为IP地址
            String[] hostAndPort = parts[1].split(":");
            return hostAndPort[0];
        }
        // 如果URL格式不正确，返回null
        return null;
    }
}
