package top.octopusyan.http;

import okhttp3.*;
import top.octopusyan.http.annotation.HttpHeader;
import top.octopusyan.http.annotation.HttpIgnore;
import top.octopusyan.http.annotation.HttpRename;
import top.octopusyan.http.api.NotParamApi;
import top.octopusyan.http.api.ParamApi;
import top.octopusyan.http.api.PathParamApi;
import top.octopusyan.http.api.RequestApi;
import top.octopusyan.http.callback.NormalCallback;
import top.octopusyan.http.config.BodyType;
import top.octopusyan.http.config.HttpConstant;
import top.octopusyan.http.listener.OnHttpListener;
import top.octopusyan.http.model.*;
import top.octopusyan.http.request.IRequestHandler;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @author : octopus yan
 * @email : octopus_yan@foxmail.com
 * @description : http 请求工具
 * @create : 2022-4-1 15:14
 */
public class EasyHttp<Param, Result> {

    /**
     * 请求处理策略
     */
    private final IRequestHandler mHandler = HttpConfig.getInstance().getHandler();

    /**
     * 请求执行代理类
     */
    private CallProxy mCallProxy;

    /**
     * 请求标记
     */
    private String mTag;

    /**
     * 请求延迟
     */
    private long mDelayMillis;

    /**
     * 请求服务地址
     */
    private final String server = HttpConfig.getInstance().getServerPath();

    /**
     * 请求接口
     */
    private RequestApi<Param, Result> mRequestApi;

    /**
     * 请求参数
     */
    private Param param;

    public static class Builder {
        private String method;
        private String apiPath;
        private BodyType bodyType;

        public <Param, Result> ParamRequest<Param, Result> api(ParamApi<Param, Result> api) {
            return new ParamRequest<Param, Result>().api(api);
        }

        public <Result> UrlRequest<Result> api(NotParamApi<Result> api) {
            return new UrlRequest<Result>().api(api);
        }

        public <Result> RestfulRequest<Result> api(PathParamApi<Result> api) {
            return new RestfulRequest<Result>().api(api);
        }

        public <Param, Result> EasyHttp<Param, Result> api(RequestApi<Param, Result> api) {
            return new EasyHttp<Param, Result>().api(api);
        }

        /**
         * 无参数请求
         */
        public <Result> UrlRequest<Result> build(ResponseClass<Result> result) {
            if (apiPath == null) throw new IllegalArgumentException("请求接口地址为空！");

            NotParamApi<Result> requestApi = new NotParamApi<>(apiPath, method, bodyType);

            return new UrlRequest<Result>().api(requestApi);
        }

        /**
         * 带参请求
         *
         * @param param 请求参数
         */
        public <Param, Result> ParamRequest<Param, Result> build(Param param, ResponseClass<Result> result) {
            ParamApi<Param, Result> requestApi = new ParamApi<>(apiPath, method, bodyType);
            return new ParamRequest<Param, Result>().api(requestApi).param(param);
        }

        public Builder setMethod(String method) {
            this.method = method;
            return this;
        }

        public Builder setApiPath(String apiPath) {
            this.apiPath = apiPath;
            return this;
        }

        public Builder setBodyType(BodyType bodyType) {
            this.bodyType = bodyType;
            return this;
        }
    }

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

    protected EasyHttp<Param, Result> api(RequestApi<Param, Result> api) {
        this.mRequestApi = api;
        return this;
    }

    protected EasyHttp<Param, Result> param(Param param) {
        this.param = param;
        return this;
    }

    private RequestApi<Param, Result> getRequestApi() {
        return mRequestApi;
    }

    private IRequestHandler getRequestHandler() {
        return mHandler;
    }

    protected void path(String path) {
        this.mRequestApi.setApi(path);
    }

    protected String getPath() {
        return this.mRequestApi.getApi();
    }

    public String getTag() {
        return mTag;
    }

    public void setTag(String mTag) {
        this.mTag = mTag;
    }

    public long getDelayMillis() {
        return mDelayMillis;
    }

    public void setDelayMillis(long mDelayMillis) {
        this.mDelayMillis = mDelayMillis;
    }

    /**
     * 执行异步请求
     */
    public void request(OnHttpListener<Result> listener) {
        if (mDelayMillis > 0) {
            // 打印请求延迟时间
            HttpLog.print("RequestDelay", String.valueOf(mDelayMillis));
        }

        StackTraceElement[] stackTrace = new Throwable().getStackTrace();
        EasyUtils.postDelayed(() -> {
            HttpLog.print(stackTrace);
            mCallProxy = new CallProxy(createCall());
            mCallProxy.enqueue(new NormalCallback(mCallProxy, mRequestApi, mHandler, listener));

        }, mDelayMillis);
    }

    /**
     * 执行同步请求
     *
     * @param responseClass 需要解析泛型的对象
     * @return 返回解析完成的对象
     * @throws Exception 如果请求失败或者解析失败则抛出异常
     */
    public Result execute(ResponseClass<Result> responseClass) throws Exception {
        if (mDelayMillis > 0) {
            // 打印请求延迟时间
            HttpLog.print("RequestDelay", String.valueOf(mDelayMillis));
            Thread.sleep(mDelayMillis);
        }

        HttpLog.print(new Throwable().getStackTrace());
        try {
            mCallProxy = new CallProxy(createCall());
            Response response = mCallProxy.execute();
            return mHandler.requestSucceed(getRequestApi(), response, mHandler.getType(responseClass));
        } catch (Exception e) {
            throw mHandler.requestFail(getRequestApi(), e);
        }
    }

    /**
     * 创建请求回调
     */
    private Call createCall() {
        String url = server + mRequestApi.getApi();
        HttpParams params = new HttpParams();
        HttpHeaders headers = new HttpHeaders();

        BodyType type = mRequestApi.getBodyType();

        if (param != null) setParam(params, headers, type);

        return HttpConfig.getInstance().getClient().newCall(createRequest(url, mTag, params, headers, type));
    }

    private void setParam(HttpParams params, HttpHeaders headers, BodyType type) {

        List<Field> fields = new ArrayList<>();

        Class<?> clazz = param.getClass();
        do {
            Field[] declaredFields = clazz.getDeclaredFields();
            fields.addAll(0, Arrays.asList(declaredFields));
            // 遍历获取父类的字段
            clazz = clazz.getSuperclass();
        } while (clazz != null && !Object.class.equals(clazz));

        // 当前请求是否存在流参数
        params.setMultipart(EasyUtils.isMultipart(fields));

        // 如果参数中包含流参数并且当前请求方式不是表单的话
        if (params.isMultipart() && type != BodyType.FORM) {
            // 就强制设置成以表单形式提交参数
            type = BodyType.FORM;
        }

        for (Field field : fields) {
            // 允许访问私有字段
            field.setAccessible(true);

            try {
                // 获取字段的对象
                Object value = field.get(param);

                // 获取字段的名称
                String key;
                HttpRename annotation = field.getAnnotation(HttpRename.class);
                if (annotation != null) {
                    key = annotation.value();
                } else {
                    key = field.getName();
                    // 如果是内部类则会出现一个字段名为 this$0 的外部类对象，会导致无限递归，这里要忽略掉，如果使用静态内部类则不会出现这个问题
                    // 和规避 Kotlin 自动生成的伴生对象：https://github.com/getActivity/EasyHttp/issues/15
                    if (key.matches("this\\$\\d+") || "Companion".equals(key)) {
                        continue;
                    }
                }

                // 如果这个字段需要忽略，则进行忽略
                if (field.isAnnotationPresent(HttpIgnore.class)) {
                    if (field.isAnnotationPresent(HttpHeader.class)) {
                        headers.remove(key);
                    } else {
                        params.remove(key);
                    }
                    continue;
                }

                // 前提是这个字段值不能为空（基本数据类型有默认的值，而对象默认的值为 null）
                if (EasyUtils.isEmpty(value)) {
                    // 遍历下一个字段
                    continue;
                }

                // 如果这是一个请求头参数
                if (field.isAnnotationPresent(HttpHeader.class)) {
                    if (value instanceof Map) {
                        Map<?, ?> map = ((Map<?, ?>) value);
                        for (Object o : map.keySet()) {
                            if (o != null && map.get(o) != null) {
                                headers.put(String.valueOf(o), String.valueOf(map.get(o)));
                            }
                        }
                    } else {
                        headers.put(key, String.valueOf(value));
                    }
                    continue;
                }

                // 否则这就是一个普通的参数
                switch (type) {
                    case FORM:
                        if (value instanceof Map) {
                            Map<?, ?> map = ((Map<?, ?>) value);
                            for (Object o : map.keySet()) {
                                if (o != null && map.get(o) != null) {
                                    params.put(String.valueOf(o), map.get(o));
                                }
                            }
                        } else {
                            params.put(key, value);
                        }
                        break;
                    case JSON:
                        if (value instanceof List) {
                            // 如果这是一个 List 参数
                            params.put(key, EasyUtils.listToJsonArray(((List<?>) value)));
                        } else if (value instanceof Map) {
                            // 如果这是一个 Map 参数
                            params.put(key, EasyUtils.mapToJsonObject(((Map<?, ?>) value)));
                        } else if (EasyUtils.isBeanType(value)) {
                            // 如果这是一个 Bean 参数
                            params.put(key, EasyUtils.mapToJsonObject(EasyUtils.beanToHashMap(value)));
                        } else {
                            // 如果这是一个普通的参数
                            params.put(key, value);
                        }
                        break;
                    default:
                        break;
                }

            } catch (IllegalAccessException e) {
                HttpLog.print(e);
            }
        }
    }

    /**
     * 创建 okhttp 请求
     *
     * @param url     请求地址
     * @param tag     标记
     * @param params  参数
     * @param headers 请求头
     * @param type    参数提交方式
     * @return okhttp   请求对象
     */
    private Request createRequest(String url, String tag, HttpParams params, HttpHeaders headers, BodyType type) {
        Request.Builder request = new Request.Builder();

        if (tag != null) {
            request.tag(tag);
        }

        // 添加请求头
        if (!headers.isEmpty()) {
            for (String key : headers.getNames()) {
                request.addHeader(key, headers.get(key));
            }
        }

        RequestBody body = null;
        String requestUrl = "";
        String httpMethod = getRequestApi().getMethod();

        switch (httpMethod) {
            case HttpConstant.Method.GET:
                HttpUrl.Builder builder = HttpUrl.get(url).newBuilder();
                // 添加参数
                if (!params.isEmpty()) {
                    for (String key : params.getNames()) {
                        builder.addEncodedQueryParameter(key, String.valueOf(params.get(key)));
                    }
                }
                HttpUrl link = builder.build();
                requestUrl = String.valueOf(link);
                request.url(link);
                break;
            case HttpConstant.Method.POST:
                request.url(url);
                requestUrl = url;
                body = createBody(params, type);
                break;
        }

        request.method(httpMethod, body);
        HttpLog.print("RequestUrl", requestUrl);
        HttpLog.print("RequestMethod", httpMethod);

        // 打印请求头和参数的日志
        if (HttpConfig.getInstance().isLogEnabled()) {

            if (!headers.isEmpty() || !params.isEmpty()) {
                HttpLog.print();
            }

            for (String key : headers.getNames()) {
                HttpLog.print(key, headers.get(key));
            }

            if (!headers.isEmpty() && !params.isEmpty()) {
                HttpLog.print();
            }

            for (String key : params.getNames()) {
                HttpLog.print(key, String.valueOf(params.get(key)));
            }

            if (!headers.isEmpty() || !params.isEmpty()) {
                HttpLog.print();
            }
        }

        return getRequestHandler().requestStart(getRequestApi(), request.build());
    }

    /**
     * 组装 RequestBody 对象
     */
    private RequestBody createBody(HttpParams params, BodyType type) {
        RequestBody body;

        if (params.isMultipart() && !params.isEmpty()) {
            MultipartBody.Builder builder = new MultipartBody.Builder();
            builder.setType(MultipartBody.FORM);
            for (String key : params.getNames()) {
                Object object = params.get(key);

                // 如果这是一个 File 对象
                if (object instanceof File) {
                    MultipartBody.Part part = EasyUtils.createPart(key, (File) object);
                    if (part != null) {
                        builder.addPart(part);
                    }
                    continue;
                }

                // 如果这是一个 InputStream 对象
                if (object instanceof InputStream) {
                    MultipartBody.Part part = EasyUtils.createPart(key, (InputStream) object);
                    if (part != null) {
                        builder.addPart(part);
                    }
                    continue;
                }

                // 如果这是一个自定义的 MultipartBody.Part 对象
                if (object instanceof MultipartBody.Part) {
                    builder.addPart((MultipartBody.Part) object);
                    continue;
                }

                // 如果这是一个自定义的 RequestBody 对象
                if (object instanceof RequestBody) {
                    builder.addFormDataPart(key, null, (RequestBody) object);
                    continue;
                }

                // 如果这是一个普通参数
                builder.addFormDataPart(key, String.valueOf(object));
            }

            try {
                body = builder.build();
            } catch (IllegalStateException ignored) {
                // 如果参数为空则会抛出异常：Multipart body must have at least one part.
                body = new FormBody.Builder().build();
            }

        } else if (type == BodyType.JSON) {
            body = new JsonBody(params.getParams());
        } else {
            FormBody.Builder builder = new FormBody.Builder();
            if (!params.isEmpty()) {
                for (String key : params.getNames()) {
                    builder.add(key, String.valueOf(params.get(key)));
                }
            }
            body = builder.build();
        }

        return body;
    }


    /**
     * 根据 TAG 取消请求任务
     */
    public static void cancel(Object tag) {
        if (tag == null) {
            return;
        }

        OkHttpClient client = HttpConfig.getInstance().getClient();

        // 清除排队等候的任务
        for (Call call : client.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }

        // 清除正在执行的任务
        for (Call call : client.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }

    /**
     * 清除所有请求任务
     */
    public static void cancel() {
        OkHttpClient client = HttpConfig.getInstance().getClient();

        // 清除排队等候的任务
        for (Call call : client.dispatcher().queuedCalls()) {
            call.cancel();
        }

        // 清除正在执行的任务
        for (Call call : client.dispatcher().runningCalls()) {
            call.cancel();
        }
    }

    /**
     * 无参数请求
     */
    public static class UrlRequest<Result> extends EasyHttp<Void, Result> {

        @Override
        public UrlRequest<Result> api(RequestApi<Void, Result> api) {
            super.api(api);
            return this;
        }
    }

    /**
     * 路径参数请求
     */
    public static class RestfulRequest<Result> extends EasyHttp<Void, Result> {

        @Override
        public RestfulRequest<Result> api(RequestApi<Void, Result> api) {
            super.api(api);
            return this;
        }

        /**
         * @param params 路径参数
         */
        public RestfulRequest<Result> pathParam(String... params) {
            super.path(MessageFormat.format(getPath(), params));
            return this;
        }
    }

    /**
     * 带参数请求
     *
     * @param <Param>  参数类型
     * @param <Result> 返回结果类型
     */
    public static class ParamRequest<Param, Result> extends EasyHttp<Param, Result> {

        @Override
        public ParamRequest<Param, Result> api(RequestApi<Param, Result> api) {
            super.api(api);
            return this;
        }

        @Override
        public ParamRequest<Param, Result> param(Param param) {
            super.param(param);
            return this;
        }
    }
}
