package com.avg.zaap.crashlibrary.provider.app;

import android.support.annotation.NonNull;

import com.avg.zaap.crashlibrary.core.logger.CrashLogger;
import com.avg.zaap.crashlibrary.core.writer.DataWriterException;
import com.avg.zaap.crashlibrary.core.writer.IDataWriter;
import com.avg.zaap.crashlibrary.provider.CrashProviderException;
import com.avg.zaap.crashlibrary.provider.ICrashAsyncProvider;
import com.avg.zaap.crashlibrary.provider.ICrashProviderCallback;

import java.io.File;
import java.lang.reflect.Field;

/**
 * This class serves as the abstract basis for the application provider.
 * <p>
 * Created by marek.osvald on 29.4.2016.
 */
public abstract class BuildConfigProvider implements ICrashAsyncProvider {

    private static final String TAG = BuildConfigProvider.class.getSimpleName();
    private static final String FORMATTER = "%-20s";

    private String mFileName;
    private Class mClass;
    private IDataWriter mDataWriter;
    private IClassFieldHelper mClassFieldHelper;

    /**
     * Default constructor. Uses the default dependency factory.
     *
     * @param fileName file name to
     * @param clazz    BuildConfig class to load the configuration fields from
     */
    @SuppressWarnings("WeakerAccess")
    protected BuildConfigProvider(@SuppressWarnings("SameParameterValue") String fileName, Class clazz) {
        this(fileName, clazz, new BuildConfigProviderFactory());
    }

    /**
     * Parametric constructor. Sets the dependency factory to be used.
     *
     * @param fileName file name to write the information into
     * @param clazz    BuildConfig class to load the configuration fields from
     * @param factory  dependency factory to be used
     */
    protected BuildConfigProvider(String fileName, Class clazz, IBuildConfigProviderFactory factory) {
        mFileName = fileName;
        mClass = clazz;
        mDataWriter = factory.getDataWriter();
        mClassFieldHelper = factory.getClassFieldHelper();
    }

    @Override
    public void provideData(@NonNull String directory, @NonNull ICrashProviderCallback callback) {
        String path = directory + File.separatorChar + mFileName;
        String buildConfigValues;

        try {
            buildConfigValues = getBuildConfigValues();
        } catch (CrashProviderException ex) {
            CrashLogger.e(TAG, "Cannot get BuildConfig values: %s", ex.getMessage());
            callback.onFailure(TAG, ex);
            return;
        }

        try {
            mDataWriter.write(path, buildConfigValues);
            callback.onSuccess(path);
        } catch (DataWriterException ex) {
            CrashLogger.e(TAG, "Can't write BuildConfig values to file: %s", ex.getMessage());
            callback.onFailure(TAG, new CrashProviderException("Can't write BuildConfig values to file", ex));
        }
    }

    private String getBuildConfigValues() throws CrashProviderException {
        StringBuilder stringBuilder = new StringBuilder();

        try {
            Field[] fields = mClassFieldHelper.loadFields(mClass);
            for (Field field : fields) {
                //@formatter:off
                stringBuilder.append(String.format(FORMATTER, field.getName()))
                             .append(": ")
                             .append(field.get(null))
                             .append('\n');
                //@formatter:on
            }

            return stringBuilder.toString();
        } catch (IllegalAccessException ex) {
            CrashLogger.e(TAG, ex.getMessage());
            throw new CrashProviderException("Inaccessible field", ex);
        }
    }

}
