# -*- coding: utf-8 -*-
# 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 unittest

from trytond import backend
from trytond.model.exceptions import (
    RequiredValidationError, SizeValidationError)
from trytond.pool import Pool
from trytond.tests.test_tryton import (
    TestCase, activate_module, with_transaction)
from trytond.transaction import Transaction


class CommonTestCaseMixin:

    @with_transaction()
    def test_create(self):
        "Test create text"
        Text = self.Text()

        text, text_none = Text.create([{
                    'text': "Test",
                    }, {
                    'text': None,
                    }])

        self.assertEqual(text.text, "Test")
        self.assertEqual(text_none.text, None)

    @with_transaction()
    def test_create_unicode(self):
        "Test create unicode"
        Text = self.Text()

        text, = Text.create([{
                    'text': "é",
                    }])

        self.assertEqual(text.text, "é")

    @with_transaction()
    def test_create_multiline(self):
        "Test create multilibe"
        Text = self.Text()

        text, = Text.create([{
                    'text': "Foo\nBar",
                    }])

        self.assertEqual(text.text, "Foo\nBar")

    @with_transaction()
    def test_search_equals(self):
        "Test search text equals"
        Text = self.Text()
        text, = Text.create([{
                    'text': "Foo",
                    }])

        texts_foo = Text.search([
                ('text', '=', "Foo"),
                ])
        texts_bar = Text.search([
                ('text', '=', "Bar"),
                ])

        self.assertListEqual(texts_foo, [text])
        self.assertListEqual(texts_bar, [])

    @with_transaction()
    def test_search_equals_none(self):
        "Test search text equals None"
        Text = self.Text()
        text, = Text.create([{
                    'text': None,
                    }])

        texts = Text.search([
                ('text', '=', None),
                ])

        self.assertListEqual(texts, [text])

    @with_transaction()
    def test_search_none_equals_empty(self):
        "Test search text None equals empty"
        Text = self.Text()
        text, = Text.create([{
                    'text': None,
                    }])

        texts = Text.search([
                ('text', '=', ''),
                ])

        self.assertListEqual(texts, [text])

    @with_transaction()
    def test_search_empty_equals_none(self):
        "Test search text empty equals None"
        Text = self.Text()
        text, = Text.create([{
                    'text': '',
                    }])

        texts = Text.search([
                ('text', '=', None),
                ])

        self.assertListEqual(texts, [text])

    @with_transaction()
    def test_search_equals_unicode(self):
        "Test search text equals unicode"
        Text = self.Text()
        text, = Text.create([{
                    'text': "é",
                    }])

        texts = Text.search([
                ('text', '=', "é"),
                ])

        self.assertListEqual(texts, [text])

    @with_transaction()
    def test_search_equals_non_unicode(self):
        "Test search text equals non unicode"
        Text = self.Text()
        text, = Text.create([{
                    'text': "é",
                    }])

        texts = Text.search([
                ('text', '=', "é"),
                ])

        self.assertListEqual(texts, [text])

    @with_transaction()
    def test_search_non_equals(self):
        "Test search text non equals"
        Text = self.Text()
        text, = Text.create([{
                    'text': "Foo",
                    }])

        texts_foo = Text.search([
                ('text', '!=', "Foo"),
                ])
        texts_bar = Text.search([
                ('text', '!=', "Bar"),
                ])

        self.assertListEqual(texts_foo, [])
        self.assertListEqual(texts_bar, [text])

    @with_transaction()
    def test_search_non_equals_none(self):
        "Test search text non equals None"
        Text = self.Text()
        text, = Text.create([{
                    'text': None,
                    }])

        texts = Text.search([
                ('text', '!=', None),
                ])

        self.assertListEqual(texts, [])

    @with_transaction()
    def test_search_in(self):
        "Test search text in"
        Text = self.Text()
        text, = Text.create([{
                    'text': "Foo",
                    }])

        texts_foo = Text.search([
                ('text', 'in', ["Foo"]),
                ])
        texts_bar = Text.search([
                ('text', 'in', ["Bar"]),
                ])
        texts_empty = Text.search([
                ('text', 'in', []),
                ])

        self.assertListEqual(texts_foo, [text])
        self.assertListEqual(texts_bar, [])
        self.assertListEqual(texts_empty, [])

    @with_transaction()
    def test_search_in_none(self):
        "Test search text in [None]"
        Text = self.Text()
        text, = Text.create([{
                    'text': None,
                    }])

        texts = Text.search([
                ('text', 'in', [None]),
                ])

        self.assertListEqual(texts, [text])

    @with_transaction()
    def test_search_not_in(self):
        "Test search text not in"
        Text = self.Text()
        text, = Text.create([{
                    'text': "Foo",
                    }])

        texts_foo = Text.search([
                ('text', 'not in', ["Foo"]),
                ])
        texts_bar = Text.search([
                ('text', 'not in', ["Bar"]),
                ])
        texts_empty = Text.search([
                ('text', 'not in', []),
                ])

        self.assertListEqual(texts_foo, [])
        self.assertListEqual(texts_bar, [text])
        self.assertListEqual(texts_empty, [text])

    @with_transaction()
    def test_search_not_in_none(self):
        "Test search text not in [None]"
        Text = self.Text()
        text, = Text.create([{
                    'text': None,
                    }])

        texts = Text.search([
                ('text', 'not in', [None]),
                ])

        self.assertListEqual(texts, [])

    @with_transaction()
    def test_search_like(self):
        "Test search text like"
        Text = self.Text()
        text, = Text.create([{
                    'text': "Bar",
                    }])

        texts_bar = Text.search([
                ('text', 'like', "Bar"),
                ])
        texts_b = Text.search([
                ('text', 'like', "B%"),
                ])
        texts_foo = Text.search([
                ('text', 'like', "Foo"),
                ])
        texts_f = Text.search([
                ('text', 'like', "F%"),
                ])

        self.assertListEqual(texts_bar, [text])
        self.assertListEqual(texts_b, [text])
        self.assertListEqual(texts_foo, [])
        self.assertListEqual(texts_f, [])

    @with_transaction()
    def test_search_ilike(self):
        "Test search text ilike"
        Text = self.Text()
        text, sambreville = Text.create([{
                    'text': "Bar",
                    }, {
                    'text': "Sambreville",
                    }])
        with Transaction().set_context({
                    '%s.text.search_full_text' % Text.__name__: False,
                }):
            texts_bar = Text.search([
                    ('text', 'ilike', "bar"),
                    ])
            texts_b = Text.search([
                    ('text', 'ilike', "b%"),
                    ])
            texts_foo = Text.search([
                    ('text', 'ilike', "foo"),
                    ])
            texts_f = Text.search([
                    ('text', 'ilike', "f%"),
                    ])

        self.assertListEqual(texts_bar, [text])
        self.assertListEqual(texts_b, [text])
        self.assertListEqual(texts_foo, [])
        self.assertListEqual(texts_f, [])

        self.assertListEqual(
            Text.search([('text', 'ilike', "%Sambreville%")]),
            [sambreville])

    @unittest.skipIf(backend.name == 'sqlite',
        "SQLite does not have full text search")
    @with_transaction()
    def test_search_full_text(self):
        "Test search full text"
        Text = self.Text()
        fat_cat, = Text.create([{
                    'text': "a fat cat sat on a mat and ate a fat rat",
                    }])
        sad_cat, = Text.create([{
                    'text': "a sad cat sat",
                    }])

        for value, result in [
                ("Fat rat", [fat_cat]),
                ('"sad cat" or "fat rat"', [fat_cat, sad_cat]),
                ]:
            with self.subTest(value=value):
                self.assertListEqual(Text.search([
                            ('text', 'ilike', value),
                            ]), result)

    @unittest.skipIf(backend.name == 'sqlite',
        "SQLite does not have full text search")
    @with_transaction()
    def test_search_full_text_order(self):
        "Test order search full text"
        Text = self.Text()
        sad_cat, = Text.create([{
                    'text': "a sad cat sat",
                    }])
        dog, = Text.create([{
                    'text': "a dog",
                    }])
        fat_cat, = Text.create([{
                    'text': "a fat cat sat on a mat and ate a fat rat",
                    }])

        with Transaction().set_context({
                    '%s.text.order' % Text.__name__: (
                        '"sad cat" or "fat rat"'),
                    }):
            self.assertListEqual(
                Text.search([], order=[('text', 'DESC')]),
                [fat_cat, sad_cat, dog])

    @with_transaction()
    def test_search_not_like(self):
        "Test search text not like"
        Text = self.Text()
        text, = Text.create([{
                    'text': "Bar",
                    }])

        texts_bar = Text.search([
                ('text', 'not like', "Bar"),
                ])
        texts_b = Text.search([
                ('text', 'not like', "B%"),
                ])
        texts_foo = Text.search([
                ('text', 'not like', "Foo"),
                ])
        texts_f = Text.search([
                ('text', 'not like', "F%"),
                ])

        self.assertListEqual(texts_bar, [])
        self.assertListEqual(texts_b, [])
        self.assertListEqual(texts_foo, [text])
        self.assertListEqual(texts_f, [text])

    @with_transaction()
    def test_search_not_ilike(self):
        "Test search text not like"
        Text = self.Text()
        text, = Text.create([{
                    'text': "Bar",
                    }])

        with Transaction().set_context({
                    '%s.text.search_full_text' % Text.__name__: False,
                    }):
            texts_bar = Text.search([
                    ('text', 'not ilike', "bar"),
                    ])
            texts_b = Text.search([
                    ('text', 'not ilike', "b%"),
                    ])
            texts_foo = Text.search([
                    ('text', 'not ilike', "foo"),
                    ])
            texts_f = Text.search([
                    ('text', 'not ilike', "f%"),
                    ])

        self.assertListEqual(texts_bar, [])
        self.assertListEqual(texts_b, [])
        self.assertListEqual(texts_foo, [text])
        self.assertListEqual(texts_f, [text])

    @with_transaction()
    def test_write_unicode(self):
        "Test write text unicode"
        Text = self.Text()
        text, = Text.create([{
                    'text': "Foo",
                    }])

        Text.write([text], {
                'text': "é",
                })

        self.assertEqual(text.text, "é")


class FieldTextTestCase(TestCase, CommonTestCaseMixin):
    "Test Field Text"

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        activate_module('tests')

    def Text(self):
        return Pool().get('test.text')

    @with_transaction()
    def test_create_without_default(self):
        "Test create text without default"
        Text = Pool().get('test.text')

        text, = Text.create([{}])

        self.assertEqual(text.text, None)

    @with_transaction()
    def test_create_with_default(self):
        "Test create text without default"
        Text = Pool().get('test.text_default')

        text, = Text.create([{}])

        self.assertEqual(text.text, "Test")

    @with_transaction()
    def test_create_required_with_value(self):
        "Test create text required with value"
        Text = Pool().get('test.text_required')

        text, = Text.create([{
                    'text': "Test",
                    }])

        self.assertEqual(text.text, "Test")

    @with_transaction()
    def test_create_required_without_value(self):
        "Test create text required without value"
        Text = Pool().get('test.text_required')

        with self.assertRaises(RequiredValidationError):
            Text.create([{}])

    @with_transaction()
    def test_create_required_with_empty(self):
        "Test create text required with empty"
        Text = Pool().get('test.text_required')

        with self.assertRaises(RequiredValidationError):
            Text.create([{
                        'text': '',
                        }])

    @with_transaction()
    def test_create_size_valid(self):
        "Test create text with size"
        Text = Pool().get('test.text_size')

        text, = Text.create([{
                    'text': "Test",
                    }])

        self.assertEqual(text.text, "Test")

    @with_transaction()
    def test_create_size_invalid(self):
        "Test create text with invalid size"
        Text = Pool().get('test.text_size')

        with self.assertRaises(SizeValidationError):
            Text.create([{
                        'text': "foobar",
                        }])

    @with_transaction()
    def test_write(self):
        "Test write text"
        Text = Pool().get('test.text')
        text, = Text.create([{
                    'text': "Foo",
                    }])

        Text.write([text], {
                'text': "Bar",
                })

        self.assertEqual(text.text, "Bar")

    @with_transaction()
    def test_write_none(self):
        "Test write text None"
        Text = Pool().get('test.text')
        text, = Text.create([{
                    'text': "Foo",
                    }])

        Text.write([text], {
                'text': None,
                })

        self.assertEqual(text.text, None)

    @with_transaction()
    def test_write_size_invalid(self):
        "Test write text with invalid size"
        Text = Pool().get('test.text_size')

        text, = Text.create([{
                    'text': "Test",
                    }])

        with self.assertRaises(SizeValidationError):
            Text.write([text], {
                    'text': 'foobar',
                    })


class FieldTextTranslatedTestCase(TestCase, CommonTestCaseMixin):
    "Test Field Text Translated"

    def Text(self):
        return Pool().get('test.text_translate')


class FieldFullTextTestCase(TestCase):
    "Test Field FullText"

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        activate_module('tests')

    @with_transaction()
    def test_create(self):
        "Test create full text"
        pool = Pool()
        Model = pool.get('test.text_full')

        record, = Model.create([{
                    'full_text': "a fat cat sat on a mat and ate a fat rat",
                    }])

        self.assertTrue(record.full_text)

    @with_transaction()
    def test_create_list(self):
        "Test create full text with a list"
        pool = Pool()
        Model = pool.get('test.text_full')

        record, = Model.create([{
                    'full_text': [
                        "The Fat Cats",
                        "a fat cat sat on a mat and ate a fat rat",
                        ],
                    }])

        self.assertTrue(record.full_text)

    @with_transaction()
    def test_search(self):
        "Test search full text"
        pool = Pool()
        Model = pool.get('test.text_full')

        fat_cat, sad_cat = Model.create([{
                    'full_text': "a fat cat sat on a mat and ate a fat rat",
                    }, {
                    'full_text': "a sad cat sat",
                    }])

        for clause, result, neg_result in [
                ('fat', [fat_cat], [sad_cat]),
                ('cat', [fat_cat, sad_cat], []),
                ]:
            with self.subTest(clause=clause):
                self.assertListEqual(Model.search([
                            ('full_text', 'ilike', clause),
                            ]), result)
                self.assertListEqual(Model.search([
                            ('full_text', 'not ilike', clause),
                            ]), neg_result)

    @unittest.skipIf(backend.name == 'sqlite',
        "SQLite does not have full text search")
    @with_transaction()
    def test_search_full_text(self):
        "Test search full text"
        pool = Pool()
        Model = pool.get('test.text_full')

        fat_cat, sad_cat = Model.create([{
                    'full_text': "a fat cat sat on a mat and ate a fat rat",
                    }, {
                    'full_text': "a sad cat sat",
                    }])

        for clause, result in [
                ("Fat rat", [fat_cat]),
                ('"sad cat" or "fat rat"', [fat_cat, sad_cat]),
                ]:
            with self.subTest(clause=clause):
                self.assertListEqual(Model.search([
                            ('full_text', 'ilike', clause),
                            ]), result)

    @unittest.skipIf(backend.name == 'sqlite',
        "SQLite does not have full text search")
    @with_transaction()
    def test_search_full_text_filter(self):
        "Test search full text"
        pool = Pool()
        Model = pool.get('test.text_full')

        fat_cat, sad_cat = Model.create([{
                    'full_text': "a fat cat sat on a mat and ate a fat rat",
                    }, {
                    'full_text': "a sad cat sat",
                    }])

        for clause, result in [
                ("Fat rat", [fat_cat]),
                ('"sad cat" or "fat rat"', [fat_cat, sad_cat]),
                ]:
            with self.subTest(clause=clause):
                with Transaction().set_context({
                            '%s.full_text.order' % Model.__name__: clause,
                            }):
                    self.assertListEqual(Model.search([
                                ('full_text', '>', 0.01),
                                ]), result)

    @unittest.skipIf(backend.name == 'sqlite',
        "SQLite does not have full text search")
    @with_transaction()
    def test_search_full_text_order(self):
        "Test order full text"
        pool = Pool()
        Model = pool.get('test.text_full')

        sad_cat, fat_cat, dog = Model.create([{
                    'full_text': [
                        None,
                        "a fat cat sat on a mat and ate a fat rat",
                        ],
                    }, {
                    'full_text': [
                        "The Fat Cats",
                        "a fat cat sat on a mat and ate a fat rat",
                        ]
                    }, {
                    'full_text': "a dog",
                    }])

        with Transaction().set_context({
                    '%s.full_text.order' % Model.__name__: (
                        '"sad cat" or "fat rat"'),
                    }):
            self.assertListEqual(
                Model.search([], order=[('full_text', 'DESC')]),
                [fat_cat, sad_cat, dog])
