package ru.yandex.direct.http.smart.core;

import java.lang.reflect.Type;

import javax.annotation.Nullable;

import io.netty.handler.codec.http.HttpHeaders;
import org.asynchttpclient.uri.Uri;

import ru.yandex.direct.http.smart.converter.ResponseChecker;
import ru.yandex.direct.http.smart.converter.ResponseConverter;
import ru.yandex.direct.http.smart.parameters.ParameterHandler;


final class ServiceMethod<R> {
    final ResponseConverter<R> responseConverter;
    final ResponseChecker responseChecker;
    final String methodName;
    final Type responseType;
    final Class returnType;
    final Smart smart;

    private final Uri baseUrl;
    private final String httpMethod;
    private final String relativeUrl;
    private final HttpHeaders headers;
    private final ParameterHandler<?>[] parameterHandlers;

    ServiceMethod(ServiceMethodBuilder<R> builder) {
        this.smart = builder.smart;

        this.httpMethod = builder.httpMethod;
        this.methodName = builder.method.getName();
        this.baseUrl = Uri.create(smart.baseUrl);
        this.relativeUrl = builder.relativeUrl;
        this.headers = builder.headers;
        this.parameterHandlers = builder.parameterHandlers;
        this.responseType = builder.responseType;
        this.returnType = builder.method.getReturnType();
        this.responseConverter = builder.responseConverter;
        this.responseChecker = builder.responseChecker;
    }

    AbstractCall<R> toCall(@Nullable Object... args) {
        SmartRequestBuilder requestBuilder = new SmartRequestBuilder();
        requestBuilder
                .setRelativeUrl(relativeUrl)
                .setUri(baseUrl)
                .setMethod(httpMethod);

        //хедеры над методами добавляются первыми
        //дальше к ним добавляются хедеры из параметров
        //дальше вызывается конфигуратор хедеров
        HttpHeaders requestHeaders = requestBuilder.getHeaders();
        requestHeaders.add(headers);

        @SuppressWarnings("unchecked")
        ParameterHandler<Object>[] handlers = (ParameterHandler<Object>[]) parameterHandlers;

        int argumentCount = args != null ? args.length : 0;
        if (argumentCount != handlers.length) {
            throw new IllegalArgumentException("Argument count (" + argumentCount
                    + ") doesn't match expected count (" + handlers.length + ")");
        }

        for (int p = 0; p < argumentCount; p++) {
            handlers[p].apply(requestBuilder, args[p]);
        }

        for (Configurator<HttpHeaders> headersConfigurator : smart.headersConfigurators) {
            headersConfigurator.configure(requestHeaders);
        }

        if (CallsPack.class.isAssignableFrom(returnType)) {
            return new CallsPack<>(this, smart.parallelFetcherFactory, requestBuilder);
        }
        return new Call<>(this, smart.parallelFetcherFactory, requestBuilder);
    }
}
