package org.purl.wf4ever.robundle.manifest;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.purl.wf4ever.robundle.Bundle;
import org.purl.wf4ever.robundle.Bundles;

import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.QuerySolution;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Resource;

public class TestManifest {
    private Bundle bundle;

    @Test
    public void populateFromBundle() throws Exception {
        Path r = bundle.getRoot();
        URI base = r.toUri();

        Manifest manifest = new Manifest(bundle);
        manifest.populateFromBundle();

        List<String> uris = new ArrayList<>();
        for (PathMetadata s : manifest.getAggregates()) {
            uris.add(s.getFile().toString());
            Path path = s.getFile();
            assertNotNull(path.getParent());
            assertEquals(Manifest.withSlash(path.getParent()), s.getFolder());
            if (s.getFile().equals(URI.create("/f/nested/empty/"))) {
                continue;
                // Folder's don't need proxy and createdOn
            }            
            assertEquals("urn", s.getProxy().getScheme());
            UUID.fromString(s.getProxy().getSchemeSpecificPart().replace("uuid:", ""));
            assertEquals(s.getCreatedOn(), Files.getLastModifiedTime(path));
        }
        System.out.println(uris);
        assertFalse(uris.contains("/mimetype"));
        assertFalse(uris.contains("/META-INF"));
        assertTrue(uris.remove("/hello.txt"));
        assertTrue(uris.remove("/f/file1.txt"));
        assertTrue(uris.remove("/f/file2.txt"));
        assertTrue(uris.remove("/f/file3.txt"));
        assertTrue(uris.remove("/f/nested/file1.txt"));
        assertTrue(uris.remove("/f/nested/empty/"));
        assertTrue(uris.isEmpty());
    }

    private Path uri2path(URI base, URI uri) {
        URI fileUri = base.resolve(uri);
        return Paths.get(fileUri);
    }


    @Test
    public void writeAsJsonLD() throws Exception {
        Manifest manifest = new Manifest(bundle);
        manifest.populateFromBundle();
        PathMetadata helloMeta = null;        
        for (PathMetadata meta : manifest.getAggregates()) {            
            if (meta.getFile().endsWith("hello.txt")) {
                helloMeta = meta;
            }
        }
        assertNotNull("No metadata for </hello.txt>", helloMeta);
        
        
        
        Path jsonld = manifest.writeAsJsonLD();
        assertEquals(bundle.getFileSystem().getPath(".ro",  "manifest.json"), jsonld);
        assertTrue(Files.exists(jsonld));
        String manifestStr = new String(Files.readAllBytes(jsonld), "UTF8");
        System.out.println(manifestStr);
        
        // Rough and ready that somethings are there
        // TODO: Read back and check as JSON structure
        // TODO: Check as JSON-LD graph 
        assertTrue(manifestStr.contains("@context"));
        assertTrue(manifestStr.contains("http://purl.org/wf4ever/ro-bundle/context.json"));
        assertTrue(manifestStr.contains("/f/file2.txt"));
        assertTrue(manifestStr.contains("/hello.txt"));
        assertTrue(manifestStr.contains(helloMeta.getProxy().toASCIIString()));
        
        // Parse back as JSON-LD
        try (InputStream jsonIn = Files.newInputStream(jsonld)) {        
            URI baseURI = jsonld.toUri();
            Model model = RDFToManifest.jsonLdAsJenaModel(jsonIn, baseURI);
            model.write(System.out, "TURTLE", baseURI.toString());
            model.write(System.out, "RDF/XML", baseURI.toString());
           
            String queryStr = "PREFIX ore: <http://www.openarchives.org/ore/terms/>" +
            		"PREFIX bundle: <http://purl.org/wf4ever/bundle#>" +
            		"SELECT ?file ?proxy " +
            		"WHERE {" +
            		"    ?ro ore:aggregates ?file ." +
            		"    OPTIONAL { ?file bundle:hasProxy ?proxy . } " +
            		"}";
            Query query = QueryFactory.create(queryStr) ;
            QueryExecution qexec = QueryExecutionFactory.create(query, model);
            
            try {
                ResultSet results = qexec.execSelect();
                int aggregationCount=0;
                for (; results.hasNext(); aggregationCount++) {
                    QuerySolution soln = results.nextSolution();
                    Resource fileRes = soln.getResource("file");
                    Resource proxy = soln.getResource("proxy");
                    Path file = Paths.get(asURI(fileRes));
                    assertTrue(Files.exists(file));
                    PathMetadata meta = manifest.getAggregation(file);                    
                    assertEquals(asURI(proxy), meta.getProxy());
                }
                assertEquals("Could not find all aggregations from manifest: " + manifest.getAggregates(),
                        manifest.getAggregates().size(), aggregationCount);
            } finally {
                // WHY is not QueryExecution an instance of Closable?
                qexec.close();
            }            
        }        
    }
    

    @Test
    public void readManifest() throws Exception {
        Manifest manifest = new Manifest(bundle);
        
        new RDFToManifest().readTo(
                getClass().getResourceAsStream("/manifest.json"), manifest);

        Path r = bundle.getRoot();
        assertNotNull(manifest.getAggregation(r.resolve("/README.txt")));
//        assertEquals("http://example.com/foaf#bob",
//                manifest.getAggregation(r.resolve("/README.txt"))
//                        .getCreatedBy().get(0).getUri());
//        assertEquals("Bob Builder",
//                manifest.getAggregation(r.resolve("/README.txt"))
//                        .getCreatedBy().get(0).getName());
//        assertEquals("text/plain",
//                manifest.getAggregation(r.resolve("/README.txt"))
//                .getMediatype());

        
        assertNull(manifest.getAggregation(r.resolve("/README.txt")).getProxy());
//        assertNotNull(manifest.getAggregation(URI.create("http://example.com/comments.txt")).getProxy());
        
    }
    
    private URI asURI(Resource proxy) {
        if (proxy == null) {
            return null;
        }
        String uri = proxy.getURI();
        if (uri == null) {
            return null;
        }
        return bundle.getRoot().toUri().resolve(uri);
    }

    @Before
    public void exampleBundle() throws IOException {
        Path source;
        try (Bundle bundle = Bundles.createBundle()) {
            source = bundle.getSource();
            Path r = bundle.getRoot();
            Files.createFile(r.resolve("hello.txt"));
            Path f = r.resolve("f");
            Files.createDirectory(f);
            Files.createFile(f.resolve("file3.txt"));
            Files.createFile(f.resolve("file2.txt"));
            Files.createFile(f.resolve("file1.txt"));

            Path nested = f.resolve("nested");
            Files.createDirectory(nested);
            Files.createFile(nested.resolve("file1.txt"));

            Files.createDirectory(nested.resolve("empty"));
            bundle.setDeleteOnClose(false);
        }
        bundle = Bundles.openBundle(source);
    }
    
    @After
    public void closeBundle() throws IOException {
        bundle.close();
        
    }
}

@@ -3187,16 +3187,17 @@
 etPath(%22
+/
 .ro%22,  %22
@@ -3643,31 +3643,21 @@
 http
+s
 ://
-purl
+w3id
 .org/
-wf4ever/ro-
 bund
@@ -3666,21 +3666,16 @@
 /context
-.json
 %22));%0A   

correct @context