package org.gcube.application.geoportal.clients;

import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.gcube.application.cms.tests.TestDocuments;
import org.gcube.application.cms.tests.Tests;
import org.gcube.application.geoportal.client.utils.Serialization;
import org.gcube.application.geoportal.common.faults.InvalidRequestException;
import org.gcube.application.geoportal.common.model.JSONPathWrapper;
import org.gcube.application.geoportal.common.model.document.Project;
import org.gcube.application.geoportal.common.model.document.access.Access;
import org.gcube.application.geoportal.common.model.document.access.AccessPolicy;
import org.gcube.application.geoportal.common.model.document.filesets.RegisteredFileSet;
import org.gcube.application.geoportal.common.model.document.relationships.RelationshipNavigationObject;
import org.gcube.application.geoportal.common.model.rest.CreateRelationshipRequest;
import org.gcube.application.geoportal.common.model.rest.DeleteRelationshipRequest;
import org.gcube.application.geoportal.common.model.rest.RegisterFileSetRequest;
import org.gcube.application.geoportal.common.model.rest.StepExecutionRequest;
import org.gcube.application.geoportal.common.model.useCaseDescriptor.Field;
import org.gcube.application.geoportal.common.model.useCaseDescriptor.HandlerDeclaration;
import org.gcube.application.geoportal.common.model.useCaseDescriptor.UseCaseDescriptor;
import org.gcube.application.geoportal.common.rest.Projects;
import org.gcube.application.geoportal.common.utils.FileSets;
import org.gcube.application.geoportal.common.utils.Files;
import org.gcube.application.geoportal.common.utils.StorageUtils;
import org.gcube.application.geoportal.common.utils.tests.GCubeTest;
import org.junit.Test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.rmi.RemoteException;
import java.util.Iterator;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;

@Slf4j
public class ProfiledConcessioniTest <M extends Project,C extends Projects<M>> extends GenericUseCases{

    @Data
    private static class MyClass {
        private String field;
    }

    @Override
    protected String getUCID() {
        return "profiledConcessioni";
    }

    public void test(){
        UseCaseDescriptor p=null;
        HandlerDeclaration h = p.getHandlersMapByID().get("MyID").get(0);
        Document doc = h.getConfiguration();
        System.out.println(Serialization.convert(doc, MyClass.class));
    }

    @Test
    public void performSubmit() throws RemoteException, JsonProcessingException, FileNotFoundException, InvalidRequestException {

        Projects<Project> client = getClient();
        Project p=client.createNew(getNewDocument());

        p=prepareWithFileSet(p);

        p = getClient().performStep(p.getId(), new StepExecutionRequest("SUBMIT-FOR-REVIEW",new Document()));
        System.out.println("Result is "+Serialization.write(p));
    }


    @Override
    protected Document getNewDocument() {
        try {
            String json = Files.readFileAsString(TestDocuments.BASE_FOLDER + "/basicConcessioneDocument.json", Charset.defaultCharset());
            return Document.parse(json);
        }catch (Throwable t){
            throw new RuntimeException(t);
        }
    }

    @Override
    protected Project prepareWithFileSet(Project project) throws FileNotFoundException, InvalidRequestException, RemoteException {

        Projects<Project> client = getClient();

        // Set relazione


        Access toSetAccess = new Access();
        toSetAccess.setPolicy(AccessPolicy.RESTRICTED);
        toSetAccess.setLicense("CC0-1.0");

        RegisterFileSetRequest request=FileSets.
                prepareRequest(new StorageUtils(),
                        "$.relazioneScavo","fileset",
                        "$.relazioneScavo."+Field.CHILDREN+"[?(@.fileset)]",
                        new File(Tests.FOLDER_CONCESSIONI,"relazione.pdf"));
        request.setToSetAccess(toSetAccess);

        project=client.registerFileSet(project.getId(), request);

        String jsonDoc=project.getTheDocument().toJson();
        log.info("Registered fileset. Result is {}", jsonDoc);
        RegisteredFileSet f= Serialization.convert(new JSONPathWrapper(jsonDoc).getByPath(
                request.getParentPath()+"."+request.getFieldName()).get(0),RegisteredFileSet.class);

        assertEquals(Serialization.convert(f.getAccess(),Access.class).getPolicy(),toSetAccess.getPolicy());
        assertEquals(Serialization.convert(f.getAccess(),Access.class).getLicense(),toSetAccess.getLicense());


        // Set Abstract

        client.registerFileSet(project.getId(), FileSets.
                prepareRequest(new StorageUtils(),
                        "$.abstractRelazione","filesetIta",
                        "$.abstractRelazione."+Field.CHILDREN+"[?(@.filesetIta)]",
                        new File(Tests.FOLDER_CONCESSIONI,"relazione.pdf")));


        // Set pos

        RegisterFileSetRequest req = FileSets.
                prepareRequest(new StorageUtils(),
                        "$.posizionamentoScavo","fileset",
                        "$.posizionamentoScavo."+Field.CHILDREN+"[?(@.fileset)]",
                        new File(Tests.FOLDER_CONCESSIONI,"pos.shx"),
                        new File(Tests.FOLDER_CONCESSIONI,"pos.shp"));

        String toTestStrangeName = "e. gna_topografia_timponedellamotta";
        //String toTestStrangeName = "1. my: Strange name *pos";

        req.getStreams().forEach(t ->t.setFilename(t.getFilename().replace("pos",toTestStrangeName)));

        client.registerFileSet(project.getId(), req);


        // Add 2 imgs

        for(int i=0;i<2;i++){
            client.registerFileSet(project.getId(), FileSets.
                    prepareRequest(new StorageUtils(),
                            "$.immaginiRappresentative["+i+"]","fileset",
                            "$.immaginiRappresentative."+Field.CHILDREN+"[?(@.fileset)]",
                            new File(Tests.FOLDER_CONCESSIONI,"immagine"+(i+1)+".png")));
        }


        // Add 2 piante
        for(int i=0;i<2;i++){
            client.registerFileSet(project.getId(), FileSets.
                    prepareRequest(new StorageUtils(),
                            "$.pianteFineScavo["+i+"]","fileset",
                            "$.pianteFineScavo."+Field.CHILDREN+"[?(@.fileset)]",
                            new File(Tests.FOLDER_CONCESSIONI,"pianta.shp"),
                            new File(Tests.FOLDER_CONCESSIONI,"pianta.shx")));
        }

        return client.getById(project.getId());
    }

    @Test
    public void testRelationships() throws Exception{
        assumeTrue(GCubeTest.isTestInfrastructureEnabled());
        Projects<M> client = (Projects<M>) getClient();
        Project source = client.createNew(new Document().append("field","value"));
        Project target = client.createNew(new Document().append("field2","value"));

        String relId="follows";


        Project linkedSource = client.setRelation(
                new CreateRelationshipRequest(source.getId(),relId,
                        target.getId(), null));

        assertTrue(linkedSource.getRelationships()!=null);
        assertTrue(!linkedSource.getRelationships().isEmpty());
        assertTrue(linkedSource.getRelationships().get(0).getRelationshipName().equals(relId));
        assertTrue(linkedSource.getRelationships().get(0).getTargetID().equals(target.getId()));
        assertTrue(linkedSource.getRelationships().get(0).getTargetUCD().equals(target.getProfileID()));

        Iterator<RelationshipNavigationObject> it = client.getRelationshipChain(source.getId(),relId,true);
        System.out.println("Scanning rel ..");
        it.forEachRemaining(r -> {
            System.out.println(r.getTarget().getId());
            System.out.println("Children size : "+r.getChildren().size());
        });


        Project unLinkedSource = client.deleteRelation(
                new DeleteRelationshipRequest(source.getId(),relId,
                        target.getId(),null));


        assertTrue(unLinkedSource.getRelationships()==null || unLinkedSource.getRelationships().isEmpty());

    }
}
