#!/usr/bin/env perl

use FindBin qw($Bin);
use Plack::Request;

use strict;
use Test::More;# tests => 37;
use Test::RDF;
use RDF::Trine qw[iri literal blank variable statement];
use Log::Any::Adapter;
use URI::NamespaceMap;
use Module::Load::Conditional qw[can_load];
use URI::Escape;

unless (can_load( modules => { 'RDF::Generator::Void' => 0.04 })) {
  plan skip_all => 'You need RDF::Generator::Void for this test'
}

Log::Any::Adapter->set($ENV{LOG_ADAPTER} || 'Stderr') if $ENV{TEST_VERBOSE};

my $file = $Bin . '/data/fragments.ttl';

use_ok('RDF::LinkedData');
use_ok('RDF::Generator::Void');

my $ns = URI::NamespaceMap->new(['rdf', 'rdfs', 'foaf', 'void', 'dc', 'xsd']);
$ns->add_mapping('hydra' => 'http://www.w3.org/ns/hydra/core#');

my $parser     = RDF::Trine::Parser->new( 'turtle' );
my $rxparser   = RDF::Trine::Parser->new( 'rdfxml' );
my $model = RDF::Trine::Model->temporary_model;
my $base_uri = 'http://localhost';
$parser->parse_file_into_model( $base_uri, $file, $model );

ok($model, "We have a model");

my $ec = {fragments_path => '/fragments'} ;

my $void_subject = iri($base_uri . '/#dataset-0');


{
	note 'Testing the query interface itself';

	my $ld = RDF::LinkedData->new(model => $model,
											base_uri => $base_uri, 
											namespaces_as_vocabularies => 1, 
											void_config => { urispace => 'http://localhost' }, 
											fragments_config => $ec
										  );

	isa_ok($ld, 'RDF::LinkedData');

	$ld->request(Plack::Request->new({}));

	{
		my $response = $ld->response($base_uri . '/fragments?subject=' . uri_escape_utf8('http://localhost/foo'));
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200");
		my $retmodel = return_model($response->content, $parser);
		has_literal('This is a test', 'en', undef, $retmodel, "Test phrase in content");
		pattern_target($retmodel);
		pattern_ok(
					  statement(iri($base_uri . '/foo'),
									iri($ns->rdfs->label),
									literal("This is a test", 'en')),
					  statement(iri($base_uri . '/foo'),
									iri($ns->foaf->page),
									iri('http://en.wikipedia.org/wiki/Foo'))
					  , 'Both triples present',
					 );
		
		pattern_ok(
					  statement(iri($base_uri . '/fragments?subject=' . uri_escape_utf8('http://localhost/foo')),
									iri($ns->void->triples),
									literal("2", undef, iri($ns->xsd->integer))),
					  statement(iri($base_uri . '/fragments?subject=' . uri_escape_utf8('http://localhost/foo')),
									iri($ns->hydra->totalItems),
									literal("2", undef, iri($ns->xsd->integer))),
					  , 'Triple count is correct',
					 );
		
		pattern_ok(	 statement(iri($base_uri . '/fragments?subject=' . uri_escape_utf8('http://localhost/foo')),
									iri($ns->dc->source),
									$void_subject),
					  , 'Void Subject in dc:source'
					 );

		has_subject($void_subject->uri_value, $retmodel, "Void Subject URI in content");

		pattern_ok(
					  statement($void_subject,
									iri($ns->rdf->type),
									iri($ns->hydra->Collection)),
					  statement($void_subject,
									iri($ns->hydra->search),
									blank('template')),
					  statement(blank('template'),
									iri($ns->hydra->template),
									literal($base_uri . '/fragments{?subject,predicate,object}')),
					  statement(blank('template'),
					  				iri($ns->hydra->mapping),
					  				blank('subject')),
					  statement(blank('template'),
					  				iri($ns->hydra->mapping),
					  				blank('predicate')),
					  statement(blank('template'),
					  				iri($ns->hydra->mapping),
					  				blank('predicate')),
					  statement(blank('template'),
					  				iri($ns->hydra->mapping),
					  				blank('object')),
					  statement(blank('subject'),
									iri($ns->hydra->property),
									iri($ns->rdf->subject)),
					  statement(blank('subject'),
									iri($ns->hydra->variable),
									literal('subject')),
					  statement(blank('predicate'),
									iri($ns->hydra->property),
									iri($ns->rdf->predicate)),
					  statement(blank('predicate'),
									iri($ns->hydra->variable),
									literal('predicate')),
					  statement(blank('object'),
									iri($ns->hydra->property),
									iri($ns->rdf->object)),
					  statement(blank('object'),
									iri($ns->hydra->variable),
									literal('object')),
					  "Control statements OK");
   }
	
	{
		my $response = $ld->response($base_uri . '/fragments?predicate=' . uri_escape_utf8('http://www.w3.org/2000/01/rdf-schema#label') . '&object=' . uri_escape_utf8('"Testing with longer URI."@en'));
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200");
		my $retmodel = return_model($response->content, $parser);
		has_literal('Testing with longer URI.', 'en', undef, $retmodel, "Longer test phrase is in content");
		has_literal("1", undef, $ns->xsd->integer->as_string, $retmodel, 'Triple count is correct');
		hasnt_literal('This is a test', 'en', undef, $retmodel, "Test phrase isn't in content");
	}
	{
		my $response = $ld->response($base_uri . '/fragments?object=' . uri_escape_utf8('"42"^^http://www.w3.org/2001/XMLSchema#integer'));
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200");
		my $retmodel = return_model($response->content, $parser);
		has_literal('42', undef, $ns->xsd->integer->as_string, $retmodel, "The Answer is in the content");
		has_literal("1", undef, $ns->xsd->integer->as_string, $retmodel, 'Triple count is correct');
		hasnt_literal('This is a test', 'en', undef, $retmodel, "Test phrase isn't in content");
	}
	{
		my $response = $ld->response($base_uri . '/fragments?predicate=' . uri_escape_utf8('http://www.w3.org/2000/01/rdf-schema#label') . '&object=' . uri_escape_utf8('"Nothing here."'));
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200");
		my $retmodel = return_model($response->content, $parser);
		hasnt_literal('Testing with longer URI.', 'en', undef, $retmodel, "Longer test phrase is in content");
		has_literal("0", undef, $ns->xsd->integer->as_string, $retmodel, 'Triple count is correct');
	}
	{
		my $response = $ld->response($base_uri . '/fragments?predicate=' . uri_escape_utf8('http://www.w3.org/2000/01/rdf-schema#label') . '&subject=');
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200");
		my $retmodel = return_model($response->content, $parser);
		has_subject($base_uri . '/foo', $retmodel, 'Subject 1 is correct');
		has_subject($base_uri . '/bar/baz/bing', $retmodel, 'Subject 2 is correct');
		has_literal("2", undef, $ns->xsd->integer->as_string, $retmodel, 'Triple count is correct');
		has_literal('This is a test', 'en', undef, $retmodel, "Test phrase is in content");
	}
	{
		my $response = $ld->response($base_uri . '/fragments?subject=&predicate=&object=');	
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200 with all parameters empty");
	}
	{
		my $response = $ld->response($base_uri . '/fragments');	
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200 with all parameters missing");
		my $retmodel = return_model($response->content, $parser);
		has_predicate('http://www.w3.org/ns/hydra/core#next', $retmodel, 'Has hydra:next predicate');
		has_object_uri($base_uri . '/fragments?allow_dump_dataset=1', $retmodel, '...and object with ? to find the rest');
	}
	{
		my $response = $ld->response($base_uri . '/fragments?predicate=&object=');	
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200 with subject missing other parameters empty");
		my $retmodel = return_model($response->content, $parser);
		has_predicate('http://www.w3.org/ns/hydra/core#next', $retmodel, 'Has hydra:next predicate');
		has_object_uri($base_uri . '/fragments?allow_dump_dataset=1', $retmodel, '...and object with & to find the rest');
	}
}


{
	note 'Testing the Void for fragments';
	
	my $ld = RDF::LinkedData->new(model => $model, base_uri=>$base_uri, 
											fragments_config => $ec, 
											void_config => { urispace => 'http://localhost' });
	isa_ok($ld, 'RDF::LinkedData');
	
	$ld->request(Plack::Request->new({}));
	my $response = $ld->response($base_uri . '/');
	isa_ok($response, 'Plack::Response');
	is($response->status, 200, "Returns 200");
	my $retmodel = return_model($response->content, $parser);
	has_subject($void_subject->uri_value, $retmodel, "Subject URI in content");
	has_predicate($ns->hydra->search->as_string, $retmodel, 'Hydra search predicate');
	pattern_target($retmodel);
	pattern_ok(
				  statement(
								$void_subject,
								iri($ns->void->triples),
								literal(4, undef, iri($ns->xsd->integer))
							  ),
				  statement(
								$void_subject,
								iri($ns->rdf->type),
								iri($ns->void->Dataset)
							  ),
				  'VoID-specific statements');
	pattern_ok(
				  statement($void_subject,
								iri($ns->rdf->type),
								iri($ns->hydra->Collection)),
				  statement($void_subject,
								iri($ns->hydra->search),
								blank('template')),
				  statement(blank('template'),
								iri($ns->hydra->template),
								literal($base_uri . '/fragments{?subject,predicate,object}')),
				  statement(blank('template'),
				                iri($ns->hydra->mapping),
					  		    blank('subject')),
				  statement(blank('template'),
					  			iri($ns->hydra->mapping),
					  			blank('predicate')),
				  statement(blank('template'),
				                iri($ns->hydra->mapping),
					  		    blank('predicate')),
	              statement(blank('template'),
					            iri($ns->hydra->mapping),
					  		    blank('object')),
				  statement(blank('subject'),
								iri($ns->hydra->property),
								iri($ns->rdf->subject)),
				  statement(blank('subject'),
								iri($ns->hydra->variable),
								literal('subject')),
				  statement(blank('predicate'),
								iri($ns->hydra->property),
								iri($ns->rdf->predicate)),
				  statement(blank('predicate'),
								iri($ns->hydra->variable),
								literal('predicate')),
				  statement(blank('object'),
								iri($ns->hydra->property),
								iri($ns->rdf->object)),
				  statement(blank('object'),
								iri($ns->hydra->variable),
								literal('object')),
				  "Control statements OK");
}

{
	note 'Testing the allow_dump_dataset feature with config param';

	my $ld = RDF::LinkedData->new(model => $model,
											base_uri => $base_uri, 
											namespaces_as_vocabularies => 1, 
											void_config => { urispace => 'http://localhost' }, 
											fragments_config => { %$ec , allow_dump_dataset => 1 }
										  );

	isa_ok($ld, 'RDF::LinkedData');

	$ld->request(Plack::Request->new({}));

	{
		my $response = $ld->response($base_uri . '/fragments?subject=&predicate=&object=');	
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200 with all parameters empty");
		my $retmodel = return_model($response->content, $parser);
		has_literal("4", undef, $ns->xsd->integer->as_string, $retmodel, 'Triple count is correct got all 4 triples');
	}
}

{
	note 'Testing the allow_dump_dataset feature with hypermedia';

	my $ld = RDF::LinkedData->new(model => $model,
											base_uri => $base_uri, 
											namespaces_as_vocabularies => 1, 
											void_config => { urispace => 'http://localhost' }, 
											fragments_config => { %$ec }
										  );

	isa_ok($ld, 'RDF::LinkedData');

	$ld->request(Plack::Request->new({}));

	{
		my $response = $ld->response($base_uri . '/fragments?subject=&predicate=&object=');	
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200 with all parameters empty");
		my $retmodel1 = return_model($response->content, $parser);
		has_literal("4", undef, $ns->xsd->integer->as_string, $retmodel1, 'Triple count is correct got all 4 triples');
		my $size1 = $retmodel1->size;
		is($size1, 20, 'Returned triples contain only controls and metadata');
		has_predicate('http://www.w3.org/ns/hydra/core#next', $retmodel1, 'Has hydra:next predicate');
		has_object_uri($base_uri . '/fragments?allow_dump_dataset=1', $retmodel1, '...and object to find the rest');
		my $response2 = $ld->response($base_uri . '/fragments?allow_dump_dataset=1');	
		isa_ok($response, 'Plack::Response');
		is($response->status, 200, "Returns 200 with all parameters empty");
		my $retmodel2 = return_model($response2->content, $parser);
		has_literal("4", undef, $ns->xsd->integer->as_string, $retmodel2, 'Triple count is correct got all 4 triples');
		cmp_ok($size1 + 4 - 1 , '==', $retmodel2->size, 'Size is now three more (+data, -hydra:next)');
		cmp_ok($size1, '<', $retmodel2->size, 'Size is now larger');
		hasnt_uri('http://www.w3.org/ns/hydra/core#next', $retmodel2, 'Hasnt hydra:next predicate');
	}
}




sub return_model {
	my ($content, $parser) = @_;
	my $retmodel = RDF::Trine::Model->temporary_model;
	$parser->parse_into_model( $base_uri, $content, $retmodel );
	return $retmodel;
}


done_testing;
