# This file is part of Tryton.  The COPYRIGHT file at the top level of
# this repository contains the full copyright notices and license terms.
import sys
import unittest

from trytond import backend
from trytond.pool import Pool
from trytond.tests.test_tryton import ModuleTestCase, with_transaction
from trytond.transaction import Transaction


class TestCase(ModuleTestCase):
    'Test Party relationship module'
    module = 'party_relationship'

    @with_transaction()
    def test_reverse_relationship(self):
        'Test reverse relationship'
        pool = Pool()
        Party = pool.get('party.party')
        RelationType = pool.get('party.relation.type')
        Relation = pool.get('party.relation')

        employee_of, = RelationType.create([{
                    'name': 'Employee of',
                    }])
        works_for, = RelationType.create([{
                    'name': 'Works for',
                    'reverse': employee_of.id,
                    }])
        RelationType.write([employee_of], {
                'reverse': works_for.id,
                })

        company, employee = Party.create([{
                    'name': 'Company',
                    }, {
                    'name': 'Employee',
                    }])

        Relation.create([{
                    'from_': company.id,
                    'to': employee.id,
                    'type': employee_of.id
                    }])
        company_relation, = company.relations
        employee_relation, = employee.relations
        self.assertEqual(company_relation.type, employee_of)
        self.assertEqual(company_relation.to, employee)
        self.assertEqual(employee_relation.type, works_for)
        self.assertEqual(employee_relation.to, company)

    @with_transaction()
    def test_without_reverse_relationship(self):
        'Test without reverse relationship'
        pool = Pool()
        Party = pool.get('party.party')
        RelationType = pool.get('party.relation.type')
        Relation = pool.get('party.relation')

        employee_of, = RelationType.create([{
                    'name': 'Employee of',
                    }])
        works_for, = RelationType.create([{
                    'name': 'Works for',
                    }])

        company, employee = Party.create([{
                    'name': 'Company',
                    }, {
                    'name': 'Employee',
                    }])

        Relation.create([{
                    'from_': company.id,
                    'to': employee.id,
                    'type': employee_of.id
                    }])
        company_relation, = company.relations
        self.assertEqual(len(employee.relations), 0)
        self.assertEqual(company_relation.type, employee_of)
        self.assertEqual(company_relation.to, employee)

    @with_transaction()
    def test_relation_all(self):
        'Test relation all'
        pool = Pool()
        Party = pool.get('party.party')
        RelationType = pool.get('party.relation.type')
        RelationAll = pool.get('party.relation.all')

        relation_type, reverse_relation_type = RelationType.create([{
                    'name': 'Relation',
                    }, {
                    'name': 'Reverse',
                    }])
        relation_type.reverse = reverse_relation_type
        relation_type.save()
        reverse_relation_type.reverse = relation_type
        reverse_relation_type.save()

        party1, party2, party3 = Party.create([{
                    'name': 'Party 1',
                    }, {
                    'name': 'Party 2',
                    }, {
                    'name': 'Party 3',
                    }])

        relation, = RelationAll.create([{
                    'from_': party1.id,
                    'to': party2.id,
                    'type': relation_type.id,
                    }])
        self.assertFalse(relation.id % 2)
        reverse_relation, = RelationAll.search([
                ('id', '!=', relation.id),
                ])
        self.assertTrue(reverse_relation.id % 2)
        self.assertEqual(relation.reverse_id, reverse_relation.id)
        self.assertEqual(reverse_relation.from_, party2)
        self.assertEqual(reverse_relation.to, party1)

        reverse_relation.from_ = party3
        reverse_relation.save()
        relation.save()  # clear cache
        self.assertEqual(reverse_relation.from_, party3)
        self.assertEqual(reverse_relation.to, party1)
        self.assertEqual(relation.from_, party1)
        self.assertEqual(relation.to, party3)

        reverse_relation.type = reverse_relation_type
        reverse_relation.save()
        self.assertEqual(reverse_relation.from_, party3)
        self.assertEqual(reverse_relation.to, party1)
        self.assertEqual(reverse_relation.type, reverse_relation_type)
        self.assertEqual(relation.from_, party1)
        self.assertEqual(relation.to, party3)
        self.assertEqual(relation.type, relation_type)

        relation.type = relation_type
        relation.save()
        self.assertEqual(reverse_relation.from_, party3)
        self.assertEqual(reverse_relation.to, party1)
        self.assertEqual(reverse_relation.type, reverse_relation_type)
        self.assertEqual(relation.from_, party1)
        self.assertEqual(relation.to, party3)
        self.assertEqual(relation.type, relation_type)

        reverse_relation.to = party2
        reverse_relation.save()
        relation.save()  # clear cache
        self.assertEqual(reverse_relation.from_, party3)
        self.assertEqual(reverse_relation.to, party2)
        self.assertEqual(relation.from_, party2)
        self.assertEqual(relation.to, party3)

        reverse_relation.from_ = party2
        reverse_relation.to = party1
        reverse_relation.save()
        relation.save()  # clear cache
        self.assertEqual(reverse_relation.from_, party2)
        self.assertEqual(reverse_relation.to, party1)
        self.assertEqual(relation.from_, party1)
        self.assertEqual(relation.to, party2)

        relation.from_ = party2
        relation.to = party1
        relation.save()
        reverse_relation.save()  # clear cache
        self.assertEqual(relation.from_, party2)
        self.assertEqual(relation.to, party1)
        self.assertEqual(reverse_relation.from_, party1)
        self.assertEqual(reverse_relation.to, party2)

        RelationAll.delete([reverse_relation])
        self.assertEqual(RelationAll.search([]), [])

    @unittest.skipIf(
        backend.name == 'sqlite' and sys.version_info < (3, 6),
        "Executing distance query prevents complete rollback")
    @with_transaction()
    def test_party_distance(self):
        "Test party distance"
        pool = Pool()
        Party = pool.get('party.party')
        RelationType = pool.get('party.relation.type')
        Relation = pool.get('party.relation')

        usages = RelationType.usages.selection
        self.addCleanup(setattr, RelationType.usages, 'selection', usages)
        RelationType.usages.selection = usages + [('test', "Test")]

        relation, reverse = RelationType.create([{
                    'name': 'Relation',
                    }, {
                    'name': 'Reverse',
                    }])
        relation.reverse = reverse
        relation.save()
        reverse.reverse = relation
        reverse.save()

        A, B, C, D = Party.create([{
                    'name': 'A',
                    }, {
                    'name': 'B',
                    }, {
                    'name': 'C',
                    }, {
                    'name': 'D'
                    }])
        Relation.create([{
                    'from_': A.id,
                    'to': C.id,
                    'type': relation.id,
                    }, {
                    'from_': C.id,
                    'to': D.id,
                    'type': relation.id,
                    }])

        parties = Party.search([])
        self.assertEqual([p.distance for p in parties], [None] * 4)

        with Transaction().set_context(related_party=A.id):
            parties = Party.search([])
            self.assertEqual(
                [(p.name, p.distance) for p in parties],
                [('A', 0), ('C', 1), ('D', 2), ('B', None)])

        another_relation, = RelationType.create([{
                    'name': 'Another Relation',
                    'usages': ['test'],
                    }])
        Relation.create([{
                    'from_': A.id,
                    'to': B.id,
                    'type': another_relation.id,
                    }])

        with Transaction().set_context(
                related_party=A.id, relation_usages=['test']):
            parties = Party.search([])
            self.assertEqual(
                [(p.name, p.distance) for p in parties],
                [('A', 0), ('B', 1), ('C', None), ('D', None)])

        with Transaction().set_context(related_party=A.id):
            parties = Party.search([])
            self.assertEqual(
                [(p.name, p.distance) for p in parties],
                [('A', 0), ('B', 1), ('C', 1), ('D', 2)])

    @with_transaction()
    def test_contact_mechanism_distance(self):
        "Test relation distance"
        pool = Pool()
        Party = pool.get('party.party')
        ContactMechanism = pool.get('party.contact_mechanism')
        RelationType = pool.get('party.relation.type')
        Relation = pool.get('party.relation')

        relation, = RelationType.create([{
                    'name': 'Relation',
                    }])

        A, B, C = Party.create([{
                    'name': "A",
                    'contact_mechanisms': [
                        ('create', [{'value': "A", 'type': 'other'}])],
                    }, {
                    'name': "B",
                    'contact_mechanisms': [
                        ('create', [{'value': "B", 'type': 'other'}])],
                    }, {
                    'name': "C",
                    'contact_mechanisms': [
                        ('create', [{'value': "C", 'type': 'other'}])],
                    }])

        Relation.create([{
                    'from_': A.id,
                    'to': C.id,
                    'type': relation.id,
                    }])

        with Transaction().set_context(related_party=A.id):
            contact_mechanisms = ContactMechanism.search([])
            self.assertEqual(
                [c.value for c in contact_mechanisms],
                ['A', 'C', 'B'])


del ModuleTestCase
