from __future__ import unicode_literals

from datetime import datetime, timedelta
import threading
import warnings

from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.db import connections, DEFAULT_DB_ALIAS
from django.db import DatabaseError
from django.db.models.fields import Field
from django.db.models.fields.related import ForeignObjectRel
from django.db.models.manager import BaseManager
from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet, MAX_GET_RESULTS
from django.test import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
from django.utils import six
from django.utils.translation import ugettext_lazy

from .models import Article, SelfRef, ArticleSelectOnSave


class ModelInstanceCreationTests(TestCase):

    def test_object_is_not_written_to_database_until_save_was_called(self):
        a = Article(
            id=None,
            headline='Area man programs in Python',
            pub_date=datetime(2005, 7, 28),
        )
        self.assertIsNone(a.id)
        self.assertEqual(Article.objects.all().count(), 0)

        # Save it into the database. You have to call save() explicitly.
        a.save()
        self.assertIsNotNone(a.id)
        self.assertEqual(Article.objects.all().count(), 1)

    def test_can_initialize_model_instance_using_positional_arguments(self):
        """
        You can initialize a model instance using positional arguments,
        which should match the field order as defined in the model.
        """
        a = Article(None, 'Second article', datetime(2005, 7, 29))
        a.save()

        self.assertEqual(a.headline, 'Second article')
        self.assertEqual(a.pub_date, datetime(2005, 7, 29, 0, 0))

    def test_can_create_instance_using_kwargs(self):
        a = Article(
            id=None,
            headline='Third article',
            pub_date=datetime(2005, 7, 30),
        )
        a.save()
        self.assertEqual(a.headline, 'Third article')
        self.assertEqual(a.pub_date, datetime(2005, 7, 30, 0, 0))

    def test_autofields_generate_different_values_for_each_instance(self):
        a1 = Article.objects.create(headline='First', pub_date=datetime(2005, 7, 30, 0, 0))
        a2 = Article.objects.create(headline='First', pub_date=datetime(2005, 7, 30, 0, 0))
        a3 = Article.objects.create(headline='First', pub_date=datetime(2005, 7, 30, 0, 0))
        self.assertNotEqual(a3.id, a1.id)
        self.assertNotEqual(a3.id, a2.id)

    def test_can_mix_and_match_position_and_kwargs(self):
        # You can also mix and match position and keyword arguments, but
        # be sure not to duplicate field information.
        a = Article(None, 'Fourth article', pub_date=datetime(2005, 7, 31))
        a.save()
        self.assertEqual(a.headline, 'Fourth article')

    def test_cannot_create_instance_with_invalid_kwargs(self):
        six.assertRaisesRegex(
            self,
            TypeError,
            "'foo' is an invalid keyword argument for this function",
            Article,
            id=None,
            headline='Some headline',
            pub_date=datetime(2005, 7, 31),
            foo='bar',
        )

    def test_can_leave_off_value_for_autofield_and_it_gets_value_on_save(self):
        """
        You can leave off the value for an AutoField when creating an
        object, because it'll get filled in automatically when you save().
        """
        a = Article(headline='Article 5', pub_date=datetime(2005, 7, 31))
        a.save()
        self.assertEqual(a.headline, 'Article 5')
        self.assertNotEqual(a.id, None)

    def test_leaving_off_a_field_with_default_set_the_default_will_be_saved(self):
        a = Article(pub_date=datetime(2005, 7, 31))
        a.save()
        self.assertEqual(a.headline, 'Default headline')

    def test_for_datetimefields_saves_as_much_precision_as_was_given(self):
        """as much precision in *seconds*"""
        a1 = Article(
            headline='Article 7',
            pub_date=datetime(2005, 7, 31, 12, 30),
        )
        a1.save()
        self.assertEqual(Article.objects.get(id__exact=a1.id).pub_date,
            datetime(2005, 7, 31, 12, 30))

        a2 = Article(
            headline='Article 8',
            pub_date=datetime(2005, 7, 31, 12, 30, 45),
        )
        a2.save()
        self.assertEqual(Article.objects.get(id__exact=a2.id).pub_date,
            datetime(2005, 7, 31, 12, 30, 45))

    def test_saving_an_object_again_does_not_create_a_new_object(self):
        a = Article(headline='original', pub_date=datetime(2014, 5, 16))
        a.save()
        current_id = a.id

        a.save()
        self.assertEqual(a.id, current_id)

        a.headline = 'Updated headline'
        a.save()
        self.assertEqual(a.id, current_id)

    def test_querysets_checking_for_membership(self):
        headlines = [
            'Area man programs in Python', 'Second article', 'Third article']
        some_pub_date = datetime(2014, 5, 16, 12, 1)
        for headline in headlines:
            Article(headline=headline, pub_date=some_pub_date).save()
        a = Article(headline='Some headline', pub_date=some_pub_date)
        a.save()

        # You can use 'in' to test for membership...
        self.assertIn(a, Article.objects.all())
        # ... but there will often be more efficient ways if that is all you need:
        self.assertTrue(Article.objects.filter(id=a.id).exists())


class ModelTest(TestCase):
    def test_objects_attribute_is_only_available_on_the_class_itself(self):
        six.assertRaisesRegex(
            self,
            AttributeError,
            "Manager isn't accessible via Article instances",
            getattr,
            Article(),
            "objects",
        )
        self.assertFalse(hasattr(Article(), 'objects'))
        self.assertTrue(hasattr(Article, 'objects'))

    def test_queryset_delete_removes_all_items_in_that_queryset(self):
        headlines = [
            'An article', 'Article One', 'Amazing article', 'Boring article']
        some_pub_date = datetime(2014, 5, 16, 12, 1)
        for headline in headlines:
            Article(headline=headline, pub_date=some_pub_date).save()
        self.assertQuerysetEqual(Article.objects.all().order_by('headline'),
            ["<Article: Amazing article>",
             "<Article: An article>",
             "<Article: Article One>",
             "<Article: Boring article>"])
        Article.objects.filter(headline__startswith='A').delete()
        self.assertQuerysetEqual(Article.objects.all().order_by('headline'),
            ["<Article: Boring article>"])

    def test_not_equal_and_equal_operators_behave_as_expected_on_instances(self):
        some_pub_date = datetime(2014, 5, 16, 12, 1)
        a1 = Article.objects.create(headline='First', pub_date=some_pub_date)
        a2 = Article.objects.create(headline='Second', pub_date=some_pub_date)
        self.assertNotEqual(a1, a2)
        self.assertEqual(a1, Article.objects.get(id__exact=a1.id))

        self.assertNotEqual(Article.objects.get(id__exact=a1.id), Article.objects.get(id__exact=a2.id))

    def test_multiple_objects_max_num_fetched(self):
        """
        #6785 - get() should fetch a limited number of results.
        """
        Article.objects.bulk_create(
            Article(headline='Area %s' % i, pub_date=datetime(2005, 7, 28))
            for i in range(MAX_GET_RESULTS)
        )
        six.assertRaisesRegex(
            self,
            MultipleObjectsReturned,
            "get\(\) returned more than one Article -- it returned %d!" % MAX_GET_RESULTS,
            Article.objects.get,
            headline__startswith='Area',
        )
        Article.objects.create(headline='Area %s' % MAX_GET_RESULTS, pub_date=datetime(2005, 7, 28))
        six.assertRaisesRegex(
            self,
            MultipleObjectsReturned,
            "get\(\) returned more than one Article -- it returned more than %d!" % MAX_GET_RESULTS,
            Article.objects.get,
            headline__startswith='Area',
        )

    @skipUnlessDBFeature('supports_microsecond_precision')
    def test_microsecond_precision(self):
        # In PostgreSQL, microsecond-level precision is available.
        a9 = Article(
            headline='Article 9',
            pub_date=datetime(2005, 7, 31, 12, 30, 45, 180),
        )
        a9.save()
        self.assertEqual(Article.objects.get(pk=a9.pk).pub_date,
            datetime(2005, 7, 31, 12, 30, 45, 180))

    @skipIfDBFeature('supports_microsecond_precision')
    def test_microsecond_precision_not_supported(self):
        # In MySQL, microsecond-level precision isn't available. You'll lose
        # microsecond-level precision once the data is saved.
        a9 = Article(
            headline='Article 9',
            pub_date=datetime(2005, 7, 31, 12, 30, 45, 180),
        )
        a9.save()
        self.assertEqual(Article.objects.get(id__exact=a9.id).pub_date,
            datetime(2005, 7, 31, 12, 30, 45))

    def test_manually_specify_primary_key(self):
        # You can manually specify the primary key when creating a new object.
        a101 = Article(
            id=101,
            headline='Article 101',
            pub_date=datetime(2005, 7, 31, 12, 30, 45),
        )
        a101.save()
        a101 = Article.objects.get(pk=101)
        self.assertEqual(a101.headline, 'Article 101')

    def test_create_method(self):
        # You can create saved objects in a single step
        a10 = Article.objects.create(
            headline="Article 10",
            pub_date=datetime(2005, 7, 31, 12, 30, 45),
        )
        self.assertEqual(Article.objects.get(headline="Article 10"), a10)

    def test_year_lookup_edge_case(self):
        # Edge-case test: A year lookup should retrieve all objects in
        # the given year, including Jan. 1 and Dec. 31.
        Article.objects.create(
            headline='Article 11',
            pub_date=datetime(2008, 1, 1),
        )
        Article.objects.create(
            headline='Article 12',
            pub_date=datetime(2008, 12, 31, 23, 59, 59, 999999),
        )
        self.assertQuerysetEqual(Article.objects.filter(pub_date__year=2008),
            ["<Article: Article 11>", "<Article: Article 12>"])

    def test_unicode_data(self):
        # Unicode data works, too.
        a = Article(
            headline='\u6797\u539f \u3081\u3050\u307f',
            pub_date=datetime(2005, 7, 28),
        )
        a.save()
        self.assertEqual(Article.objects.get(pk=a.id).headline,
            '\u6797\u539f \u3081\u3050\u307f')

    def test_hash_function(self):
        # Model instances have a hash function, so they can be used in sets
        # or as dictionary keys. Two models compare as equal if their primary
        # keys are equal.
        a10 = Article.objects.create(
            headline="Article 10",
            pub_date=datetime(2005, 7, 31, 12, 30, 45),
        )
        a11 = Article.objects.create(
            headline='Article 11',
            pub_date=datetime(2008, 1, 1),
        )
        a12 = Article.objects.create(
            headline='Article 12',
            pub_date=datetime(2008, 12, 31, 23, 59, 59, 999999),
        )