#!/usr/bin/env python
# encoding: utf-8
#
# ** header v3.0
# This file is a part of the CaosDB Project.
#
# 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
"""Test the error handling in datamodel_problems.py with simple
insertions. A test using a full-grown crawler finding datamodel errors
during crawling that tests the integrations of the DataModelProblems
class in crawler.py and cfood.py can be found in full-tests.

"""
import caosdb as db
import pytest
from caosadvancedtools.datamodel_problems import DataModelProblems
from caosdb.exceptions import (TransactionError,
                               UnqualifiedParentsError,
                               UnqualifiedPropertiesError)


def setup_module():
    """Clear problem sets and delete possible test entities"""
    DataModelProblems.missing.clear()
    try:
        db.execute_query("FIND Test*").delete()
    except Exception as delete_exc:
        print(delete_exc)


@pytest.fixture(autouse=True)
def setup():
    """Same as module setup."""
    setup_module()
    yield None
    setup_module()


def teardown_module():
    """Clear and delete again."""
    setup_module()


def _insert_and_evaluate_exception(ent):
    try:
        ent.insert()
    except Exception as e:
        DataModelProblems.evaluate_exception(e)


def test_missing_parent():
    """Test if missing RecordType is in datamodel problems."""
    missing_name = "TestType"
    rec = db.Record(name="TestRecord")
    rec.add_parent(name=missing_name)
    with pytest.raises(TransactionError) as te:
        _insert_and_evaluate_exception(rec)
    assert te.value.has_error(UnqualifiedParentsError)
    assert missing_name in DataModelProblems.missing


def test_missing_property():
    """Test if missing Property is in datamodel problems."""
    missing_name = "TestProp"
    rec = db.Record(name="TestRecord").add_property(name=missing_name)
    with pytest.raises(TransactionError) as te:
        _insert_and_evaluate_exception(rec)
    assert te.value.has_error(UnqualifiedPropertiesError)
    assert missing_name in DataModelProblems.missing


def test_missing_property_existing_type():
    """Test if missing Property is in datamodel problems but existing
    RecordType is not.

    """
    missing_prop = "TestProp"
    existing_rt = "TestType"
    db.RecordType(name=existing_rt).insert()
    rec = db.Record(name="TestRecord").add_parent(name=existing_rt)
    rec.add_property(name=missing_prop)
    with pytest.raises(TransactionError) as te:
        _insert_and_evaluate_exception(rec)
    assert te.value.has_error(UnqualifiedPropertiesError)
    assert missing_prop in DataModelProblems.missing
    assert existing_rt not in DataModelProblems.missing


def test_wrong_property_value():
    """An error due to a wrong value (type) is no data model problem per
    se

    """
    rt_name = "TestType"
    rt = db.RecordType(name=rt_name).insert()
    prop_name = "TestProp"
    prop_dtype = db.DOUBLE
    prop = db.Property(name=prop_name, datatype=prop_dtype).insert()
    rec = db.Record(name="TestRecord").add_parent(
        name=rt_name).add_property(name=prop_name, value="bla")
    with pytest.raises(TransactionError) as te:
        _insert_and_evaluate_exception(rec)
    assert te.value.has_error(UnqualifiedPropertiesError)
    # Should be empty
    assert not DataModelProblems.missing
