from __future__ import unicode_literals
from __future__ import absolute_import, division, print_function

"""
Tests for entity module
"""

__author__      = "Graham Klyne (GK@ACM.ORG)"
__copyright__   = "Copyright 2014, G. Klyne"
__license__     = "MIT (http://opensource.org/licenses/MIT)"

import os
import unittest

import logging
log = logging.getLogger(__name__)

from django.conf                import settings
from django.contrib.auth.models import User
from django.test                import TestCase # cf. https://docs.djangoproject.com/en/dev/topics/testing/tools/#assertions
from django.test.client         import Client

from annalist.identifiers       import ANNAL
from annalist.models.entity     import EntityRoot, Entity

from .AnnalistTestCase import AnnalistTestCase
from .tests import (
    test_layout,
    TestHost, TestHostUri, TestBasePath, TestBaseUri, TestBaseDir
    )
from .init_tests import (
    init_annalist_test_site, init_annalist_test_coll, resetSitedata
    )

#   -----------------------------------------------------------------------------
#
#   EntityRoot tests
#
#   -----------------------------------------------------------------------------

class TestEntityRootType(EntityRoot):

    _entitytype   = "test:EntityRootType"
    _entitypath   = None
    _entitybase   = ".sub/"
    _entityfile   = "manifest.jsonld"
    _entityref    = "../"
    _contextbase  = "testrootbase/"
    _contextref   = "../../coll_context.jsonld"


class EntityRootTest(TestCase):
    """
    Tests for EntityRoot object interface
    """

    def setUp(self):
        init_annalist_test_site()
        return

    def tearDown(self):
        return

    @classmethod
    def setUpClass(cls):
        super(EntityRootTest, cls).setUpClass()
        return

    @classmethod
    def tearDownClass(cls):
        super(EntityRootTest, cls).tearDownClass()
        resetSitedata(scope="all")
        return

    def test_EntityRootTest(self):
        self.assertEqual(EntityRootTest.__name__, "EntityRootTest", "Check class name")
        return

    def test_entityroot_init(self):
        e = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        self.assertEqual(e._entitytype,     ANNAL.CURIE.EntityRoot)
        self.assertEqual(e._entityfile,     None)
        self.assertEqual(e._entityref,      None)
        self.assertEqual(e._entityid,       None)
        self.assertEqual(e._entityurl,      TestBaseUri+"/")
        self.assertEqual(e._entityviewurl,  TestBaseUri+"/")
        self.assertEqual(e._entitydir,      TestBaseDir+"/")
        self.assertEqual(e._values,         None)
        return

    def test_entityroot_id(self):
        e = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        self.assertEqual(e.get_id(),        None)
        e.set_id("testId")
        self.assertEqual(e.get_id(),        "testId")
        return

    def test_entityroot_type_id(self):
        e = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        self.assertEqual(e.get_type_id(),   None)
        return

    def test_entityroot_values(self):
        test_values1 = (
            { 'type':           'annal:EntityRoot'
            , 'title':          'Name collection coll1'
            # , 'uri':            '/annalist/coll1'
            })
        test_values1_returned = (
            { 'annal:id':       None
            , 'annal:type_id':  None
            , 'annal:type':     'annal:EntityRoot'
            , 'annal:url':      TestBasePath+'/'
            , 'type':           'annal:EntityRoot'
            , 'title':          'Name collection coll1'
            })
        test_values2 = (
            { 'id':             'TestId'
            , 'type':           'annal:EntityRoot'
            , 'rdfs:label':     'Name collection coll2'
            # , 'uri':            '/annalist/coll2'
            })
        test_values2_returned = (
            { 'annal:id':       None
            , 'annal:type_id':  None
            , 'annal:type':     'annal:EntityRoot'
            , 'annal:url':      TestBasePath+'/'
            , 'id':             'TestId'
            , 'type':           'annal:EntityRoot'
            , 'rdfs:label':     'Name collection coll2'
            })
        e = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        self.assertEqual(e.get_values(),    None)
        e.set_values(test_values1)
        self.assertEqual(e.get_values(),    test_values1_returned)
        e.set_values(test_values2)
        self.assertEqual(e.get_values(),    test_values2_returned)
        return

    def test_entityroot_indexing(self):
        test_values1 = (
            { 'type':           'annal:EntityRoot'
            , 'title':          'Name collection coll1'
            # , 'uri':            '/annalist/coll1'
            })
        test_values1_returned = (
            { 'annal:id':       None
            , 'annal:type_id':  None
            , 'annal:type':     'annal:EntityRoot'
            , 'type':           'annal:EntityRoot'
            , 'annal:url':      TestBasePath+'/'
            , 'title':          'Name collection coll1'
            })
        test_values2_returned = (
            { 'annal:id':       None
            , 'annal:type_id':  None
            , 'annal:type':     'annal:EntityRoot'
            , 'annal:url':      TestBasePath+'/'
            , 'id':             'testid'
            , 'type':           'annal:EntityRoot'
            , 'title':          'new title'
            })
        e = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        self.assertEqual(e.get_values(),    None)
        e.set_values(test_values1)
        self.assertEqual(set(e.get_values().keys()), set(test_values1_returned.keys()))
        self.assertEqual(e.get_values(),    test_values1_returned)
        for k,v in test_values1_returned.items():
            self.assertEqual(e[k], v)
        e['id']    = "testid"
        e['title'] = "new title" 
        # e['uri']   = "/annalist/coll2"
        self.assertEqual(e.get_values(),    test_values2_returned)
        for k,v in test_values2_returned.items():
            self.assertEqual(e[k], v)
        return

    def test_entityroot_subclass(self):
        e = TestEntityRootType(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        self.assertEqual(e._entitytype,     "test:EntityRootType")
        self.assertEqual(e._entitybase,     ".sub/")
        self.assertEqual(e._entityfile,     "manifest.jsonld")
        self.assertEqual(e._entityref,      "../")
        self.assertEqual(e._entityid,       None)
        self.assertEqual(e._entityurl,      TestBaseUri+"/")
        self.assertEqual(e._entitydir,      TestBaseDir+"/")
        self.assertEqual(e._values,         None)
        self.assertEqual(e._dir_path(),     (TestBaseDir+"/.sub", TestBaseDir+"/.sub/manifest.jsonld"))
        return

    def test_entityroot_save_load(self):
        test_values = (
            { 'type':   'annal:EntityRoot'
            , 'title':  'Name collection coll1'
            })
        test_values_returned = (
            { '@id':            '../'
            , '@type':          ['test:EntityRootType']
            , '@context':       [{"@base": "testrootbase/"}, "../../coll_context.jsonld"]
            , 'annal:id':       'testId'
            , 'annal:type_id':  None
            , 'annal:type':     'test:EntityRootType'
            , 'annal:url':      TestBasePath+'/'
            , 'title':          'Name collection coll1'
            , 'type':           'annal:EntityRoot'
            })
        e = TestEntityRootType(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        e.set_id("testId")
        self.assertEqual(e._entitytype,     "test:EntityRootType")
        self.assertEqual(e._entitybase,     ".sub/")
        self.assertEqual(e._entityfile,     "manifest.jsonld")
        self.assertEqual(e._entityref,      "../")
        self.assertEqual(e._entityid,       "testId")
        self.assertEqual(e._entityurl,      TestBaseUri+"/")
        self.assertEqual(e._entitydir,      TestBaseDir+"/")
        self.assertEqual(e._values,         None)
        e.set_values(test_values)
        e._save()
        e2 = TestEntityRootType(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        e2.set_id("testId")
        v2 = e2._load_values()
        self.assertEqual(set(v2.keys()), set(test_values_returned.keys()))
        self.assertEqual(v2, test_values_returned)
        return

    def test_entityroot_exists(self):
        test_values = (
            { 'type':   'annal:EntityRoot'
            , 'title':  'Name collection coll1'
            # , 'uri':    '/annalist/coll1'
            })
        e = TestEntityRootType(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        e.set_id("testId")
        e.set_values(test_values)
        self.assertFalse(e._exists())
        e._save()
        self.assertTrue(e._exists())
        return

    def test_entityroot_enum_fields(self):
        test_values = (
            { "@type": ['test:EntityRootType']
            , "a": "a"
            , "b": "b"
            , "c": 2
            , "d":
              [ { "d1a": "d1a"
                , "d1b": "d1b"
                }
              , { "d2a": "d2a"
                , "d2b": "d2b"
                }
              ]
            , "e": { "ex": "ex", "ey": "ey"}
            })
        e = TestEntityRootType(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        e.set_id("testId")
        e.set_values(test_values)
        test_fields_returned = (
            [ (['@type'],           ['test:EntityRootType'])
            , (['annal:id'],        'testId')
            , (['annal:url'],       TestBasePath+'/')
            , (['annal:type_id'],   None)
            , (['annal:type'],      'test:EntityRootType')
            , (["a"],               "a")
            , (["b"],               "b")
            , (["c"],               2)
            , (["d",0,"d1a"],       "d1a")
            , (["d",0,"d1b"],       "d1b")
            , (["d",1,"d2a"],       "d2a")
            , (["d",1,"d2b"],       "d2b")
            , (["e"],               { "ex": "ex", "ey": "ey" })
            ])
        actual_fields_returned = list(e.enum_fields())
        for f in test_fields_returned:
            self.assertIn(f, actual_fields_returned)
        self.assertEqual(len(actual_fields_returned), len(test_fields_returned))
        # Also get get_field...
        for p, v in test_fields_returned:
            self.assertEqual(e.get_field(p), v)
        return


#   -----------------------------------------------------------------------------
#
#   Entity tests
#
#   -----------------------------------------------------------------------------

class TestEntityType(Entity):

    _entitytype   = "test:EntityType"
    _entityroot   = None
    _entityview   = "%(id)s/"
    _entitybase   = ".sub/"
    _entityfile   = "manifest.jsonld"
    _entityref    = "../"
    _contextbase  = "testtypebase/"
    _contextref   = "../../coll_context.jsonld"

class TestEntityTypeSub(Entity):

    _entitytype   = "test:EntityTypeSub"
    _entityroot   = "sub/%(id)s"
    _entityview   = "sub/%(id)s/"
    _entitybase   = ".sub/"
    _entityfile   = "manifest.jsonld"
    _entityref    = "../"
    _contextbase  = "testtypesubbase/"
    _contextref   = "../../coll_context.jsonld"

class EntityTest(AnnalistTestCase):
    """
    Tests for Entity object interface
    """

    def setUp(self):
        init_annalist_test_site()
        return

    def tearDown(self):
        return

    @classmethod
    def setUpClass(cls):
        super(EntityTest, cls).setUpClass()
        return

    @classmethod
    def tearDownClass(cls):
        super(EntityTest, cls).tearDownClass()
        resetSitedata(scope="all")
        return

    #   ----------------------------------------
    #
    #   Helpers
    #
    #   ----------------------------------------

    def values_created(self, entity_type='test:EntityType', entity_title='Name entity test'):
        return (
            { 'type':   entity_type
            , 'title':  entity_title
            })

    def values_returned(self, 
            entity_id='testid', entity_type='test:EntityType', 
            entity_title=None, 
            entity_parent_path=""
            ):
        entity_url = TestBasePath+entity_parent_path+'/%s/'%entity_id
        vals = (
            { 'annal:id':       entity_id
            , 'annal:type_id':  None
            , 'annal:type':     entity_type
            , 'annal:url':      entity_url
            , 'type':           entity_type
            })
        if entity_title:
            vals.update(
                { 'title':          entity_title
                })
        return vals

    def values_reloaded(self, 
            entity_id='testid', entity_type='test:EntityType', 
            entity_title=None, 
            entity_parent_path="",
            entity_base_url="testtypebase/"
            ):
        vals = self.values_returned(
            entity_id=entity_id, entity_type=entity_type, 
            entity_title=entity_title,
            entity_parent_path=entity_parent_path
            )
        vals.update(
            { '@id':        '../'   # Local testing only: see `TestEntityType`
            , '@type':      [entity_type]
            , '@context':   [{"@base": entity_base_url}, "../../coll_context.jsonld"]
            })
        return vals

    #   ----------------------------------------
    #
    #   Tests
    #
    #   ----------------------------------------

    def test_EntityTest(self):
        self.assertEqual(EntityTest.__name__, "EntityTest", "Check class name")
        return

    def test_entity_init(self):
        r = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        e = Entity(r, "testid")
        self.assertEqual(e._entitytype,     ANNAL.CURIE.Entity)
        self.assertEqual(e._entityfile,     None)
        self.assertEqual(e._entityref,      None)
        self.assertEqual(e._entityid,       "testid")
        self.assertEqual(e._entityurl,      TestBaseUri+"/testid/")
        self.assertEqual(e._entityviewurl,  TestBaseUri+"/testid/")
        self.assertEqual(e._entitydir,      TestBaseDir+"/testid/")
        self.assertEqual(e._values,         None)
        self.assertEqual(e.get_root_url(),  TestBaseUri+"/testid")
        self.assertEqual(e.get_view_url(),  TestBaseUri+"/testid/")
        self.assertEqual(e.get_base_url(),  TestBaseUri+"/testid/")
        self.assertEqual(e.get_data_url(),  None)
        self.assertEqual(e.get_data_url("testfile.jsonld"),  TestBaseUri+"/testid/testfile.jsonld")
        return

    def test_entity_type_id(self):
        r = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        self.assertEqual(r.get_type_id(),   None)
        e1 = Entity(r, "testid1")
        self.assertEqual(e1.get_type_id(),  None)
        e2 = Entity(e1, "testid2")
        self.assertEqual(e2.get_type_id(),  None)
        return

    def test_entity_subclass(self):
        r = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        e = TestEntityType(r, "testid")
        self.assertEqual(e._entitytype,     "test:EntityType")
        self.assertEqual(e._entityfile,     "manifest.jsonld")
        self.assertEqual(e._entityref,      "../")
        self.assertEqual(e._entityid,       "testid")
        self.assertEqual(e._entityurl,      TestBaseUri+"/testid/")
        self.assertEqual(e._entitydir,      TestBaseDir+"/testid/")
        self.assertEqual(e._values,         None)
        self.assertEqual(e._dir_path(),     (TestBaseDir+"/testid/.sub", TestBaseDir+"/testid/.sub/manifest.jsonld"))
        self.assertEqual(e.get_root_url(),  None)
        self.assertEqual(e.get_view_url(),  TestBaseUri+"/testid/")
        self.assertEqual(e.get_base_url(),  TestBaseUri+"/testid/.sub/")
        self.assertEqual(e.get_data_url(),  "http://test.example.com/testsite/testid/.sub/manifest.jsonld")
        self.assertEqual(e.get_data_url("testfile.jsonld"),  TestBaseUri+"/testid/.sub/testfile.jsonld")
        return

    def test_entity_dict(self):
        r = EntityRoot(TestBaseUri, TestBaseUri, TestBaseDir, TestBaseDir)
        e = TestEntityType(r, "testid")
        self.assertEqual(e.get("foo",  "bar"),    "bar")
        self.assertEqual(e.get("type", "notype"), "notype")
        e.set_values({'type': 'annal:EntityRoot'})
        self.assertEqual(e.get("foo",  "bar"),    "bar")