# -*- coding: utf-8 -*-
from decimal import Decimal
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.conf import settings
from ccbasket.models import Basket, BasketItem
from ccbasket.signals import pre_remove_from_basket, post_remove_from_basket,\
                                pre_add_to_basket, post_add_to_basket,\
                                basket_is_now_not_empty, basket_is_now_empty
from ccbasket_testproject.shop.models import SimpleProduct

# perhaps globals are ok for this one purpose
test_dict = {}
# define a test_receiver
def test_receiver(**kwargs):
    global test_dict
    test_dict = kwargs

class SignalTestCases(TestCase):
    """various signals are sent in the basket process, these tests ensure
    they operate as advertised.
    """

    def setUp(self):
        # set up the test list as empty dict
        global test_dict
        test_dict = {}
        # define a product to use
        item = SimpleProduct()
        item.price = Decimal("199.99")
        item.title = 'Test Product'
        item.save()
        self.item = item
        # get the basket
        r = self.client.get(reverse('ccbasket:basket'))
        self.basket = self.client.session['basket']
        # delete cache
        self.basket._delete_cache()

    def tearDown(self):
        global test_dict
        test_dict = {}
        # disconnect signals
        pre_add_to_basket.disconnect(dispatch_uid='pre_test_rec')
        post_add_to_basket.disconnect(dispatch_uid='post_test_rec')

    def test_basket_is_now_not_empty(self):
        """Test basket_is_now_not_empty is sent"""
        # were using a global
        global test_dict
        # connect the signal 
        basket_is_now_not_empty.connect(test_receiver,
            dispatch_uid='not_empty_test')
        # add item remove item and validate kwargs
        basketitem = self.basket.add(self.item)
        item = self.basket.remove(basketitem)
        self.assertEqual(test_dict['signal'], basket_is_now_not_empty)
        self.assertEqual(test_dict['sender'], Basket)
        # disconnect the signal 
        basket_is_now_not_empty.disconnect(dispatch_uid='not_empty_test')

    def test_basket_is_now_empty(self):
        """Test basket_is_now_empty from basket is sent"""
        # were using a global
        global test_dict
        # connect the signal 
        basket_is_now_empty.connect(test_receiver,
            dispatch_uid='now_empty_test')
        # add item remove item and validate kwargs
        basketitem = self.basket.add(self.item)
        item = self.basket.remove(basketitem)
        self.assertEqual(test_dict['signal'], basket_is_now_empty)
        self.assertEqual(test_dict['sender'], Basket)
        # disconnect the signal 
        basket_is_now_empty.disconnect(dispatch_uid='now_empty_test')


    def test_pre_remove_from_basket(self):
        """Test pre_remove_from_basket from basket is sent"""
        # were using a global
        global test_dict
        # connect the signal 
        pre_remove_from_basket.connect(test_receiver,
            dispatch_uid='pre_remove_test')
        # add item remove item and validate kwargs
        basketitem = self.basket.add(self.item)
        item = self.basket.remove(basketitem)
        self.assertEqual(test_dict['signal'], pre_remove_from_basket)
        self.assertEqual(test_dict['sender'], Basket)
        # disconnect the signal 
        pre_remove_from_basket.disconnect(dispatch_uid='pre_remove_test')
        #

    def test_pre_remove_from_basket_do_not_add(self):
        """Test pre_remove_from_basket doesn't remove if do_not_remove
        is True is returned from a receiver"""
        
        def do_not_remove(sender,instance, **kwargs):
            return {
                'do_not_remove' : True
            }
        # connect
        pre_remove_from_basket.connect(do_not_remove,
                    dispatch_uid='pre_remove_test')
        # add item remove item and validate kwargs
        basketitem = self.basket.add(self.item)
        item = self.basket.remove(basketitem)
        # still one and item is false
        self.assertEqual(1, self.basket.total_items())
        self.assertEqual(False, item)
        # disconnect 
        pre_remove_from_basket.connect(do_not_remove,
                    dispatch_uid='pre_remove_test')

    def test_post_remove_from_basket(self):
        """Test post_remove_from_basket is sent"""
        # were using a global
        global test_dict
        # connect the signal 
        post_remove_from_basket.connect(test_receiver,
            dispatch_uid='post_remove_test')
        # add item remove item and validate kwargs
        basketitem = self.basket.add(self.item)
        item = self.basket.remove(basketitem)
        self.assertEqual(test_dict['signal'], post_remove_from_basket)
        self.assertEqual(test_dict['sender'], Basket)
        # disconnect the signal 
        post_remove_from_basket.disconnect(dispatch_uid='post_remove_test')

    def test_do_not_add(self):
        # make the products
        hammer = SimpleProduct(
            title="Hammer",
            price="4.99")
        hammer.save()
        # crude receiver
        def do_not_add(sender,instance, **kwargs):
            return {'do_not_add' : True}
        # connect it
        pre_add_to_basket.connect(do_not_add, dispatch_uid='do_not_add')
        # add to basket - None
        basketitem = self.basket.add(hammer)
        self.assertEqual(0, self.basket.total_items())
        # disconnect it
        pre_add_to_basket.disconnect(dispatch_uid='do_not_add')
        # add to basket now and it will go in
        basketitem = self.basket.add(hammer)
        self.assertEqual(1, self.basket.total_items())

    def test_buy_hammer_get_nails_example(self):
        """Test the example as described in the docs
        /signals.html#an-example-of-post-add-to-basket"""
        # make the products
        hammer = SimpleProduct(
            title="Hammer",
            price="4.99")
        hammer.save()
        nails = SimpleProduct(
            title="Bag of 500 Nails",
            price="1.99")
        nails.save()
        #
        def free_nails_with_every_hammer(sender,
                        instance,
                        basketitem,
                        **kwargs):
            if basketitem.content_object.title == 'Hammer':
                nails = SimpleProduct.objects.get(title="Bag of 500 Nails")
                instance.add(nails,
                            price=Decimal("0.00"),
                            description="Free nails with every hammer",
                            silent=True,
                            locked=True)
        # connect signal
        post_add_to_basket.connect(free_nails_with_every_hammer,
            dispatch_uid='free_nails')
        # basket has zero
        self.assertEqual(0, self.basket.total_items())
        # add hammer
        basketitem = self.basket.add(hammer)
        # now have two items
        self.assertEqual(2, self.basket.total_items())
        # 1st: hammer, 1 , 4.99, locked=False
        self.assertEqual(
            self.basket.basketitem_set.all()[0].description,
            "Hammer")
        self.assertEqual(1, self.basket.basketitem_set.filter(
                    description="Hammer").count())
        self.assertEqual(
            self.basket.basketitem_set.all()[0].price,
            Decimal("4.99"))
        self.assertEqual(
            self.basket.basketitem_set.all()[0].locked,
            False)
        # 2nd: free nails, 1 , 0.00, locked=True
        self.assertEqual(
            self.basket.basketitem_set.all()[1].description,
            "Free nails with every hammer")
        self.assertEqual(1, self.basket.basketitem_set.filter(
                    description="Free nails with every hammer").count())
        self.assertEqual(
            self.basket.basketitem_set.all()[1].price,
            Decimal("0.00"))
        self.assertEqual(
            self.basket.basketitem_set.all()[1].locked,
            True)
        # disconnect
        post_add_to_basket.disconnect(dispatch_uid='free_nails')

    def test_buy_one_get_one_free_example(self):
        """Test as described in the docs
        /signals.html#an-example-of-pre-add-to-basket"""
        # make the product
        hammer = SimpleProduct(
            title="Hammer",
            price="4.99")
        hammer.save()
        # define the receiver
        def example_buy_one_get_one_free(sender,
                    instance,
                    item,
                    quantity,
                    **kwargs):
            # insert a hammer, with title 'buy one get one free' and a price
            # of 0.00
            if item.name == "Hammer":
                instance.add(hammer,
                        price="0.00",
                        description="Free Hammer! Buy One Get One Free",
                        locked=True,
                        silent=True)
        # connect it
        pre_add_to_basket.connect(example_buy_one_get_one_free,
            dispatch_uid='bogof')
        # our basket has no items
        self.assertEqual(0, self.basket.total_items())
        # add one hammer, quantity of one assumed by add method
        basketitem = self.basket.add(hammer)
        # now we have two basketitems
        self.assertEqual(2, self.basket.total_items())
        # The first is the free hammer
        self.assertEqual(
            self.basket.basketitem_set.all()[0].description,
            "Free Hammer! Buy One Get One Free")
        self.assertEqual(
            self.basket.basketitem_set.all()[0].price,
            Decimal("0.00"))
        # it's locked so the user can't mess with it
        self.assertEqual(
            self.basket.basketitem_set.all()[0].locked,
            True)
        # second is the paid for hammer
        self.assertEqual(
            self.basket.basketitem_set.all()[1].description,
            "Hammer")
        self.assertEqual(
            self.basket.basketitem_set.all()[1].price,
            Decimal("4.99"))
        self.assertEqual(
            self.basket.basketitem_set.all()[1].locked,
            False)
        # disconnect it
        pre_add_to_basket.disconnect('bogof')

    def test_pre_add_to_basket_multiple_receivers(self):
        """Attach multiple receivers and make sure that there is no bork"""
        # define three listeners
        def modify_description(sender, instance, **kwargs):
            return {
                'description' : 'Ho ho ho and a bottle of rum'
            }
        
        def modify_quantity(sender, instance, **kwargs):
            return {
                'quantity' : 2
            }
        
        def modify_price(sender, instance, **kwargs):
            return {
                'price' : Decimal('0.01')
            }
        # connect them
        pre_add_to_basket.connect(modify_description, dispatch_uid='md')
        pre_add_to_basket.connect(modify_quantity, dispatch_uid='mq')
        pre_add_to_basket.connect(modify_price, dispatch_uid='mp')
        # no basketitems to start with
        self.assertEqual(0, BasketItem.objects.count())
        #  add to 1 to basket and we should end up with..
        basketitem = self.basket.add(self.item, quantity=1)
        # 2 items
        self.assertEqual(2, BasketItem.objects.all()[0].quantity)
        # costing 0.02
        self.assertEqual(self.basket.total_price(), Decimal('0.02'))
        # disconnect
        pre_add_to_basket.disconnect(dispatch_uid='md')
        pre_add_to_basket.disconnect(dispatch_uid='mq')
        pre_add_to_basket.disconnect(dispatch_uid='mp')

    def test_pre_add_to_basket_kwargs(self):
        """Test pre_add_to_basket is sent"""
        # disconnect the post add to baskets 
        post_add_to_basket.disconnect(dispatch_uid='post_test_rec')
        # were using a global
        global test_dict
        # connect the signal
        pre_add_to_basket.connect(test_receiver, dispatch_uid='pre_test_rec')
        # test_dict is empty
        self.assertEqual(test_dict.keys(), [])
        # add a product to the basket
        basketitem = self.basket.add(self.item, quantity=2)
        # test_dict now has all the right keys
        self.assertEqual(test_dict['signal'], pre_add_to_basket)
        self.assertEqual(test_dict['sender'], Basket)
        self.assertEqual(test_dict['instance'], self.basket)
        self.assertEqual(test_dict['item'].title, self.item.title)
        self.assertEqual(test_dict['quantity'], 2)

    def test_pre_add_to_basket_modified_product(self):
        # define a reciver that provides some overrides
        def modify_product(sender, instance, **kwargs):
            return {
                'price' : Decimal('14000.00'),
                'quantity': 20,
                'description' : 'w00t, w00t. Its da police.'
            }
        # connect it
        pre_add_to_basket.connect(modify_product, dispatch_uid='pre_test_mod')
        # add to basket
        basketitem = self.basket.add(self.item, quantity=1)
        # basket now has 20 items
        self.assertEqual(20, self.basket.total_items())
        # and costs
        self.assertEqual(Decimal('280000.00'), self.basket.total_price())
        # but original item is untouched at the database level
        self.assertEqual(Decimal("199.99"), self.item.price)

    def test_post_add_to_basket_kwargs(self):
        """Test post_add_to_basket is sent"""
        # were using a global
        global test_dict
        # connect the signal 
        post_add_to_basket.connect(test_receiver, dispatch_uid='post_test_rec')
        # test_dict is empty
        self.assertEqual(test_dict.keys(), [])
        # add a product to the basket
        basketitem = self.basket.add(self.item, quantity=2)
        # test_dict now has all the right keys
        self.assertEqual(test_dict['signal'], post_add_to_basket)
        self.assertEqual(test_dict['sender'], Basket)
        self.assertEqual(test_dict['basketitem'].description,
                            basketitem.description)
        self.assertEqual(test_dict['instance'], self.basket)





