package com.avg.zaap.crashlibrary.offline.publisher;

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.offline.publisher.metadata.ICrashMetadataWriter;

import java.io.File;

/**
 * This class serves as the default offline publisher.
 * <p/>
 * Created by marek.osvald on 27.7.2016.
 */
    public class OfflinePublisher implements IOfflinePublisher {

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

    private IOfflinePublisherCallback mCallback;
    private ICrashMetadataWriter mCrashMetadataWriter;
    private IOfflinePublisherFactory mFactory;

    /**
     * Parametric constructor. Sets the callback. Uses default dependency factory.
     *
     * @param callback callback to use when publishing has finished
     */
    public OfflinePublisher(IOfflinePublisherCallback callback) {
        this(callback, new OfflinePublisherFactory());
    }

    /**
     * Parametric constructor. Sets the callback and dependency factory to be used.
     *
     * @param callback callback to use when publishing has finished
     * @param factory  dependency factory to be used
     */
    public OfflinePublisher(IOfflinePublisherCallback callback, IOfflinePublisherFactory factory) {
        mCallback = callback;
        mCrashMetadataWriter = factory.getCrashMetadataWriter();
        mFactory = factory;
    }

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

        File file = mFactory.openFile(filePath);

        // check whether file exists
        if (!file.exists()) {
            CrashLogger.e(TAG, "File %s does not exist.", filePath);
            mCallback.onFailure(String.format("File %s does not exist.", filePath));
            return;
        }

        // check whether file is a regular file, not directory
        if (file.isDirectory()) {
            CrashLogger.e(TAG, "File %s is a directory not a crash report file.", filePath);
            mCallback.onFailure(String.format("File %s is a directory not a crash report file.", filePath));
            return;
        }

        // if a maximal publish attempt count has been reached, simply delete the file
        final int reportAttempts = reportData.getPublishAttempts();
        final int maxAttempts = CrashLib.getConfig().getMaxAttempts();

        if (reportAttempts >= maxAttempts) {
            CrashLogger.d(TAG, "Maximal count of publish attempts have been reached: %d (max attempts: %d)", reportAttempts, maxAttempts);

            if (file.delete()) {
                CrashLogger.d(TAG, "Crash report file (%s) successfully deleted.", file.getAbsolutePath());
                mCallback.onSuccess();
            } else {
                CrashLogger.w(TAG, "Unable to delete the crash report file: %s", file.getAbsolutePath());
                mCallback.onFailure(String.format("Unable to delete the crash report file: %s", file.getAbsolutePath()));
            }

            return;
        }

        File crashReportDirectory = mFactory.openFile(context.getFilesDir(), OFFLINE_REPORT_DIRECTORY_NAME);

        // check whether the offline directory exists
        if (!initializeDirectoryIfItDoesNotExist(crashReportDirectory)) {
            return;
        }

        File offlineCrashReport = mFactory.openFile(crashReportDirectory, file.getName());

        // move the report
        if (!file.renameTo(offlineCrashReport)) {
            CrashLogger.e(TAG, "Unable to move the crash report file.");
            mCallback.onFailure("Unable to move the crash report file.");
            return;
        }

        // metadata
        if (!mCrashMetadataWriter.writeMetadata(offlineCrashReport, reportData)) {
            CrashLogger.e(TAG, "Unable to write crash metadata file.");
            mCallback.onFailure("Unable to write crash metadata file.");
            return;
        }

        mCallback.onSuccess();
    }

    private boolean initializeDirectoryIfItDoesNotExist(@NonNull File offlineCrashReportDirectory) {
        if (offlineCrashReportDirectory.exists() && offlineCrashReportDirectory.isFile()) {
            if (!offlineCrashReportDirectory.delete()) {
                CrashLogger.e(TAG, "Offline crash report directory is a file and cannot be deleted!");
                mCallback.onFailure("Offline crash report directory is a file and cannot be deleted!");
                return false;
            }
        }

        if (!offlineCrashReportDirectory.exists()) {
            if (!offlineCrashReportDirectory.mkdirs()) {
                CrashLogger.e(TAG, "Unable to create offline crash report directory.");
                mCallback.onFailure("Unable to create offline crash report directory.");
                return false;
            }

            CrashLogger.d(TAG, "Offline crash report directory successfully created.");
        }

        CrashLogger.d(TAG, "Offline crash report directory already exists.");
        return true;
    }

}
