package com.socrata.datasync.validation;

import com.socrata.datasync.PublishMethod;
import com.socrata.datasync.SocrataConnectionInfo;
import com.socrata.datasync.Utils;
import com.socrata.datasync.config.CommandLineOptions;
import com.socrata.datasync.job.GISJob;
import com.socrata.datasync.job.JobStatus;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.io.FilenameUtils;

import java.io.File;
import java.util.*;

public class GISJobValidity {
    public static final String GEOJSON_EXT = "geojson";
    public static final String ZIP_EXT = "zip";
    public static final String KML_EXT = "kml";
    public static final String KMZ_EXT = "kmz";
    public static final List<String> allowedGeoFileToPublishExtensions = Arrays.asList(GEOJSON_EXT, ZIP_EXT, KML_EXT, KMZ_EXT);

    /**
     * Checks that the command line arguments are sensible
     * NB: it is expected that this is run before 'configure'.
     * @param cmd the commandLine object constructed from the user's options
     * @return true if the commandLine is approved, false otherwise
     */
    public static boolean validateArgs(CommandLine cmd) {
        return validateDatasetIdArg(cmd) &&
            validateFileToPublishArg(cmd) &&
            validatePublishMethodArg(cmd) &&
            validateProxyArgs(cmd);
    }

    /**
     * @return an error JobStatus if any input is invalid, otherwise JobStatus.VALID
     */
    public static JobStatus validateJobParams(SocrataConnectionInfo connectionInfo, GISJob job) {
        if (connectionInfo.getUrl().equals("") || connectionInfo.getUrl().equals("https://")) {
            return JobStatus.INVALID_DOMAIN;
        }

        if (!Utils.uidIsValid(job.getDatasetID())) {
            return JobStatus.INVALID_DATASET_ID;
        }

        String fileToPublish = job.getFileToPublish();
        if (fileToPublish.equals("")) {
            return JobStatus.MISSING_FILE_TO_PUBLISH;
        }

        File publishFile = new File(fileToPublish);
        if (!publishFile.exists() || publishFile.isDirectory()) {
            JobStatus errorStatus = JobStatus.FILE_TO_PUBLISH_DOESNT_EXIST;
            errorStatus.setMessage(fileToPublish + ": File to publish does not exist");
            return errorStatus;
        }

        String fileExtension = FilenameUtils.getExtension(fileToPublish);
        if (!allowedGeoFileToPublishExtensions.contains(fileExtension)) {
            return JobStatus.FILE_TO_PUBLISH_INVALID_GIS_FORMAT;
        }

        return JobStatus.VALID;
    }

    /**
     * This method attempts to map layers in the existing dataset to layers found in a shapefile,
     * so that we replace existing layers where possible instead of creating new ones and changing
     * the 4x4 / API endpoint of the dataset.
     */
    public static JobStatus validateLayerMapping(GISJob job) {
        try {
            job.initializeLayerMapping();
            return JobStatus.VALID;
        } catch (IllegalArgumentException e) {
            // This means getDatasetInfo was unable to parse the response into a GeoDataset object.
            return JobStatus.NOT_A_GEO_DATASET;
        } catch (Exception e) {
            // If something unexpected went wrong,
            // Throw it so it can be debugged
            // (The rest of Datasync seems to like to swallow exceptions)
            throw new RuntimeException(e);
        }
    }

    private static boolean validateFileToPublishArg(CommandLine cmd) {
        if (cmd.getOptionValue(CommandLineOptions.FILE_TO_PUBLISH_FLAG) != null) {
            return true;
        } else {
            System.err.println("Missing required argument: -f,--" + CommandLineOptions.FILE_TO_PUBLISH_FLAG + " is required");
            return false;
        }
    }

    private static boolean validateDatasetIdArg(CommandLine cmd) {
        if (cmd.getOptionValue(CommandLineOptions.DATASET_ID_FLAG) != null) {
            return true;
        } else {
            System.err.println("Missing required argument: -i,--" + CommandLineOptions.DATASET_ID_FLAG + " is required");

            return false;
        }
    }

    private static boolean validatePublishMethodArg(CommandLine cmd) {
        String method = cmd.getOptionValue("m");
        String publishingWithDi2 = cmd.getOptionValue(CommandLineOptions.PUBLISH_VIA_DI2_FLAG);
        String publishingWithFtp = cmd.getOptionValue(CommandLineOptions.PUBLISH_VIA_FTP_FLAG);
        String controlFilePath = cmd.getOptionValue(CommandLineOptions.PATH_TO_CONTROL_FILE_FLAG);

        if (method == null && controlFilePath == null
            && isNullOrFalse(publishingWithFtp) && isNullOrFalse(publishingWithDi2)) {

            System.err.println("Missing required argument: -m,--" +
                    CommandLineOptions.PUBLISH_METHOD_FLAG + " is required");

            return false;
        } else if (method == null && controlFilePath == null
                   && (!isNullOrFalse(publishingWithFtp) || !isNullOrFalse(publishingWithDi2))) {
            // if publishing via ftp or di2/http, we want to err about the control file, not the method arg

            return true;
        } else if (method == null && controlFilePath != null) {
            // have a control file, don't need the method arg (and would ignore it anyway)

            return true;
        } else { // method != null
            boolean publishMethodValid = false;

            for (PublishMethod m : PublishMethod.values()) {
                if (m.name().equalsIgnoreCase(method))
                    publishMethodValid = true;
            }

            if (!publishMethodValid) {
                System.err.println("Invalid argument: -m,--" + CommandLineOptions.PUBLISH_METHOD_FLAG + " must be " +
                                   Arrays.toString(PublishMethod.values()));
                return false;
            }

            return true;
        }
    }

    private static boolean validateProxyArgs(CommandLine cmd) {
        String username = cmd.getOptionValue(CommandLineOptions.PROXY_USERNAME_FLAG);
        String password = cmd.getOptionValue(CommandLineOptions.PROXY_PASSWORD_FLAG);
        if(username == null && password != null) {
            System.err.println("Missing required argument: -pun,--" + CommandLineOptions.PROXY_USERNAME_FLAG + " is required if" +
                               " supplying proxy credentials with -ppw, --" + CommandLineOptions.PROXY_PASSWORD_FLAG);
            return false;
        } else if(username != null && password == null) {
            System.err.println("Missing required argument: -ppw,--" + CommandLineOptions.PROXY_PASSWORD_FLAG + " is required if" +
                               " supplying proxy credentials with -pun, --" + CommandLineOptions.PROXY_USERNAME_FLAG);
            return false;
        }
        return true;
    }

    private static boolean isNullOrFalse(String s) {
        return s == null || s.equalsIgnoreCase("false");
    }
}