package org.gcube.application.geoportal.common.model;

import com.jayway.jsonpath.JsonPath;
import org.gcube.application.geoportal.common.model.document.Project;
import org.gcube.application.geoportal.common.model.document.filesets.RegisteredFileSet;
import org.gcube.application.geoportal.common.model.document.lifecycle.TriggeredEvents;
import org.gcube.application.geoportal.common.model.useCaseDescriptor.HandlerDeclaration;
import org.gcube.application.geoportal.common.utils.Files;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static junit.framework.TestCase.*;

public class JSONPathTests {


    static File baseFolder=new File("../test-data/profiledDocuments");

    private JSONPathWrapper profileNavigator() throws IOException {
        return getNavigator("profiles/basicProfile.json");
    }

    private JSONPathWrapper getNavigator(String filename) throws IOException {
        return new JSONPathWrapper(Files.readFileAsString(new File(baseFolder, filename).getAbsolutePath(), Charset.defaultCharset()));
    }

    private Project getDocument(String filename){
          return null;
    }


    @Test
    public void readElements() throws IOException {
        JSONPathWrapper documentNavigator=getNavigator("profiledConcessioniExample.json");

        // TODO REVERVED KEYS

        System.out.println("All Registered FileNames"+ documentNavigator.getByPath("$..fileset.payloads[*].name"));

        System.out.println(documentNavigator.getByPath("$..fileset."+ RegisteredFileSet.MATERIALIZATIONS));

        // WMS
        System.out.println(documentNavigator.getByPath("$..wms"));


        System.out.println(documentNavigator.getMatchingPaths("$..[?(@." + RegisteredFileSet.PAYLOADS + ")]"));

    }


    @Test
    public void writeTest() throws IOException {
        JSONPathWrapper wrapper =getNavigator("emptyProfiledDocument.json");
        checkSetValue(wrapper,"firstField","value1");

        checkSetValue(wrapper,"$.firstField","value");
        checkSetValue(wrapper,"$..event","fakeEvent");
        checkSetValue(wrapper,"$['firstField']","value");


        TriggeredEvents event=new TriggeredEvents();
        event.setEvent("newEvent");
        checkAddElement(wrapper,"$..triggeredEvents",event);


        wrapper=getNavigator("profiles/basicProfile.json");
        checkPutElement(wrapper,"$..[?(@."+HandlerDeclaration.ID+" == 'DEFAULT-SINGLE-PHASE')]."+ HandlerDeclaration.CONFIGURATION,"additional",
                Collections.singletonMap("myKey","myField"));

    }

    private void checkPutElement(JSONPathWrapper wrapper, String path, String elementName, Object value){
        wrapper.putElement(path,elementName,value);
        System.out.println("JSON is "+wrapper.getValueCTX().jsonString());
        List<Map> foundElements= wrapper.getByPath(path,Map.class);

        assertTrue(foundElements!=null);
        assertTrue(!foundElements.isEmpty());
        foundElements.forEach(d->{
            assertTrue(d.containsKey(elementName));
            assertTrue(d.get(elementName).equals(value));
        });
    }

    private void checkSetValue(JSONPathWrapper wrapper, String element, Object value){
        wrapper.setElement(element,value);
        System.out.println("JSON is "+wrapper.getValueCTX().jsonString());
        List<Object> foundElements= wrapper.getByPath(element);

        assertTrue(foundElements!=null);
        assertTrue(!foundElements.isEmpty());
        assertTrue(foundElements.get(0).toString().equals(value));

    }

    private void checkAddElement(JSONPathWrapper wrapper, String element, Object value){
        int sizeBefore = wrapper.getByPath(element,List.class).get(0).size();

        wrapper.addElementToArray(element,value);
        System.out.println("JSON is "+wrapper.getValueCTX().jsonString());
        List<Object> foundElements= wrapper.getByPath(element,List.class).get(0);

        assertTrue(foundElements!=null);
        foundElements.removeIf(f->f==null);
        assertTrue(!foundElements.isEmpty());
        assertTrue(foundElements.size()==sizeBefore+1);
    }

    @Test
    public void testTokenizer(){
        assertTrue(JSONPathWrapper.tokenizePath("$.firstField").size()==1);
        assertTrue(JSONPathWrapper.tokenizePath("$.firstField.child").size()==2);
        assertTrue(JSONPathWrapper.tokenizePath("$.firstField[3].child").size()==2);
        assertTrue(JSONPathWrapper.tokenizePath("$.book[?(@.isbn)].child").size()==2);
        assertTrue(JSONPathWrapper.tokenizePath("$.store.book[*].author").size()==3);


        assertTrue(JSONPathWrapper.tokenizePath("$['firstField']").size()==1);
        assertTrue(JSONPathWrapper.tokenizePath("$['firstField']['child']").size()==2);
        assertTrue(JSONPathWrapper.tokenizePath("$['firstField'][3]['child']").size()==2);
        assertTrue(JSONPathWrapper.tokenizePath("$['book'][?(@.isbn)]['child']").size()==2);
        assertTrue(JSONPathWrapper.tokenizePath("$['store'].['book'][*].['author']").size()==3);
    }


    @Test
    public void testGetFieldNameFromPathElement(){
        assertEquals(JSONPathWrapper.extractFieldNameFromPathElement("$.firstField"),"firstField");
        assertEquals(JSONPathWrapper.extractFieldNameFromPathElement("firstField"),"firstField");
        assertEquals(JSONPathWrapper.extractFieldNameFromPathElement("$.firstField[3]"),"firstField");
        assertEquals(JSONPathWrapper.extractFieldNameFromPathElement("$['book'][?(@.isbn)]"),"book");
    }

    @Test
    public void testDefinitePath(){
        assertTrue(JsonPath.isPathDefinite("$.firstField"));
        assertTrue(JsonPath.isPathDefinite("$.firstField.child"));
        assertTrue(JsonPath.isPathDefinite("$.firstField[3].child"));
        assertFalse(JsonPath.isPathDefinite("$.book[?(@.isbn)].child"));
        assertFalse(JsonPath.isPathDefinite("$.store.book[*].author"));


        assertTrue(JsonPath.isPathDefinite("$['firstField']"));
        assertTrue(JsonPath.isPathDefinite("$['firstField']['child']"));
        assertTrue(JsonPath.isPathDefinite("$['firstField'][3]['child']"));
        assertFalse(JsonPath.isPathDefinite("$['book'][?(@.isbn)]['child']"));
        assertFalse(JsonPath.isPathDefinite("$['store'].['book'][*].['author']"));
    }
}
