package io.lumify.web.routes.vertex;

import com.google.common.io.Files;
import com.google.inject.Inject;
import io.lumify.core.config.Configuration;
import io.lumify.core.ingest.FileImport;
import io.lumify.core.model.user.UserRepository;
import io.lumify.core.model.workspace.Workspace;
import io.lumify.core.model.workspace.WorkspaceRepository;
import io.lumify.core.user.User;
import io.lumify.core.util.LumifyLogger;
import io.lumify.core.util.LumifyLoggerFactory;
import io.lumify.miniweb.HandlerChain;
import io.lumify.web.BaseRequestHandler;
import io.lumify.web.clientapi.model.ClientApiArtifactImportResponse;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ParameterParser;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.securegraph.Authorizations;
import org.securegraph.Graph;
import org.securegraph.Vertex;
import org.securegraph.Visibility;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class VertexImport extends BaseRequestHandler {
    private static final LumifyLogger LOGGER = LumifyLoggerFactory.getLogger(VertexImport.class);

    private static final String PARAMS_FILENAME = "filename";
    private static final String UNKNOWN_FILENAME = "unknown_filename";
    private final Graph graph;

    private final FileImport fileImport;

    @Inject
    public VertexImport(
            final Graph graph,
            final FileImport fileImport,
            final UserRepository userRepository,
            final WorkspaceRepository workspaceRepository,
            final Configuration configuration) {
        super(userRepository, workspaceRepository, configuration);
        this.graph = graph;
        this.fileImport = fileImport;
    }

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, HandlerChain chain) throws Exception {
        if (!ServletFileUpload.isMultipartContent(request)) {
            LOGGER.warn("Could not process request without multi-part content");
            respondWithBadRequest(response, "file", "Could not process request without multi-part content");
            return;
        }

        User user = getUser(request);
        Authorizations authorizations = getAuthorizations(request, user);
        String workspaceId = getActiveWorkspaceId(request);
        File tempDir = Files.createTempDir();
        try {
            List<FileImport.FileAndVisibility> files = getFileAndVisibilities(request, response, chain, tempDir, authorizations, user);
            if (files == null) {
                return;
            }

            Workspace workspace = getWorkspaceRepository().findById(workspaceId, user);

            List<Vertex> vertices = fileImport.importVertices(workspace, files, user, authorizations);

            respondWithClientApiObject(response, toArtifactImportResponse(vertices));
        } finally {
            FileUtils.deleteDirectory(tempDir);
        }
    }

    private ClientApiArtifactImportResponse toArtifactImportResponse(List<Vertex> vertices) {
        ClientApiArtifactImportResponse response = new ClientApiArtifactImportResponse();
        for (Vertex vertex : vertices) {
            response.getVertexIds().add(vertex.getId());
        }
        return response;
    }

    private List<FileImport.FileAndVisibility> getFileAndVisibilities(HttpServletRequest request, HttpServletResponse response, HandlerChain chain, File tempDir, Authorizations authorizations, User user) throws Exception {
        List<String> invalidVisibilities = new ArrayList<String>();
        List<FileImport.FileAndVisibility> files = new ArrayList<FileImport.FileAndVisibility>();
        int visibilitySourceIndex = 0;
        int fileIndex = 0;
        for (Part part : request.getParts()) {
            if (part.getName().equals("file")) {
                String fileName = getFilename(part);
                File outFile = new File(tempDir, fileName);
                copyPartToFile(part, outFile);
                addFileToFilesList(files, fileIndex++, outFile);
            } else if (part.getName().equals("visibilitySource")) {
                String visibilitySource = IOUtils.toString(part.getInputStream(), "UTF8");
                if (!graph.isVisibilityValid(new Visibility(visibilitySource), authorizations)) {
                    invalidVisibilities.add(visibilitySource);
                }
                addVisibilityToFilesList(files, visibilitySourceIndex++, visibilitySource);
            }
        }

        if (invalidVisibilities.size() > 0) {
            LOGGER.warn("%s is not a valid visibility for %s user", invalidVisibilities.toString(), user.getDisplayName());
            respondWithBadRequest(response, "visibilitySource", getString(request, "visibility.invalid"), invalidVisibilities);
            chain.next(request, response);
            return null;
        }

        return files;
    }

    private void addVisibilityToFilesList(List<FileImport.FileAndVisibility> files, int index, String visibilitySource) {
        ensureFilesSize(files, index);
        files.get(index).setVisibilitySource(visibilitySource);
    }

    private void addFileToFilesList(List<FileImport.FileAndVisibility> files, int index, File file) {
        ensureFilesSize(files, index);
        files.get(index).setFile(file);
    }

    private void ensureFilesSize(List<FileImport.FileAndVisibility> files, int index) {
        while (files.size() <= index) {
            files.add(new FileImport.FileAndVisibility());
        }
    }

    private static String getFilename(Part part) {
        String fileName = UNKNOWN_FILENAME;

        final ParameterParser parser = new ParameterParser();
        parser.setLowerCaseNames(true);

        final Map params = parser.parse(part.getHeader(FileUploadBase.CONTENT_DISPOSITION), ';');
        if (params.containsKey(PARAMS_FILENAME)) {
            final String name = (String) params.get(PARAMS_FILENAME);
            if (name != null) {
                fileName = name.trim();
            }
        }

        return fileName;
    }
}

@@ -1144,16 +1144,89 @@
 o.File;%0A
+import java.io.UnsupportedEncodingException;%0Aimport java.net.URLDecoder;%0A
 import j
@@ -1241,24 +1241,24 @@
 .ArrayList;%0A
-
 import java.
@@ -3883,22 +3883,16 @@
 rayList%3C
-String
 %3E();%0A   
@@ -3957,36 +3957,8 @@
 ist%3C
-FileImport.FileAndVisibility
 %3E();
@@ -6272,32 +6272,260 @@
 name != null) %7B%0A
+                try %7B%0A                    fileName = URLDecoder.decode(name, %22utf8%22).trim();%0A                %7D catch (UnsupportedEncodingException ex) %7B%0A                    LOGGER.error(%22Failed to url decode: %22 + name, ex);%0A    
                 
@@ -6540,32 +6540,50 @@
  = name.trim();%0A
+                %7D%0A
             %7D%0A  

fix upload filenames with url encoded data in them (see #566)