package com.banesco.common.domain.model;

import lombok.*;

import java.lang.reflect.Type;
import java.util.Map;

@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class HttpRequest {
    private String url;
    private HttpMethod method;
    private Object body;
    private Map<String, String> headers;
    private Map<String, String> queryParams;
    private Map<String, String> pathParams;

    @Builder.Default
    private Class<?> responseType = Object.class;

    private Class<?> genericType;

    private Type complexType;

    private Class<?> errorType;

    private String statusSuccess;

    @Builder.Default
    private boolean eitherResponse = false;

    @Builder.Default
    private boolean apiPrivateResponse = false;

    @Builder.Default
    private boolean apiResponse = false;

    @Builder.Default
    private boolean listResponse = false;

    @Builder.Default
    private int connectTimeout = 5000;

    @Builder.Default
    private int readTimeout = 10000;

    @Builder.Default
    private boolean returnFullResponse = false;

    @Builder.Default
    private boolean logRequestBody = true;

    @Builder.Default
    private boolean logResponseBody = true;

    public static <T> HttpRequest forApiResponse(
            String url,
            HttpMethod method,
            Class<T> dataType
    ) {
        return HttpRequest.builder()
                .url(url)
                .method(method)
                .responseType(ApiResponse.class)
                .genericType(dataType)
                .apiResponse(true)
                .build();
    }

    public static <T> HttpRequest forApiResponseList(
            String url,
            HttpMethod method,
            Class<T> elementType
    ) {
        return HttpRequest.builder()
                .url(url)
                .method(method)
                .responseType(ApiResponse.class)
                .complexType(TypeBuilder.listOf(elementType))
                .apiResponse(true)
                .listResponse(true)
                .build();
    }

    public static <T> HttpRequest forApiPrivateResponse(
            String url,
            String statusSuccess,
            HttpMethod method,
            Class<T> successType
    ) {
        return HttpRequest.builder()
                .url(url)
                .method(method)
                .responseType(ApiPrivateResponse.class)
                .complexType(TypeBuilder.parametricType(
                        Either.class,
                        successType,
                        ApiPrivateError.class
                ))
                .apiPrivateResponse(true)
                .eitherResponse(true)
                .errorType(ApiPrivateError.class)
                .statusSuccess(statusSuccess)
                .build();
    }

    public static <T> HttpRequest forApiPrivateResponseList(
            String url,
            String statusSuccess,
            HttpMethod method,
            Class<T> elementType
    ) {
        return HttpRequest.builder()
                .url(url)
                .method(method)
                .responseType(ApiPrivateResponse.class)
                .complexType(TypeBuilder.parametricType(
                        Either.class,
                        TypeBuilder.listOf(elementType),
                        ApiPrivateError.class
                ))
                .apiPrivateResponse(true)
                .eitherResponse(true)
                .listResponse(true)
                .errorType(ApiPrivateError.class)
                .statusSuccess(statusSuccess)
                .build();
    }

    public static <T> HttpRequest forDirectResponse(
            String url,
            HttpMethod method,
            Class<T> responseType
    ) {
        return HttpRequest.builder()
                .url(url)
                .method(method)
                .responseType(responseType)
                .build();
    }

    public static <T, R> HttpRequest forDirectResponse(
            String url,
            HttpMethod method,
            Class<T> responseType,
            Class<R> errorType
    ) {
        return HttpRequest.builder()
                .url(url)
                .method(method)
                .responseType(responseType)
                .errorType(errorType)
                .build();
    }

    public static <T, U> HttpRequest forGenericResponse(
            String url,
            HttpMethod method,
            Class<T> rawType,
            Class<U> genericType
    ) {
        return HttpRequest.builder()
                .url(url)
                .method(method)
                .responseType(rawType)
                .complexType(TypeBuilder.parametricType(rawType, genericType))
                .build();
    }

    public HttpRequest withHeaders(Map<String, String> headers) {
        this.headers = headers;
        return this;
    }

    public HttpRequest withQueryParams(Map<String, String> queryParams) {
        this.queryParams = queryParams;
        return this;
    }

    public HttpRequest withPathParams(Map<String, String> pathParams) {
        this.pathParams = pathParams;
        return this;
    }

    public HttpRequest withBody(Object body) {
        this.body = body;
        return this;
    }

    public HttpRequest withTimeout(int connectTimeout, int readTimeout) {
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        return this;
    }

    public HttpRequest disableRequestLogging() {
        this.logRequestBody = false;
        return this;
    }

    public HttpRequest disableResponseLogging() {
        this.logResponseBody = false;
        return this;
    }

    public ResponseType getExpectedResponseType() {
        if (apiPrivateResponse && eitherResponse) {
            return listResponse ? ResponseType.API_PRIVATE_WITH_EITHER_LIST : ResponseType.API_PRIVATE_WITH_EITHER;
        } else if (apiResponse) {
            return listResponse ? ResponseType.API_RESPONSE_LIST : ResponseType.API_RESPONSE;
        } else if (complexType != null) {
            return ResponseType.COMPLEX_TYPE;
        } else if (genericType != null) {
            return ResponseType.GENERIC_TYPE;
        } else {
            return ResponseType.DIRECT_TYPE;
        }
    }

    public enum ResponseType {
        API_RESPONSE,
        API_RESPONSE_LIST,
        API_PRIVATE_WITH_EITHER,
        API_PRIVATE_WITH_EITHER_LIST,
        GENERIC_TYPE,
        COMPLEX_TYPE,
        DIRECT_TYPE
    }

    public enum HttpMethod {
        GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
    }
}