# -*- coding: utf-8 -*-
from decimal import Decimal
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.test import TestCase
from ccbasket.models import Basket, BasketItem
from ccbasket_testproject.shop.models import SimpleProduct

class UnicodeErrorTests(TestCase):
    
    def setUp(self):
        # make a product
        item = SimpleProduct()
        item.price = Decimal("199.99")
        item.title = 'Yamaha Manu Katché Signature Junior Hip Gig Kit in Black'
        item.description = "Manu Katché famous for playing drums for the likes of Peter Gabriel and Sting announced to Yamaha \"Why don't you make a real drum kit for kids?\" The design team for Yamaha got to work and here are the results. A fantastic Philippine mahogany shell drum kit that delivers a loud, warm and an open tone. The bass drum is made from 9 ply while the toms and snare drum are made up from 6-ply. Each ply is laminated together using the exclusive Air Seal System ensuring that every shell is a uniform thickness. Finished off with a tough and very durable lacquer. This is an ideal drum kit for a beginner to the professional. Due to its sizes this drum kit can fit into most rooms without taking up too much room making this a perfect compact portable/practice drum kit"
        item.save()
        self.item = item
        # get the basket
        r = self.client.get(reverse('ccbasket:basket'))
        self.basket = self.client.session['basket']


    def test_unicode_doesnt_bork(self):
        """unicode is no problem for the basket"""
        basketitem = self.basket.add(self.item)


class BasketItemTestCases(TestCase):
    
    def setUp(self):
        # make a product
        item = SimpleProduct()
        item.price = Decimal("199.99")
        item.title = 'Test Product'
        item.save()
        self.item = item
        # 
        item2 = SimpleProduct()
        item2.price = Decimal("9.99")
        item2.title = 'Another Product'
        item2.save()
        self.item2 = item2
        # get the basket
        r = self.client.get(reverse('ccbasket:basket'))
        self.basket = self.client.session['basket']

    def test_hash_on_save(self):
        """When a basket line is saved, it get's a hash based on it
        content_type, object_id, description & price """
        b = BasketItem()
        b.content_type = ContentType.objects.get(
                    app_label=self.item._meta.app_label,
                    model=self.item._meta.object_name.lower())
        b.object_id = self.item.pk
        b.basket = self.basket
        b.description = self.item.title
        b.price = self.item.price
        b.save()
        # now it has a hash
        self.assertEqual(32, len(b.hash))
        


class BasketTestCases(TestCase):
    """
    Products can be added to the basket as outlined in the documentation.
    """
    
    def setUp(self):
        # make a product
        item = SimpleProduct()
        item.price = Decimal("199.99")
        item.title = 'Test Product'
        item.save()
        self.item = item
        # 
        item2 = SimpleProduct()
        item2.price = Decimal("9.99")
        item2.title = 'Another Product'
        item2.save()
        self.item2 = item2
        # get the basket
        r = self.client.get(reverse('ccbasket:basket'))
        self.basket = self.client.session['basket']

    def tearDown(self):
        # kill cache
        self.basket._delete_cache()

    def test_basket_empty(self):
        """baskets need to have an empty method that handles the complete
        anhiliation of a basket"""
        # 0 everything
        self.assertEqual(0, self.basket.total_items())
        self.assertEqual(Decimal("0.00"), self.basket.total_price())
        self.assertEqual(0, BasketItem.objects.count())
        # add some stuff to basket and request price and items
        self.basket.add(self.item)
        # have stuff
        self.assertEqual(1, self.basket.total_items())
        self.assertEqual(Decimal("199.99"), self.basket.total_price())
        self.assertEqual(1, BasketItem.objects.count())
        # empty the basket
        self.basket.empty()
        # 0 everything
        self.assertEqual(0, self.basket.total_items())
        self.assertEqual(Decimal("0.00"), self.basket.total_price())
        self.assertEqual(0, BasketItem.objects.count())

    def test_basketlines_removing_quantity_too_many(self):
        """It is assumed that all items would be removed from the basket, but
        this may not be the case. Ensure that if a quantity is specified to
        the remove method that it removes the desired amount"""
        # no basketitems
        self.assertEqual(0, self.basket.total_items())
        # add 2 x item
        basketitem2 = self.basket.add(self.item2, quantity=2)
        self.assertEqual(2, self.basket.total_items())
        # now remove 30, even though we only have 2
        self.basket.remove(basketitem2, quantity=30)
        # we now have 0 items in the basket
        self.assertEqual(0, self.basket.total_items())

    def test_basketlines_removing_quantity(self):
        """It is assumed that all items would be removed from the basket, but
        this may not be the case. Ensure that if a quantity is specified to
        the remove method that it removes the desired amount"""
        # no basketitems
        self.assertEqual(0, BasketItem.objects.count())
        # add 33 x item
        basketitem2 = self.basket.add(self.item2, quantity=33)
        # now remove it
        self.basket.remove(basketitem2)
        # we now have 0 items in the basket
        self.assertEqual(0, self.basket.total_items())

    def test_basketlines_removing(self):
        """Ensure the correct amount of basketlines are created when adding
        to the basket"""
        # no basketitems
        self.assertEqual(0, BasketItem.objects.count())
        # add 33 x item
        basketitem2 = self.basket.add(self.item2, quantity=33)
        # add 2 x another item
        basketitem = self.basket.add(self.item, quantity=2)
        # now have two items
        self.assertEqual(2, BasketItem.objects.count())
        # delete the first
        self.basket.remove(basketitem2)
        # now have one
        self.assertEqual(1, BasketItem.objects.count())
        # remove the other and we now have none
        self.basket.remove(basketitem)
        self.assertEqual(0, BasketItem.objects.count())

    def test_basketlines_adding(self):
        """Ensure the correct amount of basketlines are created when adding
        to the basket"""
        # no basketitems
        self.assertEqual(0, BasketItem.objects.count())
        # add something
        self.basket.add(self.item2)
        # one basketitem
        self.assertEqual(1, BasketItem.objects.count())
        # add another to change quantity - still one
        self.basket.add(self.item2, quantity=33)
        self.assertEqual(1, BasketItem.objects.count())
        # add another item
        self.basket.add(self.item)
        # two basketitems
        self.assertEqual(2, BasketItem.objects.count())

    def test_basketline_is_returned_adding(self):
        """when adding to the basket the item that is added is returned"""
        # add item
        added = self.basket.add(self.item, quantity=2)
        # we now have the basketitem that was added
        self.assertEqual(added.content_object, self.item)

    def test_basketline_is_returned_removing(self):
        """when removing from the basket the item that is added is returned"""
        # add item
        basketline = self.basket.add(self.item, quantity=2)
        # remove it
        removed = self.basket.remove(basketline)
        # we now have the item that was removed
        self.assertEqual(removed, self.item)

    def test_remove_item(self):
        """Removing items works as expected"""
        # add two items
        basketitem = self.basket.add(self.item, quantity=2)
        basketitem2 = self.basket.add(self.item2, quantity=1)
        # we have 3 items
        self.assertEqual(3, self.basket.total_items())
        self.assertEqual(Decimal("409.97"), self.basket.total_price())
        # remove the self.item
        self.basket.remove(basketitem)
        # we have 1 items
        self.assertEqual(1, self.basket.total_items())
        self.assertEqual(Decimal("9.99"), self.basket.total_price())
        # remove that and we have none
        self.basket.remove(basketitem2)
        self.assertEqual(0, self.basket.total_items())
        self.assertEqual(Decimal("0.00"), self.basket.total_price())


    def test_negative_quantities_are_ignored(self):
        """ negative quantities are silently multipled by -1 to make them
        into a positive"""
        self.basket.add(self.item, quantity=-1)
        # 1 item. 199.99
        self.assertEqual(1, self.basket.total_items())
        self.assertEqual(Decimal("199.99"), self.basket.total_price())

    def test_add_single_product_quantity_of_ten(self):
        """Add one item with a quantity of ten"""
        self.basket.add(self.item, quantity=10)
        # 10 items,  1999.90
        self.assertEqual(10, self.basket.total_items())
        self.assertEqual(Decimal("1999.90"), self.basket.total_price())

    def test_add_single_products_twice(self):
        """Add a single product, twice and the basket behaves as expected"""
        self.basket.add(self.item)
        self.basket.add(self.item)
        # 2 item,  399.98
        self.assertEqual(2, self.basket.total_items())
        self.assertEqual(Decimal("399.98"), self.basket.total_price())

    def test_add_single_products(self):
        """Add a single product and the basket behaves as expected"""
        self.basket.add(self.item)
        # 1 item. 199.99
        self.assertEqual(1, self.basket.total_items())
        self.assertEqual(Decimal("199.99"), self.basket.total_price())

    def test_empty_basket(self):
        """When adding a single item into the basket everything
        behaves as expected
        """
        # nothing
        self.assertEqual(0, self.basket.total_items())
        self.assertEqual(Decimal("0.00"), self.basket.total_price())

class BasketsHaveCorrectAttributes(TestCase):
    """The basket stores the sessionid of the user
    as the means of identifying who a basket belongs
    to.
    
    A created timestamp and last modified is also present.
    """
    
    def test_has_sessionkey(self):
        """A basket has a session key that can be used to
        get the basket for a given user"""
        # no basket
        self.assertFalse(self.client.session.get('basket'))
        # make a request and we have a basket
        r = self.client.get('/')
        self.assertEqual(
            self.client.session['basket'].session_key,
            self.client.session.session_key)
        # can use this to get it from the db
        self.assertEqual(self.client.session['basket'].pk,
                    Basket.objects.get(pk=self.client.session['basket'].pk).pk)

    def test_has_created_and_modified(self):
        """A basket gets a created and last_modified timestamp when it is
         first created"""
        # no basket
        self.assertFalse(self.client.session.get('basket'))
        # make a request and we have a basket
        r = self.client.get('/')
        self.failIfEqual(self.client.session['basket'].created, None)
        self.failIfEqual(self.client.session['basket'].created, None)


