package org.jeecg.pm.hikvision.sdk.client;

import com.chanjx.utils.HttpClientUtils;
import com.chanjx.utils.JsonUtils;
import com.chanjx.utils.entity.http.HttpResponse;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.pm.hikvision.sdk.parser.HikvisionJsonParser;
import org.jeecg.pm.hikvision.sdk.parser.HikvisionParser;
import org.jeecg.pm.hikvision.sdk.request.HikvisionAbstractRequest;
import org.jeecg.pm.hikvision.sdk.request.base.user.HikvisionGetDefaultUserUuidRequest;
import org.jeecg.pm.hikvision.sdk.response.HikvisionAbstractResponse;
import org.jeecg.pm.hikvision.sdk.response.base.user.HikvisionGetDefaultUserUuidResponse;
import org.jeecg.pm.hikvision.sdk.result.HikvisionBaseResult;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Map;

/**
 * @author: JJww
 * @Date:2022/3/21
 */
@Slf4j
public class HikvisionBaseClient extends HikvisionAbstractClient {

    /**
     * 请求的Host头，该字段值必须是：openapi/service
     */
    private final static String host = "172.16.16.2";
    private final static String protocol = "http";
    private final static String joiner = "://";
    private final String appKey;
    private final String secret;
    private final String opUserUuid;

    public HikvisionBaseClient(String appKey, String secret) {
        this.appKey = appKey;
        this.secret = secret;
        final HikvisionGetDefaultUserUuidResponse response = this.execute(new HikvisionGetDefaultUserUuidRequest());
        if (response.isSuccess) {
            opUserUuid = response.getResult().getData();
        } else {
            throw new JeecgBootException("初始化海康威视SDK异常：无法获取opUserUuid");
        }
    }

    @Override
    public <T extends HikvisionAbstractResponse<R>, R extends HikvisionBaseResult> T execute
            (HikvisionAbstractRequest<T, R> request) {
        try {
            final HttpResponse resp = doHttpClient(request);
            final T response = request.getResponseClazz().newInstance();

            if (resp.getContentType() == null) {
                return autoParser(resp, response);
            }

            final HikvisionParser<R> parser = new HikvisionJsonParser<>(request.getResultClazz());
            setResult(resp, response, parser);
            return response;
        } catch (JsonProcessingException e) {
            throw new JeecgBootException("Json转换异常！", e);
        } catch (URISyntaxException e) {
            throw new JeecgBootException("URI语法异常！", e);
        } catch (InstantiationException | IllegalAccessException e) {
            throw new JeecgBootException("创建Response异常！", e);
        }
    }

    public <T extends HikvisionAbstractResponse<R>, R extends HikvisionBaseResult> HttpResponse doHttpClient
            (HikvisionAbstractRequest<T, R> request) throws URISyntaxException, JsonProcessingException {
        request.getModel()
                .setAppKey(appKey)
                .setTime(Instant.now().toEpochMilli());
        if (!(request instanceof HikvisionGetDefaultUserUuidRequest)) {
            request.getModel().setOpUserUuid(opUserUuid);
        }
        final URIBuilder uriBuilder = new URIBuilder(
                new URI((protocol + joiner + host + request.getReqPath())));
        uriBuilder.setCharset(StandardCharsets.UTF_8);
        uriBuilder.addParameter("token", createToken(request));
        if (request.getQuery() != null) {
            final Map<String, String> queryMap = JsonUtils.obj2MapSS(request.getQuery());
            if (MapUtils.isNotEmpty(queryMap)) {
                queryMap.forEach(uriBuilder::addParameter);
            }
        }
        final HttpRequestBase req;
        switch (request.getHttpMethod()) {
            case GET:
                req = new HttpGet(uriBuilder.build());
                break;
            case POST:
                req = new HttpPost(uriBuilder.build());
                HttpClientUtils.setRowBody(
                        JsonUtils.obj2JsonNonNull(request.getModel()),
                        (HttpEntityEnclosingRequestBase) req,
                        request.getContentType());
                break;
            default:
                throw new JeecgBootException("不支持的请求类型！");
        }
        final CloseableHttpClient httpClient = HttpClientUtils.getHttpClient();
        return HttpClientUtils.send(httpClient, req, request.getHeaders());
    }

    private <T extends HikvisionAbstractResponse<R>, R extends HikvisionBaseResult> String createToken
            (HikvisionAbstractRequest<T, R> request) throws JsonProcessingException {
        final String tokenSource =
                request.getReqPath()
                        + JsonUtils.obj2JsonNonNull(request.getModel())
                        + this.secret;
        return DigestUtils.md5Hex(tokenSource).toUpperCase();
    }

    public <T extends HikvisionAbstractResponse<R>, R extends HikvisionBaseResult> T autoParser(HttpResponse resp, T response) {
        try {
            log.warn("返回头无 ContentType 值，执行自动解析，进行 Json 解析！");
            final HikvisionJsonParser<R> jsonParser = new HikvisionJsonParser<>(response.getResultClazz());
            setResult(resp, response, jsonParser);
            log.info("返回头无 ContentType 值，执行自动解析，Json 解析成功！");
            return response;
        } catch (JeecgBootException e) {
            log.error("返回头无 ContentType 值，自动进行 Json 解析，解析失败：{}", e.getMessage());
        }

        throw new JeecgBootException("返回头无 ContentType 值，自动解析全部失败！");
    }

    public <T extends HikvisionAbstractResponse<R>, R extends HikvisionBaseResult> void setResult(HttpResponse resp, T response, HikvisionParser<R> parser) {
        final R result = parser.parse(new String(resp.getByteBody()));
        response.setSuccess(result.isSuccess());
        response.setResult(result);
    }
}
