#
# This file is a part of the CaosDB Project.
#
# Copyright (C) 2021 IndiScale GmbH <info@indiscale.com>
# Copyright (C) 2021 Timm Fitschen <t.fitschen@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/>.
#

from pytest import raises
import caosdb as db


def setup():
    teardown_module()


def teardown_module():
    clean = db.execute_query("FIND Test*")
    if len(clean) > 0:
        clean.delete()


def test_obl_missing_error():
    p = db.Property(name="TestOblProperty", datatype=db.TEXT).insert()
    p_dummy = db.Property(name="TestDummyProperty", datatype=db.TEXT).insert()

    rt1 = db.RecordType(name="TestRecordType1")
    rt1.add_property(name="TestOblProperty", importance=db.OBLIGATORY)
    rt1.insert()

    # normal behaviour, should be identical to "error" level
    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)
    rt2.add_property("TestDummyProperty")
    with raises(db.TransactionError) as exc:
        rt2.insert()
    assert exc.value.errors[0].msg == "An obligatory property is missing."

    # force error
    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)
    rt2.add_property("TestDummyProperty")
    rt2.set_flag("force-missing-obligatory", "error")
    with raises(db.TransactionError) as exc:
        rt2.insert()
    assert exc.value.errors[0].msg == "An obligatory property is missing."

    # warning is treated as error because "strict" flag
    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)
    rt2.add_property("TestDummyProperty")
    rt2.set_flag("force-missing-obligatory", "warn")
    with raises(db.TransactionError) as exc:
        rt2.insert(strict=True)
    assert exc.value.errors[0].msg == (
        "A warning occured while processing an entity with the strict flag.")
    assert "An obligatory property is missing." in [
        w.description for w in rt2.get_warnings()]

    # insert with warning
    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)
    rt2.add_property("TestDummyProperty")
    rt2.set_flag("force-missing-obligatory", "warn")
    rt2.insert()
    assert len(rt2.get_errors()) == 0
    assert (rt2.get_warnings()[0].description
            == "An obligatory property is missing.")

    # insert silently
    rt3 = db.RecordType(name="TestRecordType3")
    rt3.add_parent("TestRecordType1", inheritance=db.NONE)
    rt3.add_property("TestDummyProperty")
    rt3.set_flag("force-missing-obligatory", "ignore")
    rt3.insert()
    assert len(rt3.get_errors()) == 0
    assert len(rt3.get_warnings()) == 0


def test_obl_missing_parent_in_same_container():
    p = db.Property(name="TestOblProperty", datatype=db.TEXT).insert()

    rt1 = db.RecordType(name="TestRecordType1")
    rt1.add_property(name="TestOblProperty", importance=db.OBLIGATORY)

    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)

    c = db.Container()
    c.extend([rt1, rt2])
    with raises(db.TransactionError) as exc:
        c.insert()
    assert exc.value.errors[0].msg == "An obligatory property is missing."


def test_obl_missing_parent_in_same_container_override():
    """`force-missing-obligatory` flags of entities override flags of containing containers.
    """
    p = db.Property(name="TestOblProperty", datatype=db.TEXT).insert()

    rt1 = db.RecordType(name="TestRecordType1")
    rt1.add_property(name="TestOblProperty", importance=db.OBLIGATORY)

    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)
    rt2.set_flag("force-missing-obligatory", "error")

    c = db.Container()
    c.extend([rt1, rt2])
    with raises(db.TransactionError) as exc:
        c.insert(flags={"force-missing-obligatory": "ignore"})
    assert exc.value.errors[0].msg == "An obligatory property is missing."


def test_sub_property_of_obl():
    """If p1 <|- p2, then p2 should work for an obligatory p1.
    """
    p1 = db.Property(name="TestOblProperty1", datatype=db.TEXT).insert()
    p2 = db.Property(name="TestOblProperty2", datatype=db.TEXT)
    p2.add_parent("TestOblProperty1")
    p2.insert()

    rt1 = db.RecordType(name="TestRecordType1")
    rt1.add_property(name="TestOblProperty1", importance=db.OBLIGATORY)
    rt1.insert()

    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)

    # add subtype of TestOblProperty1
    rt2.add_property("TestOblProperty2")
    rt2.insert()  # everything ok!


def test_sub_property_of_obl_in_same_container():

    p1 = db.Property(name="TestOblProperty1", datatype=db.TEXT).insert()

    rt1 = db.RecordType(name="TestRecordType1")
    rt1.add_property(name="TestOblProperty1", importance=db.OBLIGATORY)
    rt1.insert()

    p2 = db.Property(name="TestOblProperty2", datatype=db.TEXT)
    p2.add_parent("TestOblProperty1")

    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)
    rt2.add_property("TestOblProperty2")

    c = db.Container()
    c.extend([rt2, p2])
    c.insert()  # everything ok!


def test_illegal_flag_value():
    p = db.Property(name="TestOblProperty", datatype=db.TEXT).insert()
    p_dummy = db.Property(name="TestDummyProperty", datatype=db.TEXT).insert()

    rt1 = db.RecordType(name="TestRecordType1")
    rt1.add_property(name="TestOblProperty", importance=db.OBLIGATORY)
    rt1.insert()

    rt2 = db.RecordType(name="TestRecordType2")
    rt2.add_parent("TestRecordType1", inheritance=db.NONE)
    rt2.add_property("TestDummyProperty")
    rt2.set_flag("force-missing-obligatory", "illegal!!!!")
    with raises(db.TransactionError) as exc:
        rt2.insert()
    # default behavior + warning
    assert exc.value.errors[0].msg == "An obligatory property is missing."
    assert "Illegal value for flag 'force-missing-obligatory'." in [
        w.description for w in rt2.get_warnings()]