package com.microservices.common.httpClient.util;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.constant.Constants;
import com.microservices.common.core.constant.HttpStatus;
import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.html.EscapeUtil;
import com.microservices.common.httpClient.constant.GitLinkConstants;
import com.microservices.common.httpClient.domain.FileEntryDto;
import com.microservices.common.httpClient.domain.GitLinkRequestUrl;
import com.microservices.common.httpClient.service.HttpAPIService;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.domain.SysUser;
import com.microservices.system.api.model.LoginUser;
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 javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import static com.microservices.common.httpClient.domain.GitLinkRequestUrl.GET_FILE_SUB_ENTRIES;
import static com.microservices.common.httpClient.util.GitLinkRequestHelper.RequestType.GET;

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

    private static final Logger logger = LoggerFactory.getLogger(GitLinkRequestHelper.class);
    @Autowired
    private HttpAPIService httpAPIService;

    @Value("${http.gitLinkUrl}")
    public String gitLinkUrl;

    @Value("${gitlink.admin_user.username}")
    public String adminUsername;

    @Value("${gitlink.admin_user.password}")
    public String adminPassword;

    @Value("${gitlink.admin_user.client_id}")
    public String adminClientId;

    @Value("${gitlink.admin_user.client_secret}")
    public String adminClientSecret;

    enum RequestType {
        POST,
        POST_FILE,
        GET,
        DELETE,
        PUT,
        PATCH
    }

    /**
     * 带参数的请求
     *
     * @param gitLinkRequestUrl gitlink请求地址
     * @param object body参数
     * @param requestType 请求类型
     * @param useGitlinkErrorMsg 是否直接使用Gitlink错误信息
     * @return 响应对象
     */
    private JSONObject doRequest(GitLinkRequestUrl gitLinkRequestUrl, Object object, RequestType requestType, Boolean useGitlinkErrorMsg) {

        HashMap<String, String> headers = prepareAuthenticationHeaders(gitLinkRequestUrl);
        String requestId = UUID.randomUUID().toString();
        String path = gitLinkRequestUrl.getPath();
        logger.info("[{}]通过HttpClient向平台发起请求，请求方法为：{},请求网站为：{},请求路由为：{},请求载体为：{},请求头为：{}"
                , requestId
                , requestType
                , gitLinkUrl
                , path
                , object
                , headers);
        JSONObject result;
        try {
            String requestURL = EscapeUtil.removeExtraSlashOfUrl(gitLinkUrl + path.replace(" ", "%20"));
            switch (requestType) {
                case POST:
                    result = httpAPIService.doPost(requestURL, (JSONObject) object, headers);
                    break;
                case POST_FILE:
                    result = httpAPIService.doPostFile(requestURL, (MultipartFile) object, headers);
                    break;
                case DELETE:
                    result = httpAPIService.doDelete(requestURL, (JSONObject) object, headers);
                    break;
                case GET:
                    result = httpAPIService.doGet(requestURL, headers);
                    break;
                case PUT:
                    result = httpAPIService.doPut(requestURL, (JSONObject) object, 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) {
            throw new ServiceException("请求平台出现错误：" + e.getMessage());
        }
        if (result == null) {
            throw new ServiceException("请求平台时，未返回内容");
        }
        if (result.containsKey(GitLinkConstants.STATUS_KEY)) {
            Object statusObject = result.getObject(GitLinkConstants.STATUS_KEY, Object.class);
            if ( statusObject instanceof Integer
                    && ((Integer) statusObject < 0 || (Integer) statusObject > Constants.SUCCESS)) {
                String msg = "";
                if (result.containsKey(GitLinkConstants.MESSAGE_KEY)) {
                    msg = result.getString(GitLinkConstants.MESSAGE_KEY);
                }
                if (!useGitlinkErrorMsg) {
                    msg = String.format("请求失败(%s)", msg);
                }
                if ((Integer) statusObject == HttpStatus.NOT_FOUND) {
                    throw new ServiceException(HttpStatus.NOT_FOUND, msg);
                }
                logger.error("Gitlink接口调用失败:{}，响应内容为：{}", msg, result);
                throw new ServiceException(msg);
            }
        }
        return result;
    }

    public JSONObject doPost(GitLinkRequestUrl path, JSONObject jsonObject) {
        return doRequest(path, jsonObject, RequestType.POST, false);
    }

    public JSONObject doPost(GitLinkRequestUrl path, JSONObject jsonObject, Boolean useGitlinkErrorMsg) {
        return doRequest(path, jsonObject, RequestType.POST, useGitlinkErrorMsg);
    }

    public <T> JSONObject doPost(GitLinkRequestUrl path, T t) {
        return doRequest(path, JSONObject.parseObject(JSON.toJSONString(t)), RequestType.POST, false);
    }

    public JSONObject doPostFile(GitLinkRequestUrl path, MultipartFile file) {
        return doRequest(path, file, RequestType.POST_FILE, false);
    }

    public JSONObject doPut(GitLinkRequestUrl path, JSONObject jsonObject) {
        return doRequest(path, jsonObject, RequestType.PUT, false);
    }

    public JSONObject doPatch(GitLinkRequestUrl path, JSONObject jsonObject) {
        return doRequest(path, jsonObject, RequestType.PATCH, false);
    }

    public <T> JSONObject doPut(GitLinkRequestUrl path, T t) {
        return doRequest(path, JSONObject.parseObject(JSON.toJSONString(t)), RequestType.PUT, false);
    }

    public JSONObject doDelete(GitLinkRequestUrl path) {
        return doRequest(path, null, RequestType.DELETE, false);
    }

    public JSONObject doDelete(GitLinkRequestUrl path, JSONObject body) {
        return doRequest(path, body, RequestType.DELETE, false);
    }

    public JSONObject doGet(GitLinkRequestUrl path) {
        return doRequest(path, null, GET, false);
    }

    public <T> T doGet(GitLinkRequestUrl path, Class<T> tClass) {
        JSONObject result = doRequest(path, null, GET, false);
        return result.toJavaObject(tClass);
    }

    public <T> List<T> doGetToObjectList(GitLinkRequestUrl path, Class<T> tClass) {
        if (StringUtils.isEmpty(path.getResultKey())) {
            throw new ServiceException("请求GitLink时，未设置GitLink结果键值(请求路径[" + path.getPath() + "])");
        }
        JSONObject result = doRequest(path, null, GET, false);
        if (!result.containsKey(path.getResultKey())) {
            throw new ServiceException("GitLink查询结果中不存在该键值(结果键值[" + path.getResultKey() + "])");
        }
        return result.getJSONArray(path.getResultKey()).toJavaList(tClass);
    }

    /**
     * 循环分页获取Gitlink列表接口所有数据
     *
     * @return 所有数据列表
     */
    public JSONArray getAllDataByPage(String url, String listKey) {
        JSONArray jsonArray = new JSONArray();
        boolean isAllData = false;
        int pageNum = 1;
        // 循环分页获取gitlink组织下所有成员
        while (!isAllData) {
            JSONObject result = doGet(GitLinkRequestUrl.GET_LIST_BY_PAGES(url, pageNum, 50)
            );
            jsonArray.addAll(result.getJSONArray(listKey));
            long total = result.getLong("total_count");
            if (total <= jsonArray.size()) {
                isAllData = true;
            } else {
                pageNum++;
            }
        }
        return jsonArray;
    }

    /**
     * 循环分页获取Gitlink列表接口所有数据
     *
     * @return 所有数据列表
     */
    public <T> List<T> getAllDataByPage(String url, String listKey, Class<T> tClass) {
        List<T> allList = new ArrayList<>();
        boolean isAllData = false;
        int pageNum = 1;
        // 循环分页获取gitlink组织下所有成员
        while (!isAllData) {
            JSONObject result = doGet(GitLinkRequestUrl.GET_LIST_BY_PAGES(url, pageNum, 50)
            );
            allList.addAll(result.getList(listKey, tClass));
            long total = result.getLong("total_count");
            if (total <= allList.size()) {
                isAllData = true;
            } else {
                pageNum++;
            }
        }
        return allList;
    }

    public FileEntryDto getFileEntryDto(String repoOwner, String repoIdentifier, String filePath, String branchName) {
        JSONObject result = doGet(
                GET_FILE_SUB_ENTRIES(
                        repoOwner,
                        repoIdentifier,
                        filePath,
                        branchName));
        String Entries_Key = "entries";
        if (!result.containsKey(Entries_Key)) {
            throw new ServiceException("获取GitLink项目中的文件失败(项目标识[" + repoIdentifier + "])");
        }
        return result.toJavaObject(FileEntryDto.class);
    }

    public void doGetResponse(GitLinkRequestUrl gitLinkRequestUrl, HttpServletResponse resp) throws Exception {
        HashMap<String, String> headers = prepareAuthenticationHeaders(gitLinkRequestUrl);

        String requestId = UUID.randomUUID().toString();
        String path = gitLinkRequestUrl.getPath();
        logger.info("[{}]通过HttpClient发起请求，请求方法为：{},请求网站为：{},请求路由为：{},请求载体为：{},请求头为：{}"
                , requestId
                , GET
                , gitLinkUrl
                , path
                , null
                , headers);
        String requestURL = EscapeUtil.removeExtraSlashOfUrl(gitLinkUrl + path.replace(" ", "%20"));
        httpAPIService.doGetResponse(requestURL, resp, headers);
    }

    private HashMap<String, String> prepareAuthenticationHeaders(GitLinkRequestUrl gitLinkRequestUrl) {
        HashMap<String, String> headers = new HashMap<>(1);
        if (gitLinkRequestUrl.getIsAdmin()) {
            JSONObject loginJson = new JSONObject();
            loginJson.put("grant_type", "password");
            loginJson.put("username", adminUsername);
            loginJson.put("password", adminPassword);
            loginJson.put("client_id", adminClientId);
            loginJson.put("client_secret", adminClientSecret);
            JSONObject loginRes = doPost(GitLinkRequestUrl.GET_TOKEN(), loginJson);
            if (loginRes.containsKey("access_token") && loginRes.containsKey("token_type")) {
                headers.put("Authorization", String.format("%s %s", loginRes.getString("token_type"), loginRes.getString("access_token")));
            } else {
                throw new ServiceException("平台特权账户已失效，请联系管理员");
            }
        } else if (!gitLinkRequestUrl.getIsOpen()) {
            LoginUser loginUser = SecurityUtils.getLoginUser();
            if (loginUser != null && loginUser.getSysUser() != null) {
                SysUser sysUser = loginUser.getSysUser();
                if (sysUser.getGitlinkUserId() == null) {
                    throw new ServiceException("当前用户未与平台账号绑定，无法进行平台相关操作(用户名[" + sysUser.getUserName() + "])");
                }
                String cookie = loginUser.getCookie();
                if (StringUtils.isNotEmpty(cookie)) {
                    headers.put("Cookie", cookie);
                } else {
                    throw new ServiceException("用户账号非平台账号，无法进行相关操作(用户名[" + loginUser.getUsername() + "])");
                }
            } else {
                throw new ServiceException("用户未登录！");
            }
        }
        return headers;
    }
}
