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

import android.support.annotation.NonNull;

import com.avg.zaap.crashlibrary.ReportData;
import com.avg.zaap.crashlibrary.core.logger.CrashLogger;

import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.List;

/**
 * This class serves as the default
 * <p/>
 * Created by marek.osvald on 28.7.2016.
 */
public class CrashMetadataWriter implements ICrashMetadataWriter {

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

    private ICrashMetadataHelper mCrashMetadataHelper;
    private ICrashMetadataWriterFactory mFactory;

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

    /**
     * Parametric constructor. Sets dependency factory to use.
     *
     * @param factory dependency factory to be used.
     */
    public CrashMetadataWriter(ICrashMetadataWriterFactory factory) {
        mCrashMetadataHelper = factory.getCrashMetadataHelper();
        mFactory = factory;
    }

    @Override
    public boolean writeMetadata(@NonNull File crashFile, @NonNull ReportData reportData) {
        CrashLogger.d(TAG, "writeMetadata() called");

        File parent = crashFile.getParentFile();

        String metadataFileName = mCrashMetadataHelper.getMetadataFileName(crashFile);
        CrashLogger.d(TAG, "Crash metadata filename: %s", crashFile);

        File metadataFile = mFactory.openFile(parent, metadataFileName);

        if (metadataFile.exists()) {
            if (!metadataFile.delete()) {
                CrashLogger.e(TAG, "Unable to delete previous metadata file %s", metadataFile.getAbsolutePath());
                return false;
            } else {
                CrashLogger.d(TAG, "Old metadata file %s successfully deleted", metadataFile.getAbsolutePath());
            }
        }

        try {
            if (!metadataFile.createNewFile()) {
                CrashLogger.e(TAG, "Unable to create new metadata file %s.");
                return false;
            }
        } catch (IOException ex) {
            CrashLogger.e(TAG, "Unable to create new metadata file %s, original exception %s: %s", metadataFile.getAbsolutePath(), ex.getClass().getSimpleName(), ex.getMessage());
            return false;
        }

        return writeData(metadataFile, reportData);
    }


    private boolean writeData(File metadataFile, ReportData reportData) {
        OutputStreamWriter writer = null;
        boolean deleteFile = false;

        try {
            writer = mFactory.getFileWriter(metadataFile);

            // common
            writer.write(String.format("%s\n", reportData.getCrashCode()));
            writer.write(String.format("%s\n", reportData.getErrorName()));
            writer.write(String.format("%s\n", reportData.getErrorMessage()));
            writer.write(String.format("%s\n", escapeNewLines(reportData.getErrorDetails())));
            writeFileNames(writer, reportData.getFilesToPack());

            // CAP
            writer.write(String.format("%s\n", reportData.getCapExceptionCode()));
            writer.write(String.format("%d\n", reportData.getCapFaultingOffset()));
            writer.write(String.format("%s\n", reportData.getCapModuleName()));
            writer.write(String.format("%s\n", reportData.getCapModuleVersion()));
            writer.write(String.format("%s\n", reportData.getCapProcessName()));
            writer.write(String.format("%s\n", reportData.getCapProcessVersion()));
            writer.write(String.format("%s\n", reportData.getCapGUID()));

            // offline
            writer.write(String.format("%d\n", reportData.getPublishAttempts()));
            writer.write(String.format("%d\n", reportData.getLastAttemptTimestamp()));

        } catch (IOException ex) {
            CrashLogger.e(TAG, "IO Exception occurred, original exception %s: %s");
            deleteFile = true;
            return false;
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException ignored) {
                    CrashLogger.w(TAG, "Unable to close output stream writer, original exception %s: %s");
                }
            }

            if (deleteFile && !metadataFile.delete()) {
                CrashLogger.e(TAG, "Unable to delete metadata file: %s", metadataFile.getAbsolutePath());
            }
        }

        return true;
    }

    private void writeFileNames(OutputStreamWriter writer, List<String> fileNames) throws IOException {
        boolean first = true;

        for (String fileName : fileNames) {
            if (first) {
                first = false;
            } else {
                writer.write(ICrashMetadataHelper.FILE_NAME_SEPARATOR);
            }

            writer.write(fileName);
        }

        writer.write('\n');
    }

    private String escapeNewLines(String source) {
        if (source == null) {
            return "";
        }

        return source.replace("\n", "\\n");
    }

}
