#!/usr/bin/env python
# encoding: utf-8
#
# ** header v3.0
# This file is a part of the CaosDB Project.
#
# Copyright (C) 2018 Research Group Biomedical Physics,
# Max-Planck-Institute for Dynamics and Self-Organization Göttingen
# Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
# Copyright (C) 2020 Florian Spreckelsen <f.spreckelsen@indiscale.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
# ** end header
"""Check basic crawler functionality without actually using
cfoods. This is tested in test_crawler_with_cfoods.py.

"""
import unittest

import caosdb as db

from caosadvancedtools.crawler import Crawler
from caosadvancedtools.guard import INSERT
from caosadvancedtools.guard import global_guard as guard


def setup_module():
    """Clear all test entities.  Allow insertions."""
    guard.set_level(INSERT)
    try:
        db.execute_query("FIND Test*").delete()
    except Exception:
        pass


class CrawlerTest(unittest.TestCase):
    def setUp(self):
        """Clear possibly existing entities and create the necessary Records,
        RecordTypes, and Properties.

        """
        setup_module()
        self.rts = db.Container().extend([
            db.RecordType(name="Test_Type_1").insert(),
            db.RecordType(name="Test_Type_2").insert(),
            db.RecordType(name="Test_Type_3").insert(),
            db.Property(name="Test_Prop", datatype=db.TEXT).insert(),
        ])
        self.rec1 = db.Record()
        self.rec1.add_parent(name="Test_Type_1")
        self.rec1.add_property(name="Test_Prop", value="Test")
        self.rec2 = db.Record()
        self.rec2.add_parent(name="Test_Type_2")
        self.rec3 = db.Record()
        self.rec3.add_parent(name="Test_Type_3")
        self.rec3.add_property(name="Test_Prop", value="Test")

    def test_check_existence(self):
        # This hasn't been inserted yet:
        assert Crawler.find_existing(self.rec1) is None

    def test_find_or_insert_identifiables(self):
        """Create identical Records that have to be identified by the
        crawler.

        """
        same_as_rec1 = db.Record()
        same_as_rec1.add_parent(name="Test_Type_1")
        same_as_rec1.add_property(name="Test_Prop", value="Test")
        same_as_rec2 = db.Record()
        same_as_rec2.add_parent(name="Test_Type_2")
        same_as_rec1.insert()
        same_as_rec2.insert()
        # copy the id
        self.rec2.id = same_as_rec2.id
        # Insert rec1, rec2, and rec3. rec1 already exists in the
        # database but doesn't have an id yet, rec2 exists, rec3 is
        # missing entirely.
        identifiables = db.Container().extend(
            [self.rec1, self.rec2, self.rec3])
        old_id = id(identifiables[0])
        reference_to_first = identifiables[0]
        assert reference_to_first is identifiables[0]

        Crawler.find_or_insert_identifiables(identifiables)

        for el in identifiables:
            assert el.is_valid()

        # check whether instance is the same
        assert reference_to_first is identifiables[0]
        assert old_id == id(identifiables[0])
        # order must not be changed
        assert identifiables[0].get_parents()[0].name == "Test_Type_1"
        assert identifiables[1].get_parents()[0].name == "Test_Type_2"
        assert identifiables[2].get_parents()[0].name == "Test_Type_3"

    def tearDown(self):
        setup_module()

        # Delete nameless entities
        for el in [self.rec1, self.rec2, self.rec3]:
            try:
                el.delete()
            except BaseException:
                pass


class CrawlerTestExist(CrawlerTest):
    """Same tests as above, but now insert Record during setup."""

    def setUp(self):
        super().setUp()
        self.rec1.insert()
        self.rec2.insert()
        self.rec3.insert()

    def test_check_existence(self):
        # Now this exists
        res = Crawler.find_existing(self.rec1)
        assert res.id == self.rec1.id