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

import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import com.google.gson.JsonSyntaxException;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.Header;
import org.apache.http.message.BasicHeader;

import ru.yandex.autotests.directapi.apiclient.BaseApiClient;
import ru.yandex.autotests.directapi.apiclient.HttpMessage;
import ru.yandex.autotests.directapi.apiclient.RequestHeader;
import ru.yandex.autotests.directapi.apiclient.config.ConnectionConfig;
import ru.yandex.autotests.directapi.apiclient.config.ProtocolType;
import ru.yandex.autotests.directapi.apiclient.errors.Api5JsonError;
import ru.yandex.autotests.directapi.apiclient.errors.RbacErrorException;
import ru.yandex.autotests.directapi.model.api5.Action;
import ru.yandex.autotests.directapi.model.api5.reports.ReportProcessingMode;
import ru.yandex.autotests.irt.testutils.json.JsonUtils;

/**
 * Author pavryabov
 * Date 30.06.14
 */
public abstract class BaseApiV5Client extends BaseApiClient {

    public static Log log = LogFactory.getLog(BaseApiV5Client.class);
    private static final Pattern RBAC_ERROR_RE =
            Pattern.compile("^rbac_move_nscamp_to_scamp\\(\\$rbac, \\d+, \\d+, \\d+\\) == 990001.*$",
                    Pattern.MULTILINE | Pattern.DOTALL);

    /**
     * @param serviceName
     * @param login       subclient or serviced client login used to pass to Client-Login Header
     * @param methodName
     * @param params
     * @param <T>
     * @return
     */
    public final <T> T invokeMethod(ServiceNames serviceName, String login, Action methodName, Object params) {
        return this.<T>invokeMethodEx(serviceName, login, methodName, params).getResponseObject();
    }

    public abstract <T> MethodInvocationResult<T> invokeMethodEx(
            ServiceNames serviceName, String login, Action methodName, Object params);

    /**
     * Initialized with default properties
     */
    public BaseApiV5Client() {
        super();
    }

    public BaseApiV5Client(ConnectionConfig connectionConfig) {
        super(connectionConfig);
    }

    public BaseApiV5Client(ConnectionConfig connectionConfig, RequestHeader requestHeader) {
        super(connectionConfig, requestHeader);
    }

    protected Map<String, Object> getHttpHeaders(String clientLogin, ReportProcessingMode processingMode) {
        Map<String, Object> requestHeaders = new HashMap<>();
        if (requestHeader.getToken() != null) {
            requestHeaders.put("Authorization", "Bearer " + requestHeader.getToken());
        }
        if (clientLogin != null) {
            requestHeaders.put("Client-Login", clientLogin);
        }
        requestHeaders.put("Accept-Language", requestHeader.getLocale());
        if (requestHeader.getPreferPerlImplementation() != null) {
            requestHeaders.put("X-Prefer-Perl-Implementation", requestHeader.getPreferPerlImplementation());
        }
        if (requestHeader.getFakeLogin() != null) {
            requestHeaders.put("Fake-Login", requestHeader.getFakeLogin());
        }
        if (requestHeader.getUseOperatorUnits() != null) {
            requestHeaders.put("Use-Operator-Units", requestHeader.getUseOperatorUnits());
        }
        if (requestHeader.getTokenType() != null) {
            requestHeaders.put("Token-Type", requestHeader.getTokenType());
        }
        if (requestHeader.getReturnMoneyInMicros() != null) {
            requestHeaders.put("returnMoneyInMicros", requestHeader.getReturnMoneyInMicros());
        }
        if (processingMode != null) {
            requestHeaders.put("processingMode", processingMode.toString());
        }
        if (requestHeader.getSkipReportHeader() != null) {
            requestHeaders.put("skipReportHeader", requestHeader.getSkipReportHeader());
        }
        if (requestHeader.getSkipColumnHeader() != null) {
            requestHeaders.put("skipColumnHeader", requestHeader.getSkipColumnHeader());
        }
        if (requestHeader.getSkipReportSummary() != null) {
            requestHeaders.put("skipReportSummary", requestHeader.getSkipReportSummary());
        }
        if (requestHeader.getUseCampAggregatedLastChange() != null) {
            requestHeaders.put("useCampAggregatedLastChange", requestHeader.getUseCampAggregatedLastChange());
        }
        return requestHeaders;
    }

    protected Header[] toHeadersArray(Map<String, Object> headersMap) {
        int index = 0;
        Header[] headers = new Header[headersMap.size()];
        for (String key : headersMap.keySet()) {
            headers[index++] = new BasicHeader(key, headersMap.get(key).toString());
        }
        return headers;
    }

    protected void checkJsonFaultPresence(String response) {
        checkJsonFaultPresence(response, false);
    }

    /**
     * Look http message for error codes and throw AxisError if found one
     *
     * @param response
     */
    void checkJsonFaultPresence(String response, boolean isJavaResponse) {
        try {
            Api5JsonError error = JsonUtils.getObject(response, Api5JsonError.class);
            if (error.getErrorCode() != null) {
                error.setJavaResponse(isJavaResponse);
                if (error.getErrorCode() == 1002 && RBAC_ERROR_RE.matcher(error.getErrorDetails()).matches()) {
                    throw new RbacErrorException(error);
                }
                throw error;
            }
        } catch (JsonSyntaxException e) {
            log.debug("Non parsable response error");
        }
    }

    public String traceHttpMessage(HttpMessage message, ProtocolType protocolType) {
        log.info(message.toString());
        Map<String, String> root = new HashMap<>();
        root.put("usualFormatString", message.toString());

        String curlString;
        switch (protocolType) {
            case JSON:
                curlString = message.toCurlStringForJson();
                break;
            case SOAP:
                curlString = message.toCurlStringForSoap();
                break;
            case XML:
                curlString = message.toCurlStringForXml();
                break;
            default:
                curlString = "Curl for this protocol is not implemented";
                break;
        }
        root.put("curlString", curlString);

        root.put("buttonName", protocolType.toString());

        String attachmentHTML;
        try {
            TemplateLoader file = new ClassTemplateLoader(this.getClass(), "/");
            Configuration cfg = new Configuration();
            cfg.setTemplateLoader(file);
            cfg.setObjectWrapper(new DefaultObjectWrapper());
            cfg.setDefaultEncoding("UTF-8");
            Template template = cfg.getTemplate("request2curlTemplate.ftl");
            Writer fileWriter = new StringWriter();
            try {
                template.process(root, fileWriter);
            } finally {
                fileWriter.close();
            }
            attachmentHTML = fileWriter.toString();
        } catch (Exception e) {
            throw new RuntimeException("Error", e);
        }

        return attachmentHTML;
    }
}
