package com.avg.zaap.crashlibrary.publish.cap;

import android.content.Context;
import android.support.annotation.NonNull;

import com.avg.zaap.crashlibrary.CrashLib;
import com.avg.zaap.crashlibrary.ReportData;
import com.avg.zaap.crashlibrary.core.logger.CrashLogger;
import com.avg.zaap.crashlibrary.publish.ICrashPublisher;
import com.avg.zaap.crashlibrary.publish.ICrashPublisherCallback;
import com.avg.zaap.crashlibrary.publish.cap.http.CapRequest;
import com.avg.zaap.crashlibrary.publish.cap.http.CapRequestException;
import com.avg.zaap.crashlibrary.publish.cap.http.CapResponse;
import com.avg.zaap.crashlibrary.publish.cap.http.CapResponseCode;

import java.util.Map;

/**
 * This publisher publishes the crash data to the CAP server.
 * <p/>
 * Created by marek.osvald on 6.6.2016.
 */
public class CapCrashPublisher implements ICrashPublisher {

    private static final String TAG = CapCrashPublisher.class.getSimpleName();

    @SuppressWarnings("SpellCheckingInspection")
    private static final String FORM_KEY_GUID = "guid";
    private static final String FORM_KEY_PRODUCT = "prd";
    private static final String FORM_KEY_PRODUCT_VERSION = "prv";
    @SuppressWarnings("SpellCheckingInspection")
    private static final String FORM_KEY_PROTOCOL_VERSION = "protver";

    private static final String FORM_VALUE_PROTOCOL_VERSION = "2";

    private IExceptionHeaderGenerator mExceptionHeaderGenerator;
    private ICapHelper mCapHelper;
    private ICapCrashPublisherFactory mFactory;

    /**
     * Default constructor. Uses default dependency factory.
     */
    public CapCrashPublisher() {
        this(new CapCrashPublisherFactory());
    }

    /**
     * Parametric constructor. Sets dependency factory to be used.
     *
     * @param factory dependency factory to be used
     */
    public CapCrashPublisher(ICapCrashPublisherFactory factory) {
        mExceptionHeaderGenerator = factory.getExceptionHeaderGenerator();
        mCapHelper = factory.getCapHelper();
        mFactory = factory;
    }

    @Override
    public void publish(Context context, @NonNull ReportData reportData, @NonNull String filePath, ICrashPublisherCallback callback) {
        CrashLogger.d(TAG, "publish() called, filePath: %s", filePath);
        try {
            CrashLogger.d(TAG, "Crash code: %s", reportData.getCrashCode());

            CapRequest reportRequest = mFactory.getCapRequest();
            reportRequest.openURL(CrashLib.getConfig().getCrashReportURL())
                    .addFormValue(FORM_KEY_GUID, reportData.getCapGUID())
                    .addFormValue(FORM_KEY_PRODUCT, CrashLib.getConfig().getCapProductID())
                    .addFormValue(FORM_KEY_PRODUCT_VERSION, CrashLib.getConfig().getApplicationVersion())
                    .addFormValue(FORM_KEY_PROTOCOL_VERSION, FORM_VALUE_PROTOCOL_VERSION);

            Map<String, String> exceptionHeader = mExceptionHeaderGenerator.generateHeader(
                    reportData.getCapExceptionCode(),
                    mCapHelper.getCapFaultingHexOffset(reportData.getCapFaultingOffset()),
                    reportData.getCapModuleName(),
                    reportData.getCapModuleVersion(),
                    reportData.getCapProcessName(),
                    reportData.getCapProcessVersion(),
                    reportData.getCrashCode()
            );

            reportRequest.addExceptionHeader(exceptionHeader);
            final CapResponse capResponse = reportRequest.send();

            CrashLogger.d(TAG, capResponse.toString());

            switch (capResponse.capCode) {
                case CapResponseCode.FATAL:
                    CrashLogger.e(TAG, "CAP response is malformed, incorrect or incomplete.");
                    callback.onCrashPublisherFailure(TAG, reportData, null);
                    break;

                case CapResponseCode.ERROR:
                    CrashLogger.e(TAG, "CAP response is ERROR.");
                    callback.onCrashPublisherFailure(TAG, reportData, null);
                    break;

                case CapResponseCode.NO_DUMP:
                    CrashLogger.w(TAG, "CAP refused the dump upload. (Has the dump limit been reached?)");
                    callback.onCrashPublisherFailure(TAG, reportData, null);
                    break;

                case CapResponseCode.MINI_DUMP:
                case CapResponseCode.FULL_DUMP:
                case CapResponseCode.KEEP_DUMPS:
                case CapResponseCode.DIRECT_CFG:
                    CapRequest uploadRequest = mFactory.getCapRequest()
                            .openURL(CrashLib.getConfig().getCrashUploadURL())
                            .addFormValue(FORM_KEY_GUID, reportData.getCapGUID())
                            .addFormValue(FORM_KEY_PRODUCT, CrashLib.getConfig().getCapProductID());

                    uploadRequest.addFile(filePath);
                    final CapResponse uploadResponse = uploadRequest.send();

                    CrashLogger.d(TAG, uploadResponse.toString());
                    callback.onCrashPublisherSuccess(TAG, reportData);
                    break;

                default:
                    CrashLogger.e(TAG, "Unknown CAP response code: %s", capResponse.capCode);
                    callback.onCrashPublisherFailure(TAG, reportData, null);
                    break;
            }
        } catch (CapRequestException ex) {
            CrashLogger.e(TAG, "Unable to create CAP Request, original exception %s: %s", ex.getClass().getSimpleName(), ex.getMessage());
            callback.onCrashPublisherFailure(getClass().getSimpleName(), reportData, ex);
        }
    }

}
