package com.sonic.common.rpc;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.sonic.common.context.HttpRequestContext;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * Rpc调用客户端接口
 * @author code
 */
public interface RpcClient {
    <T> T execute(HttpClient.HttpMethod httpMethod, String url, RequestParams requestParams, Function<String, Result<T>> converter);

    <R> R execute(HttpClient.HttpMethod httpMethod, String url, RequestParams requestParams, BiFunction<byte[], Map<String, List<String>>, R> responder);


    /** post 相关方法, 默认是使用 body */
    default <T> T post(String url, TypeReference<Result<T>> typeReference, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.POST, url, requestParams, s -> JSONObject.parseObject(s, typeReference));
    }

    default <T> T post(String url, BiFunction<byte[], Map<String, List<String>>, T> responder, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.POST, url, requestParams, responder);
    }

    @Deprecated
    /**header头参数签名方式提交请求*/
    default <T> T postFormSign(String url, BiFunction<byte[], Map<String, List<String>>, T> responder, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.POST, url, requestParams, responder);
    }

    default <T> T post(String url, Object content, TypeReference<Result<T>> typeReference) {
        Preconditions.checkArgument(!(RequestParams.class.isAssignableFrom(content.getClass())));
        Preconditions.checkArgument(!(TypeReference.class.isAssignableFrom(content.getClass())));

        RequestParams.BodyParams requestParams = RequestParams.BodyParams.builder().content(content).logEnabled(true).build();
        return post(url, typeReference, requestParams);
    }

    @Deprecated
    /**header头参数签名方式提交请求*/
    default <T> T postBodySign(String url, Object content, TypeReference<Result<T>> typeReference) {
        Preconditions.checkArgument(!(RequestParams.class.isAssignableFrom(content.getClass())));
        Preconditions.checkArgument(!(TypeReference.class.isAssignableFrom(content.getClass())));
        RequestParams.BodyParams requestParams = RequestParams.BodyParams.builder().content(content).logEnabled(true).build();
        return post(url, typeReference, requestParams);
    }

    default <T> T postBody(String url, Object content, TypeReference<Result<T>> typeReference) {
        Preconditions.checkArgument(!(RequestParams.class.isAssignableFrom(content.getClass())));
        Preconditions.checkArgument(!(TypeReference.class.isAssignableFrom(content.getClass())));
        RequestParams.BodyParams requestParams = RequestParams.BodyParams.builder().content(content).logEnabled(true).build();
        return post(url, typeReference, requestParams);
    }

    @Deprecated
    /**header头参数签名方式提交请求*/
    default <T> T postFormSign(String url, Map<String, ?> params, TypeReference<Result<T>> typeReference) {
        RequestParams.FormParams requestParams = RequestParams.FormParams.builder().params(params).logEnable(true).build();
        return post(url, typeReference, requestParams);
    }

    default <T> T postForm(String url, Map<String, ?> params, TypeReference<Result<T>> typeReference) {
        RequestParams.FormParams requestParams = RequestParams.FormParams.builder().params(params).logEnable(true).build();
        return post(url, typeReference, requestParams);
    }

    @Deprecated
    default <T> T post(String url, Object content, Class<T> clz) {
        return post(url, content, new TypeReference<Result<T>>(clz) {
        });
    }

    @Deprecated
    default <T> List<T> postAndGetList(String url, Object content, Class<T> clz) {
        return post(url, content, new TypeReference<Result<List<T>>>(clz) {
        });
    }


    /**
     * 直接从一个分页查询的服务的接口返回page.getData这个列表;
     * 注：该接口直接返回第一页数据，最大1000条
     *
     * @param url
     * @param content
     * @param clz
     * @param <T>
     * @return
     */
    @Deprecated
    default <T> List<T> postAndGetListFromPage(String url, Object content, Class<T> clz) {
        Page<T> page = Page.<T>builder().pn(1).ps(Page.MAX_PER_PAGE_COUNT).build();
        return Optional.ofNullable(post(page.appendQueryStringToUrl(url), content, new TypeReference<Result<Page<T>>>(clz) {})
                .getDatas())
                .orElse(Collections.emptyList());
    }

    @Deprecated
    default <T> Page<T> postAndGetPage(String url, Object content, Class<T> clz) {
        Preconditions.checkArgument(!(Page.class.isAssignableFrom(content.getClass())));
        return post(url, content, new TypeReference<Result<Page<T>>>(clz) {
        });
    }

    @Deprecated
    default <T> Page<T> postAndGetPage(String path, Page page, Object content, Class<T> clz) {
        return postAndGetPage(page.appendQueryStringToUrl(path), content, clz);
    }

    /* get 相关方法 */
    default <T> T get(String url, TypeReference<Result<T>> typeReference, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.GET, url, requestParams, s -> JSONObject.parseObject(s, typeReference));
    }

    default <R> R get(String url, BiFunction<byte[], Map<String, List<String>>, R> responder, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.GET, url, requestParams, responder);
    }

    default <T> T get(String url, Map params, TypeReference<Result<T>> typeReference) {
        RequestParams.FormParams requestParams = RequestParams.FormParams.builder().params(params).logEnable(true).build();
        return get(url, typeReference, requestParams);
    }

    @Deprecated
    default <T> T get(String url, Map params, Class<T> clz) {
        return get(url, params, new TypeReference<Result<T>>(clz) {
        });
    }

    /* put 相关方法 */
    default <T> T put(String url, TypeReference<Result<T>> typeReference, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.PUT, url, requestParams, s -> JSONObject.parseObject(s, typeReference));
    }

    default <T> T put(String url, BiFunction<byte[], Map<String, List<String>>, T> responder, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.PUT, url, requestParams, responder);
    }

    default <T> T put(String url, Object content, TypeReference<Result<T>> typeReference) {
        Preconditions.checkArgument(!(RequestParams.class.isAssignableFrom(content.getClass())));
        Preconditions.checkArgument(!(TypeReference.class.isAssignableFrom(content.getClass())));

        RequestParams.BodyParams requestParams = RequestParams.BodyParams.builder().content(content).logEnabled(true).build();
        return put(url, typeReference, requestParams);
    }

    /* delete 相关方法*/
    default <T> T delete(String url, TypeReference<Result<T>> typeReference, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.DELETE, url, requestParams, s -> JSONObject.parseObject(s, typeReference));
    }

    default <T> T delete(String url, BiFunction<byte[], Map<String, List<String>>, T> responder, RequestParams requestParams) {
        return execute(HttpClient.HttpMethod.DELETE, url, requestParams, responder);
    }

    default <T> T delete(String url, Map params, TypeReference<Result<T>> typeReference) {
        RequestParams.FormParams requestParams = RequestParams.FormParams.builder().params(params).logEnable(true).build();
        return delete(url, typeReference, requestParams);
    }

    default <T> Result<T> convert(String body, Class<T> clz) {
        return JSONObject.parseObject(body, new TypeReference<Result<T>>(clz) {
        });
    }

    /** 将 xg-开头的header复制到请求的下一跳 */
    String XG_HEADER_PREFIX = "xg-";

    List<Supplier<Map<String, String>>> DEFAULT_HEADER_SUPPLIERS = ImmutableList.of(() -> HttpRequestContext.get()
            .map(request -> Collections.list(request.getHeaderNames()).stream()
                    .filter(p -> p.startsWith(XG_HEADER_PREFIX))
                    .collect(Collectors.toMap(e -> e, e -> request.getHeader(e), (oldValue, newValue) -> newValue)))
            .orElse(Collections.emptyMap())
    );

    /**
     * 使用builder模式来发起请求, 先通过request()获得builder对象. 再构建具体的请求方式
     * eg: String resp = rpcClient.request().url("/my").content(Map.of("key", "value")).clz(String.class).post();
     *
     * @return
     */
    default RpcRequestBuilder request() {
        return new RpcRequestBuilder(this);
    }

    class RpcRequestBuilder {
        private String url;
        private Object content;
        private Class clz;
        private Page page;
        private RpcClient rpcClient;

        public RpcRequestBuilder(RpcClient rpcClient) {
            this.rpcClient = rpcClient;
        }

        public <R> R post() {
            checkArgument();
            Preconditions.checkArgument(content != null, "content不能为空");
            return (R) rpcClient.post(getUrl(), content, clz);
        }

        public <R> List<R> postAndGetList() {
            Preconditions.checkArgument(content != null, "content不能为空");
            checkArgument();
            return rpcClient.postAndGetList(getUrl(), content, clz);
        }

        public <R> List<R> postAndGetListFromPage() {
            Preconditions.checkArgument(content != null, "content不能为空");
            checkArgument();
            return rpcClient.postAndGetListFromPage(getUrl(), content, clz);
        }

        public <R> Page<R> postAndGetPage() {
            Preconditions.checkArgument(content != null, "content不能为空");
            checkArgument();
            return rpcClient.postAndGetPage(getUrl(), content, clz);
        }

        public <R> R put() {
            Preconditions.checkArgument(content != null, "content不能为空");
            checkArgument();
            return rpcClient.put(getUrl(), content, new TypeReference<Result<R>>(clz) {
            });
        }

        public <R> R get(Map params) {
            Preconditions.checkArgument(content == null, "content不会生效");
            checkArgument();
            return (R) rpcClient.get(getUrl(), params, clz);
        }

        public <R> R delete(Map params) {
            Preconditions.checkArgument(content == null, "content不会生效");
            checkArgument();
            return rpcClient.delete(getUrl(), params, new TypeReference<Result<R>>(clz) {
            });
        }

        private String getUrl() {
            return page != null ? page.appendQueryStringToUrl(url) : url;
        }

        private void checkArgument() {
            Preconditions.checkArgument(!Strings.isNullOrEmpty(url), "url不能为空");
            Preconditions.checkArgument(rpcClient != null, "rpcClient不能为空");
            Preconditions.checkArgument(clz != null, "clz不能为空");
            if (content != null) {
                Preconditions.checkArgument(!(RequestParams.class.isAssignableFrom(content.getClass())));
                Preconditions.checkArgument(!(TypeReference.class.isAssignableFrom(content.getClass())));
                Preconditions.checkArgument(!(Page.class.isAssignableFrom(content.getClass())));
            }
        }

        public RpcRequestBuilder url(String url) {
            this.url = url;
            return this;
        }

        public RpcRequestBuilder content(Object content) {
            this.content = content;
            return this;
        }

        public RpcRequestBuilder clz(Class clz) {
            this.clz = clz;
            return this;
        }

        public RpcRequestBuilder page(Page page) {
            this.page = page;
            return this;
        }
    }
}
