package ru.yandex.autotests.directapi.apiclient.config;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.xml.ws.WebServiceException;

import org.apache.http.client.utils.URIBuilder;

import ru.yandex.autotests.directapi.exceptions.DirectAPIException;
import ru.yandex.autotests.httpclient.lite.core.config.HttpClientConnectionConfig;

/**
 * User: mariabye
 * Date: 14.05.13
 * Client connection configuration:
 * host, port, auth type, api version
 * configured be pre-defined
 */
public class ConnectionConfig implements Cloneable {
    static final String API_PREFIX = "api.";

    //region pre-defined configurations
   /* public static ConnectionConfig TC = getTCDefaultConfig();
    public static ConnectionConfig BETA_8999 = getBetaDefaultConfig(8999);
    public static ConnectionConfig SANDBOX_TC = getSandboxDefaultConfig();

    private static int defaultCurrentVersion = 104;

    private static ConnectionConfig getTCDefaultConfig() {
        ConnectionConfig config = new ConnectionConfig()
                .stage(StageType.TC, null)
                .version(defaultCurrentVersion)
                .apiPortType(WsdlVersion.stubType.get(defaultCurrentVersion));

        return config;
    }

    private static ConnectionConfig getTC2DefaultConfig() {
        ConnectionConfig config = new ConnectionConfig()
                .stage(StageType.TC2, null)
                .version(defaultCurrentVersion)
                .apiPortType(WsdlVersion.stubType.get(defaultCurrentVersion));
        return config;
    }

    private static ConnectionConfig getSandboxDefaultConfig() {
        ConnectionConfig config = new ConnectionConfig()
                .stage(StageType.SANDBOX_TC, null)
                .version(defaultCurrentVersion)
                .apiPortType(WsdlVersion.stubType.get(defaultCurrentVersion));
        return config;
    }

    private static ConnectionConfig getBetaDefaultConfig(int betaPort) {
        ConnectionConfig config = new ConnectionConfig()
                .stage(StageType.BETA1, betaPort)
                .version(defaultCurrentVersion)
                .apiPortType(WsdlVersion.stubType.get(defaultCurrentVersion));
        return config;
    }

    private static ConnectionConfig getBetaPreciseDefaultConfig(int betaPort) {
        ConnectionConfig config = new ConnectionConfig()
                .stage(StageType.BETA_PRECISE, betaPort)
                .version(defaultCurrentVersion)
                .apiPortType(WsdlVersion.stubType.get(defaultCurrentVersion));
        return config;
    }*/
    //endregion

    /**
     * Host like http://test-direct.yandex.ru
     */
    private String apiHost;

    /**
     * api version: JSON | SOAP
     */
    private ProtocolType protocolType;

    /**
     * api version: 4 | 104 | 5
     */
    private int apiVersion = 104;

    /**
     * default wsdl package
     */
    private Class apiPortType;

    public ConnectionConfig(String apiHost) {
        this(apiHost, ProtocolType.JSON);
    }

    public ConnectionConfig(String apiHost,
                            ProtocolType protocolType) {
        this.apiHost = apiHost;
        this.apiVersion = 104;
        this.protocolType = protocolType;
        this.apiPortType = WsdlVersion.stubType.get(this.apiVersion);
    }

    public ConnectionConfig clone() {
        return new ConnectionConfig(apiHost)
                .version(apiVersion)
                .apiPortType(apiPortType)
                .protocolType(protocolType);
    }

    public ConnectionConfig version(int apiVersion) {
        this.apiVersion = apiVersion;
        return this;
    }

    public ConnectionConfig apiPortType(Class apiPortType) {
        this.apiPortType = apiPortType;
        return this;
    }

    public ConnectionConfig protocolType(ProtocolType protocolType) {
        this.protocolType = protocolType;
        return this;
    }

    public int getAPIVersion() {
        return apiVersion;
    }

    public String getPackage() {
        return apiPortType.getPackage().getName();
    }

    public String getApiHost() {
        return apiHost;
    }

    /**
     * api path definitions for each api version and protocol type
     */
    public static Map<ProtocolType, Map<Integer, String>> API_4_PATH =
            new HashMap<ProtocolType, Map<Integer, String>>() {
                {
                    put(ProtocolType.SOAP, new HashMap<Integer, String>() {
                                {
                                    put(4, "/v4/soap/");
                                    put(5, "/v5/soap/");
                                    put(104, "/live/v4/soap/");
                                    put(1, "/v4/soap/1/");
                                    put(2, "/live/v4/soap/1/");
                                    put(11, "/v4/soap/2/");
                                    put(12, "/live/v4/soap/2/");
                                }
                            }
                    );
                    put(ProtocolType.JSON, new HashMap<Integer, String>() {
                                {
                                    put(4, "/v4/json/");
                                    put(5, "/v5/json/");
                                    put(104, "/live/v4/json/");
                                    put(1, "/v4/json/");
                                    put(2, "/live/v4/json/");
                                    put(11, "/v4/json/");
                                    put(12, "/live/v4/json/");
                                }
                            }
                    );
                }
            };

    /**
     * новая perl-ТС имеет другую схему адресации
     * DIRECTADMIN-8121
     */
    private void injectServiceSubdomain(URIBuilder uriBuilder) {
        String host = uriBuilder.getHost();
        if (host.startsWith(API_PREFIX)) {
            return;
        }
        if (!(host.startsWith("test.direct."))) {
            return;
        }
        uriBuilder.setHost(API_PREFIX + host);
        // порт пока не трогаем
    }

    /**
     * generate api path
     * api use 14443 port and secure connection by default
     *
     * @return
     */
    public String getEndPoint(ProtocolType protocolType) {
        try {
            URIBuilder uriBuilder = new URIBuilder(apiHost);

            uriBuilder
                    .setScheme("https")
                    .setPath(API_4_PATH.get(protocolType).get(this.apiVersion));

            if (uriBuilder.getPort() == -1) {
                uriBuilder.setPort(14443);
            }
            injectServiceSubdomain(uriBuilder);
            uriBuilder.build();
            return uriBuilder.toString();
        } catch (URISyntaxException e) {
            throw new DirectAPIException("Ошибка формирования пути для запроса API в версии 4", e);
        }
    }

    public String getEndPointForV5(ProtocolType type, String serviceName) {
        try {
            URIBuilder uriBuilder = new URIBuilder(apiHost);

            uriBuilder.setScheme("https");

            if (uriBuilder.getPort() == -1) {
                uriBuilder.setPort(14443);
            }

            switch (type) {
                case SOAP:
                    uriBuilder.setPath("/v5/" + serviceName);
                    break;
                case JSON:
                    uriBuilder.setPath("/json/v5/" + serviceName);
                    break;
                case XML:
                    uriBuilder.setPath("/v5/" + serviceName);
                    break;
            }
            injectServiceSubdomain(uriBuilder);
            return uriBuilder.build().toString();
        } catch (URISyntaxException e) {
            throw new DirectAPIException("Ошибка формирования пути для запроса API в версии 5", e);
        }
    }

    public URL getV5WsdlLocation(String serviceName) {
        URL url = null;
        try {
            url = new URL(getEndPointForV5(ProtocolType.SOAP, serviceName) + "?wsdl");
        } catch (MalformedURLException ex) {
            throw new WebServiceException(ex);
        }
        return url;
    }

    public void url(String apiHost) {
        this.apiHost = apiHost;
    }

    public static HttpClientConnectionConfig getHttpClientConnectionConfig(String url) {
        URI uri;
        try {
            uri = new URIBuilder(url).build();
        } catch (URISyntaxException e) {
            throw new DirectAPIException("Failed url parsing", e);
        }

        HttpClientConnectionConfig httpClientConnectionConfig = new HttpClientConnectionConfig()
                .scheme(uri.getScheme())
                .host(uri.getHost())
                .port(uri.getPort())
                .path(uri.getPath());
        return httpClientConnectionConfig;

    }

    public ProtocolType getProtocolType() {
        return protocolType;
    }
}
