<?php

use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Facades\Config;

use CorpusParole\Repositories\DocumentRepository;
use CorpusParole\Repositories\RdfDocumentRepository;
use CorpusParole\Models\Document;
use CorpusParole\Models\DocumentResult;


use SebastianBergmann\Diff\Differ;

use GuzzleHttp\Client;

class DocumentRepositoryIntegrationTest extends TestCase {

    const REPO_CREATION_TTL = __DIR__.'/files/DocumentRepositoryIntegrationTest/repo_creation.ttl';

    const TEST_DOC = __DIR__.'/files/DocumentRepositoryIntegrationTest/test_doc.ttl';
    const TEST_DOC_NO_GEO = __DIR__.'/files/DocumentRepositoryIntegrationTest/test_doc_no_geo.ttl';

    function __construct(string $name = null) {
        parent::__construct($name);
    }

    public function setUp() {
        parent::setUp();
        $this->graph = new EasyRdf\Graph(config('corpusparole.corpus_doc_id_base_uri')."crdo-ALA_738", sprintf(file_get_contents(DocumentRepositoryIntegrationTest::TEST_DOC), config('corpusparole.corpus_doc_id_base_uri')));

        $this->httpClient = new Client(['base_uri' => config('corpusparole.rdf4j_base_url')]);
        $this->rdf4jRepository = config('corpusparole.rdf4j_repository');
        $this->corpusDocIdBaseUri = config('corpusparole.corpus_doc_id_base_uri');

        $this->documentRepository = $this->app->make('CorpusParole\Repositories\DocumentRepository');
        $uniqueid = uniqid('corpusparole', true);
        $repoCreateStmt = sprintf(file_get_contents(DocumentRepositoryIntegrationTest::REPO_CREATION_TTL), $this->rdf4jRepository);
        $this->httpClient->delete("repositories/$this->rdf4jRepository", ['http_errors' => false]);
        $this->httpClient->post('repositories/SYSTEM/statements', [
            'headers' => ['Content-type' => 'application/x-turtle;charset=UTF-8'],
            'query' => ['context' => "_:$uniqueid"],
            'body' => $repoCreateStmt,
        ]);
        $this->httpClient->put("repositories/$this->rdf4jRepository/statements", [
            'headers' => ['Content-type' => 'text/turtle;charset=UTF-8'],
            'body' => sprintf(file_get_contents(DocumentRepositoryIntegrationTest::TEST_DOC), config('corpusparole.corpus_doc_id_base_uri')),
            'query' => ['context' => "<".config('corpusparole.corpus_doc_id_base_uri')."crdo-ALA_738>"],
        ]);
        $this->httpClient->put("repositories/$this->rdf4jRepository/statements", [
            'headers' => ['Content-type' => 'text/turtle;charset=UTF-8'],
            'body' => sprintf(file_get_contents(DocumentRepositoryIntegrationTest::TEST_DOC_NO_GEO), config('corpusparole.corpus_doc_id_base_uri')),
            'query' => ['context' => "<".config('corpusparole.corpus_doc_id_base_uri')."crdo-ALA_739>"],
        ]);

    }

    public function tearDown() {
        $this->httpClient->delete("repositories/$this->rdf4jRepository");
        parent::tearDown();
    }

    public function testAll() {
        $expectedId = $this->corpusDocIdBaseUri.'crdo-ALA_738';
        $docList = $this->documentRepository->all();
        $this->assertCount(2, $docList, "Should have 2 element");

        $resDoc = $docList[0];

        $this->assertInstanceOf(DocumentResult::class, $resDoc, "Res doc must be a Document");
        $this->assertEquals(config('corpusparole.corpus_id_scheme').'crdo-ALA_738', $resDoc->getId(), "id must be crdo...");
        $this->assertEquals($expectedId, $resDoc->getUri(), 'url must be ...');
        $this->assertNotNull($resDoc->getGraph(), 'Graph must not be null');

    }

    public function testGet() {

        $expectedId = $this->corpusDocIdBaseUri.'crdo-ALA_738';
        $returnedGraph = new EasyRdf\Graph($expectedId, sprintf(file_get_contents(DocumentRepositoryIntegrationTest::TEST_DOC),config('corpusparole.corpus_doc_id_base_uri')));

        $res = $this->documentRepository->get('crdo-ALA_738');

        $this->assertInstanceOf(Document::class, $res, "Result must be of type Document");
        $this->assertEquals(config('corpusparole.corpus_id_scheme').'crdo-ALA_738', $res->getId(), 'id should be crdo-ALA_738' );
        $this->assertNotNull($res->getGraph(), "Graph should not be null");
        $this->assertEquals(config('corpusparole.corpus_doc_id_base_uri')."crdo-ALA_738",$res->getGraph()->getUri(), "uri of graph must be ".config('corpusparole.corpus_doc_id_base_uri')."crdo-ALA_738");
        $this->assertTrue(EasyRdf\Isomorphic::isomorphic($res->getGraph(),$returnedGraph));
    }

    public function testGetShort() {

        $expectedId = $this->corpusDocIdBaseUri.'crdo-ALA_738';
        $returnedGraph = new EasyRdf\Graph($expectedId, sprintf(file_get_contents(DocumentRepositoryIntegrationTest::TEST_DOC),config('corpusparole.corpus_doc_id_base_uri')));

        $res = $this->documentRepository->get('crdo-ALA_738', true);

        $this->assertInstanceOf(DocumentResult::class, $res, "Result must be of type DocumentResult");
        $this->assertEquals(config('corpusparole.corpus_id_scheme').'crdo-ALA_738', $res->getId(), 'id should be crdo-ALA_738' );
        $this->assertNotNull($res->getGraph(), "Graph shoul not be null");
        $this->assertEquals(config('corpusparole.corpus_doc_id_base_uri')."crdo-ALA_738",$res->getGraph()->getUri(), "uri of graph must be ".config('corpusparole.corpus_doc_id_base_uri')."crdo-ALA_738");
        $this->assertTrue(EasyRdf\Isomorphic::isomorphic($res->getGraph(),$returnedGraph));
    }

    public function testNoGeo() {
        $expectedId = $this->corpusDocIdBaseUri.'crdo-ALA_739';
        $returnedGraph = new EasyRdf\Graph($expectedId, sprintf(file_get_contents(DocumentRepositoryIntegrationTest::TEST_DOC_NO_GEO),config('corpusparole.corpus_doc_id_base_uri')));

        $res = $this->documentRepository->get('crdo-ALA_739');

        $this->assertNull($res->getGeoInfo(), "Must have no geo info");

    }

    /**
     * @expectedException        CorpusParole\Libraries\CorpusParoleException
     * @expectedExceptionMessage GetDeltaList called when changes are pending
     */
    public function testAddGeoNoCommit() {

        $doc = $this->documentRepository->get('crdo-ALA_739');

        $geoInfo = $doc->addGeoInfo();

        $res = $this->documentRepository->save($doc);
    }


    public function testAddGeo() {

        $doc = $this->documentRepository->get('crdo-ALA_739');

        $geoInfo = $doc->addGeoInfo();
        $geoInfo->commit();

        $res = $this->documentRepository->save($doc);

        $res = $this->documentRepository->get('crdo-ALA_739');

        $geoInfo = $res->getGeoInfo();

        $this->assertNotNull($geoInfo, "Must have Geo info");

        $notes = $geoInfo->getNotes();

        $this->assertTrue(is_array($notes));
        $this->assertCount(0, $notes);

        $refLocs = $geoInfo->getRefLocs();

        $this->assertTrue(is_array($refLocs));
        $this->assertCount(0,$refLocs);

    }

    public function testAddGeoExisting() {

        $doc = $this->documentRepository->get('crdo-ALA_738');

        $geoInfo = $doc->addGeoInfo();
        $geoInfo->commit();

        $res = $this->documentRepository->save($doc);

        $res = $this->documentRepository->get('crdo-ALA_738');

        $geoInfo = $res->getGeoInfo();

        $this->assertNotNull($geoInfo, "Must have Geo info");

        $notes = $geoInfo->getNotes();

        $this->assertTrue(is_array($notes));
        $this->assertCount(3, $notes);

        $refLocs = $geoInfo->getRefLocs();

        $this->assertTrue(is_array($refLocs));
        $this->assertCount(1,$refLocs);

    }

    public function testGeoSetRefLoc() {

        $doc = $this->documentRepository->get('crdo-ALA_738');
        $geoInfo = $doc->addGeoInfo();

        $newRefLocs = [ 'http://sws.geonames.org/2643743/' ];

        $geoInfo->setRefLocs($newRefLocs);

        $geoInfo->commit();

        $res = $this->documentRepository->save($doc);

        $res = $this->documentRepository->get('crdo-ALA_738');

        $geoInfo = $res->getGeoInfo();

        $refLocs = $geoInfo->getRefLocs();

        $this->assertTrue(is_array($refLocs));
        $this->assertCount(1,$refLocs);

        $this->assertEquals(['http://sws.geonames.org/2643743/'], $refLocs);

    }



    public function testSave() {

        $doc = new Document(config('corpusparole.corpus_doc_id_base_uri')."crdo-ALA_738", $this->graph);
        $updatedDiscourseTypes = ["http://ark.bnf.fr/ark:/12148/cb13319048g", "http://ark.bnf.fr/ark:/12148/cb11949715t" , "http://ark.bnf.fr/ark:/12148/cb119783362"];
        $doc->updateDiscourseTypes($updatedDiscourseTypes);

        $res = $this->documentRepository->save($doc);

        $this->assertTrue($res, 'Has started a transaction');

        $res = $this->documentRepository->get('crdo-ALA_738');

        $discoursesTypes = $res->getDiscourseTypes();

        $this->assertCount(3, $discoursesTypes, "types array must be of size 3");

        $this->assertContainsOnlyInstancesOf("EasyRdf\Resource", $discoursesTypes, "Result contains only resources");

        $newDiscoursesTypes = [];
        foreach($discoursesTypes as $dt) {
            array_push($newDiscoursesTypes, $dt->getUri());
            $this->assertContains($dt->getUri(), $updatedDiscourseTypes,"Value in ".print_r($updatedDiscourseTypes, true));
        }
        sort($newDiscoursesTypes);
        sort($updatedDiscourseTypes);

        $this->assertEquals($updatedDiscourseTypes, $newDiscoursesTypes, "array type must the same");
    }

    public function testCount() {
        $res = $this->documentRepository->getCount();

        $this->assertNotNull($res, "Res should not be null");
        $this->assertSame(2, $res, "should have 2 documents");
    }


}
