package com.workfusion.odf.impl.converter.html2pdf;

import java.util.Optional;
import java.util.UUID;
import javax.inject.Inject;

import com.google.common.collect.ImmutableMap;
import groovy.lang.Binding;
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.remote.BrowserType;
import org.slf4j.Logger;
import org.springframework.http.MediaType;
import org.webharvest.runtime.variables.Variable;
import org.webharvest.utils.SystemUtilities;

import com.workfusion.rpa.core.BindingUtils;
import com.workfusion.rpa.core.storage.S3Manager;
import com.workfusion.rpa.driver.Driver;
import com.workfusion.rpa.helpers.RPA;

import static com.workfusion.rpa.helpers.RPA.driver;
import static com.workfusion.rpa.helpers.utils.ApiUtils.checkUniversalDriver;

/**
 * @author Leanid_Piliptsevich
 */
public class RpaPdfReportGenerator implements PdfReportGenerator {

    public static final String COMPLETION_SUCCESS_MESSAGE = "Successfully printed PDF";
    public static final String S3_BUCKET_KEY = "s3_bucket";
    public static final String S3_FOLDER_KEY = "s3_folder";
    static final String HTML_2_PDF_PRINTER_JAR_NAME = "chrome-pdf-printer-0.2.20.jar";
    static final String GENERATE_PDF_SCRIPT_TEMPLATE = "\"java -jar %s %s\".execute().text";
    static final String EMPTY_PDF_FILE_GENERATION_SCRIPT
            = "java.nio.file.Files.createTempFile(\"${fileName}\", \".pdf\")" +
            ".toFile().getCanonicalPath().replace(\"\\\\\", \"/\")";
    private static final String PDF_PRINTER_S3_FOLDER = "html2pdf";
    private static final String GROOVY = "GROOVY";
    static final String CHECK_THAT_FILE_EXISTS_SCRIPT = "\nimport java.io.File;\n\n" +
            " File file = new File(\"${filePath}\");" +
            "\n return file.exists();";
    private static final String FILE_PATH_KEY = "filePath";

    private final Driver desktopDriver;
    private final S3Manager s3Manager;
    private final Logger logger;
    private final String initialDriver;
    private final SystemUtilities sys;

    @Inject
    public RpaPdfReportGenerator(final S3Manager s3Manager, final Logger logger, final Binding binding) {
        desktopDriver = driver();
        validateDriver();

        final String driverType = desktopDriver.getDriverInfo().getType();
        this.sys = BindingUtils.getSys(binding);

        validateS3Settings();

        this.s3Manager = s3Manager;
        this.logger = logger;
        initialDriver = driverType;

        switchToDesktopDriverIfCurrentIsUniversal(driverType);
    }

    @Override
    public byte[] generatePdf(final String webLink) {
        if (StringUtils.isBlank(webLink)) {
            throw new NullPointerException("Input link is null or blank");
        }

        final String jarFileLocationOnRobot = uploadLibraryIfMissing();
        logger.debug("Jar file location: {}", jarFileLocationOnRobot);
        final String uniqueTempFileName = UUID.randomUUID().toString();
        final String pdfFileTargetOnRobot = (String) desktopDriver.executeScript(
                EMPTY_PDF_FILE_GENERATION_SCRIPT, GROOVY,
                ImmutableMap.of("fileName", uniqueTempFileName));
        try {
            final String launchParameters = getLaunchParameters(webLink, pdfFileTargetOnRobot);

            final String generatePdfScript = String.format(GENERATE_PDF_SCRIPT_TEMPLATE, jarFileLocationOnRobot, launchParameters);
            final String executionResult = (String) desktopDriver.executeScript(generatePdfScript, GROOVY);

            if (!executionResult.contains(COMPLETION_SUCCESS_MESSAGE)) {
                throw new IllegalStateException("Execution of PDF was not completed successfully. Output from RPA bot:\n " + executionResult + "\nEnd of RPA bot output");
            }

            logger.debug("Execution executionResult is {}", executionResult);

            final byte[] pdfAsBytes = RPA.downloadFileFromAgent(pdfFileTargetOnRobot);
            logger.debug("PDF byte array size is {}", pdfAsBytes.length);
            return pdfAsBytes;
        } finally {
            RPA.deleteFileOnAgent(pdfFileTargetOnRobot);
            switchToOrigianalDriver();
        }
    }

    /**
     * @param folder path with folders only of format "folder1/folder2/folder3".
     *               File name with pdf extension will be generated automatically.
     * @return Link to the generated PDF report on S3.
     */
    @Override
    public String generatePdfAndUploadToS3(final String inputWebLink, final String s3Bucket, final String folder) {
        final byte[] pdfAsBytes = generatePdf(inputWebLink);

        final String linkToPdfReportOnS3 = s3Manager.uploadFileToS3(s3Bucket,
                pdfAsBytes,
                generateNameOfNewFile(folder),
                MediaType.APPLICATION_PDF_VALUE);
        logger.debug("Generated PDF report on S3: {}", linkToPdfReportOnS3);
        return linkToPdfReportOnS3;
    }

    /**
     * @return Link to the generated PDF report on S3.
     */
    @Override
    public String generatePdfAndUploadToS3(final String webLink) {
        final String linkToPdfReportOnS3;
        if (s3SettingsAreDefault()) {
            final byte[] pdfAsBytes = generatePdf(webLink);

            linkToPdfReportOnS3 = s3Manager.uploadFileToS3(pdfAsBytes,
                    generateNameOfNewFile(PDF_PRINTER_S3_FOLDER),
                    MediaType.APPLICATION_PDF_VALUE);
        } else {
            final String customS3Bucket = getVariableByKey(S3_BUCKET_KEY);
            final String customS3Flder = getVariableByKey(S3_FOLDER_KEY);
            linkToPdfReportOnS3 = generatePdfAndUploadToS3(webLink, customS3Bucket, customS3Flder);

        }

        logger.debug("Generated PDF report on S3: {}", linkToPdfReportOnS3);
        return linkToPdfReportOnS3;
    }

    private String getVariableByKey(String key) {
        return Optional
                .ofNullable(sys.getVar(key))
                .map(Variable::toString)
                .orElse("");
    }

    private void switchToDesktopDriverIfCurrentIsUniversal(String driverType) {
        if (driverType.equals(BrowserType.UNIVERSAL)) {
            checkUniversalDriver();
            driver().switchDriver(BrowserType.DESKTOP);
        }
    }

    private void validateDriver() {
        if (desktopDriver == null) {
            throw new NullPointerException("Driver is null. Please create instanse of this class inside of desktop driver");
        }

        String driverType = desktopDriver.getDriverInfo().getType();
        if (!(driverType.equals(BrowserType.DESKTOP) || driverType.equals(BrowserType.UNIVERSAL))) {
            throw new IllegalStateException("Open driver type is '" + driverType + "' while '" + BrowserType.DESKTOP + "' or '" + BrowserType.UNIVERSAL + "' driver is needed to proceed");
        }
    }

    private void validateS3Settings() {
        if (sys.isVariableDefined(S3_FOLDER_KEY) && getVariableByKey(S3_BUCKET_KEY).isEmpty()) {
            throw new IllegalArgumentException("Empty s3 bucket was set. Please set both s3 bucket and folder or do not set them at all.");
        }

    }

    private void switchToOrigianalDriver() {
        if (initialDriver.equals(BrowserType.UNIVERSAL)) {
            driver().switchToPreviousDriver();
        }
    }

    private String getLaunchParameters(String webLink, String pdfFileTargetOnRobot) {
        return String.join(" ", webLink, pdfFileTargetOnRobot);

    }



    private boolean s3SettingsAreDefault() {
        return getVariableByKey(S3_FOLDER_KEY).isEmpty() && getVariableByKey(S3_BUCKET_KEY).isEmpty();
    }

    private String generateNameOfNewFile(final String pathForFile) {
        StringBuilder fileName = new StringBuilder();
        final String pathToAppend;
        if (StringUtils.isNotBlank(pathForFile)) {
            pathToAppend = pathForFile + "/";
        } else {
            pathToAppend = "";
        }

        fileName
                .append(pathToAppend)
                .append(UUID.randomUUID().toString())
                .append(".pdf");
        return fileName.toString();
    }

    private boolean jarNotExist(final String filePath) {
        Object res = RPA.driver().executeScript(
                CHECK_THAT_FILE_EXISTS_SCRIPT, GROOVY, ImmutableMap.of(FILE_PATH_KEY, filePath));
        return !(Boolean) res;
    }

    private String uploadLibraryIfMissing() {
        final String filePath = "../external_libs/" + RpaPdfReportGenerator.HTML_2_PDF_PRINTER_JAR_NAME;
        if (jarNotExist(filePath)) {
            return putJarToNode(filePath);
        } else {
            return filePath.replace("\\", "/");
        }
    }

    private String putJarToNode(String filePath) {
        final byte[] bytesJar = s3Manager.getContent(HTML_2_PDF_PRINTER_JAR_NAME);
        return RPA.sendToAgent(bytesJar, filePath).replace("\\", "/");
    }

}
