package org.tio.sitexxx.service.service.jpush.huawei.messaging;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.tio.sitexxx.service.service.jpush.huawei.exception.HuaweiMesssagingException;
import org.tio.sitexxx.service.service.jpush.huawei.message.Message;
import org.tio.sitexxx.service.service.jpush.huawei.message.TopicMessage;
import org.tio.sitexxx.service.service.jpush.huawei.model.TopicOperation;
import org.tio.sitexxx.service.service.jpush.huawei.response.SendResponse;
import org.tio.sitexxx.service.service.jpush.huawei.response.TopicListResponse;
import org.tio.sitexxx.service.service.jpush.huawei.response.TopicSendResponse;
import org.tio.sitexxx.service.service.jpush.huawei.util.ValidatorUtils;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * <pre>
 *    @author : vagrancy
 *    e-mail : 18050829067@163.com
 *    time   : 2025-02-21 10:55
 *    desc   :
 * </pre>
 */
public class HuaweiMessageClientImpl implements HuaweiMessageClient {
    private static final String PUSH_URL = "https://push-api.cloud.huawei.com";

    private final String HcmPushUrl;
    private String hcmTopicUrl;
    private String hcmGroupUrl;
    private String hcmTokenUrl;
    private final CloseableHttpClient httpClient;

    private HuaweiMessageClientImpl(Builder builder) {
        this.HcmPushUrl = MessageFormat.format(PUSH_URL + "/v1/{0}/messages:send", builder.appId);
        this.hcmTopicUrl = MessageFormat.format(PUSH_URL + "/v1/{0}/topic:{1}", builder.appId);

        ValidatorUtils.checkArgument(builder.httpClient != null, "requestFactory must not be null");
        this.httpClient = builder.httpClient;
    }

    /**
     * getter
     */
    public String getHcmSendUrl() {
        return HcmPushUrl;
    }

    public CloseableHttpClient getHttpClient() {
        return httpClient;
    }

    @Override
    public SendResponse send(Message message, boolean validateOnly, String accessToken) throws HuaweiMesssagingException {
        try {
            return sendRequest(message, validateOnly, accessToken);
        } catch (IOException e) {
            throw new HuaweiMesssagingException(HuaweiMessaging.INTERNAL_ERROR, "Error while calling HCM backend service", e);
        }
    }

    @Override
    public SendResponse send(TopicMessage message, String operation, String accessToken) throws HuaweiMesssagingException {
        try {
            return sendRequest(message, operation, accessToken);
        } catch (IOException e) {
            throw new HuaweiMesssagingException(HuaweiMessaging.INTERNAL_ERROR, "Error while calling HCM backend service", e);
        }
    }

    private SendResponse sendRequest(TopicMessage message, String operation, String accessToken) throws IOException, HuaweiMesssagingException {
        this.hcmTopicUrl = MessageFormat.format(hcmTopicUrl, "", operation);
        HttpPost httpPost = new HttpPost(this.hcmTopicUrl);
        StringEntity entity = new StringEntity(JSON.toJSONString(message), "UTF-8");
        httpPost.setHeader("Authorization", "Bearer " + accessToken);
        httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
        httpPost.setEntity(entity);
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String rpsContent = EntityUtils.toString(response.getEntity());
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            JSONObject jsonObject = JSONObject.parseObject(rpsContent);
            String code = jsonObject.getString("code");
            String msg = jsonObject.getString("msg");
            String requestId = jsonObject.getString("requestId");
            if (StringUtils.equals(code, "80000000")) {
                SendResponse sendResponse;
                if (StringUtils.equals(operation, TopicOperation.LIST.getValue())) {
                    JSONArray topics = jsonObject.getJSONArray("topics");
                    sendResponse = TopicListResponse.fromCode(code, msg, requestId, topics);
                } else {
                    Integer failureCount = jsonObject.getInteger("failureCount");
                    Integer successCount = jsonObject.getInteger("successCount");
                    JSONArray errors = jsonObject.getJSONArray("errors");
                    sendResponse = TopicSendResponse.fromCode(code, msg, requestId, failureCount, successCount, errors);
                }
                return sendResponse;
            } else {
                String errorMsg = MessageFormat.format("error code : {0}, error message : {1}", String.valueOf(code), msg);
                throw new HuaweiMesssagingException(HuaweiMessaging.KNOWN_ERROR, errorMsg);
            }
        }
        HttpResponseException exception = new HttpResponseException(statusCode, rpsContent);
        throw createExceptionFromResponse(exception);
    }

    /**
     * send request
     *
     * @param message     message {@link Message}
     * @param validateOnly A boolean indicating whether to send message for test or not.
     * @param accessToken  A String for oauth
     * @return {@link SendResponse}
     * @throws IOException If a error occurs when sending request
     */
    private SendResponse sendRequest(Message message, boolean validateOnly, String accessToken) throws IOException, HuaweiMesssagingException {
        Map<String, Object> map = createRequestMap(message, validateOnly);
        HttpPost httpPost = new HttpPost(this.HcmPushUrl);
        StringEntity entity = new StringEntity(JSON.toJSONString(map), "UTF-8");
//        String aqa = JSON.toJSONString(map);
        httpPost.setHeader("Authorization", "Bearer " + accessToken);
        httpPost.setHeader("Content-Type", "application/json;charset=utf-8");
        httpPost.setEntity(entity);
        CloseableHttpResponse response = httpClient.execute(httpPost);
        String rpsContent = EntityUtils.toString(response.getEntity());
        int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode == 200) {
            JSONObject jsonObject = JSONObject.parseObject(rpsContent);
            String code = jsonObject.getString("code");
            String msg = jsonObject.getString("msg");
            String requestId = jsonObject.getString("requestId");
            if (StringUtils.equals(code, "80000000")) {
                return SendResponse.fromCode(code, msg, requestId);
            } else {
                String errorMsg = MessageFormat.format("error code : {0}, error message : {1}", String.valueOf(code), msg);
                throw new HuaweiMesssagingException(HuaweiMessaging.KNOWN_ERROR, errorMsg);
            }
        }
        HttpResponseException exception = new HttpResponseException(statusCode, rpsContent);
        throw createExceptionFromResponse(exception);
    }

    /**
     * create the map of the request body, mostly for wrapping the message with validate_only
     *
     * @param message      A non-null {@link Message} to be sent.
     * @param validateOnly A boolean indicating whether to send message for test or not.
     * @return a map of request
     */
    private Map<String, Object> createRequestMap(Message message, boolean validateOnly) {
        return new HashMap<String, Object>() {
            {
                put("validate_only", validateOnly);
                put("message", message);
            }
        };
    }

    private HuaweiMesssagingException createExceptionFromResponse(HttpResponseException e) {
        String msg = MessageFormat.format("Unexpected HTTP response with status : {0}, body : {1}", e.getStatusCode(), e.getMessage());
        return new HuaweiMesssagingException(HuaweiMessaging.UNKNOWN_ERROR, msg, e);
    }

    static HuaweiMessageClientImpl fromApp(HuaweiApp app) {
        String appId = ImplHuaweiTrampolines.getAppId(app);
        return HuaweiMessageClientImpl.builder()
                .setAppId(appId)
                .setHttpClient(app.getOption().getHttpClient())
                .build();
    }

    static Builder builder() {
        return new Builder();
    }

    static final class Builder {

        private String appId;
        private CloseableHttpClient httpClient;

        private Builder() {
        }

        public Builder setAppId(String appId) {
            this.appId = appId;
            return this;
        }

        public Builder setHttpClient(CloseableHttpClient httpClient) {
            this.httpClient = httpClient;
            return this;
        }

        public HuaweiMessageClientImpl build() {
            return new HuaweiMessageClientImpl(this);
        }
    }

}
