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

import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import java.util.UUID;
import javax.inject.Inject;

import com.freedomoss.crowdcontrol.webharvest.ProxySettingsDto;
import com.freedomoss.crowdcontrol.webharvest.WebHarvestTaskItem;
import com.google.common.collect.ImmutableMap;
import groovy.lang.Binding;
import org.apache.commons.io.IOUtils;
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.18.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;

    /*Variables that are received from WebHarvest context*/
    private final String proxyHost;
    private final String proxyPort;
    private final String proxyUserName;
    private final String proxyPassword;

    @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();

        final Optional<ProxySettingsDto> proxySettings = getProxySettingsDto();

        this.proxyHost = proxySettings.map(ProxySettingsDto::getServer).orElse(null);
        this.proxyPort = proxySettings.map(ProxySettingsDto::getPort).map(Object::toString).orElse(null);
        this.proxyUserName = proxySettings.map(ProxySettingsDto::getUsername).orElse(null);
        this.proxyPassword = proxySettings.map(ProxySettingsDto::getPassword).map(Object::toString).orElse(null);

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

        switchToDesktopDriverIfCurrentIsUniversal(driverType);
    }

    private Optional<ProxySettingsDto> getProxySettingsDto() {
        final WebHarvestTaskItem item = (WebHarvestTaskItem)sys.getVar("item").getWrappedObject();
        return Optional.ofNullable(item.getProxySettings());
    }


    @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 byte[] pdfAsBytes = generatePdf(webLink);

        final String linkToPdfReportOnS3 ;

        if(s3SettingsAreDefault()){
            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 isJarExist(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 (!isJarExist(filePath)) {
            final String jarLocationOnS3 = s3Manager.getS3FileURL(HTML_2_PDF_PRINTER_JAR_NAME);
            try (final InputStream jar = s3Manager.getFile(jarLocationOnS3)) {
                final byte[] bytesJar = IOUtils.toByteArray(jar);
                return RPA.sendToAgent(bytesJar, filePath).replace("\\", "/");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            return filePath.replace("\\", "/");
        }
    }
}
