from decimal import Decimal as D
import sys

from django.core.urlresolvers import reverse
from django.conf import settings
from django.utils.importlib import import_module

from oscar.test.helpers import create_product, create_voucher
from oscar.test import ClientTestCase, patch_settings
from oscar.apps.basket.models import Basket
from oscar.apps.order.models import Order
from oscar.apps.address.models import Country
from oscar.apps.voucher.models import Voucher


class CheckoutMixin(object):

    def add_product_to_basket(self):
        product = create_product(price=D('12.00'))
        self.client.post(reverse('basket:add'), {'product_id': product.id,
                                                 'quantity': 1})

    def add_voucher_to_basket(self, voucher=None):
        if voucher is None:
            voucher = create_voucher()
        self.client.post(reverse('basket:vouchers-add'),
                         {'code': voucher.code})

    def complete_guest_email_form(self, email='test@example.com'):
        response = self.client.post(reverse('checkout:index'),
                                    {'username': email,
                                     'options': 'new'})
        self.assertIsRedirect(response)

    def complete_shipping_address(self):
        Country.objects.get_or_create(
            iso_3166_1_a2='GB',
            is_shipping_country=True
        )
        response = self.client.post(reverse('checkout:shipping-address'),
                                     {'last_name': 'Doe',
                                      'line1': '1 Egg Street',
                                      'postcode': 'N1 9RT',
                                      'country': 'GB',
                                     })
        self.assertRedirectUrlName(response, 'checkout:shipping-method')

    def complete_shipping_method(self):
        self.client.get(reverse('checkout:shipping-method'))

    def submit(self):
        return self.client.post(reverse('checkout:preview'), {'action': 'place_order'})


class DisabledAnonymousCheckoutViewsTests(ClientTestCase):
    is_anonymous = True

    def test_index_does_require_login(self):
        url = reverse('checkout:index')
        response = self.client.get(url)
        self.assertIsRedirect(response)

    def test_user_address_views_require_a_login(self):
        urls = [reverse('checkout:user-address-create'),
                reverse('checkout:user-address-update', kwargs={'pk': 1}),
                reverse('checkout:user-address-delete', kwargs={'pk': 1}),]
        for url in urls:
            response = self.client.get(url)
            self.assertIsRedirect(response)

    def test_core_checkout_requires_login(self):
        urls = [reverse('checkout:shipping-address'),
                reverse('checkout:payment-method'),
                reverse('checkout:shipping-method'),
                reverse('checkout:payment-details')]
        for url in urls:
            response = self.client.get(url)
            self.assertIsRedirect(response)


class EnabledAnonymousCheckoutViewsTests(ClientTestCase, CheckoutMixin):
    is_anonymous = True

    def reload_urlconf(self):
        if settings.ROOT_URLCONF in sys.modules:
            reload(sys.modules[settings.ROOT_URLCONF])
        return import_module(settings.ROOT_URLCONF)

    def add_product_to_basket(self):
        product = create_product(price=D('12.00'))
        self.client.post(reverse('basket:add'), {'product_id': product.id,
                                                 'quantity': 1})

    def test_shipping_address_does_require_session_email_address(self):
        with patch_settings(OSCAR_ALLOW_ANON_CHECKOUT=True):
            self.reload_urlconf()
            url = reverse('checkout:shipping-address')
            response = self.client.get(url)
            self.assertIsRedirect(response)

    def test_email_address_is_saved_with_order(self):
        with patch_settings(OSCAR_ALLOW_ANON_CHECKOUT=True):
            self.reload_urlconf()
            self.add_product_to_basket()
            self.complete_guest_email_form('barry@example.com')
            self.complete_shipping_address()
            self.complete_shipping_method()
            response = self.client.post(reverse('checkout:preview'), {'action': 'place_order'})
            response = self.client.get(reverse('checkout:thank-you'))
            order = response.context['order']
            self.assertEqual('barry@example.com', order.guest_email)


class TestShippingAddressView(ClientTestCase, CheckoutMixin):
    fixtures = ['countries.json']

    def test_pages_returns_200(self):
        self.add_product_to_basket()
        response = self.client.get(reverse('checkout:shipping-address'))
        self.assertIsOk(response)

    def test_anon_checkout_disabled_by_default(self):
        self.assertFalse(settings.OSCAR_ALLOW_ANON_CHECKOUT)

    def test_create_shipping_address_adds_address_to_session(self):
        response = self.client.post(reverse('checkout:shipping-address'),
                                            {'last_name': 'Doe',
                                             'line1': '1 Egg Street',
                                             'postcode': 'N1 9RT',
                                             'country': 'GB',
                                            })
        self.assertIsRedirect(response)
        session_address = self.client.session['checkout_data']['shipping']['new_address_fields']
        self.assertEqual('Doe', session_address['last_name'])
        self.assertEqual('1 Egg Street', session_address['line1'])
        self.assertEqual('N1 9RT', session_address['postcode'])

    def test_user_must_have_a_nonempty_basket(self):
        response = self.client.get(reverse('checkout:shipping-address'))
        self.assertRedirectUrlName(response, 'basket:summary')


class TestShippingMethodView(ClientTestCase, CheckoutMixin):
    fixtures = ['countries.json']

    def test_shipping_method_view_redirects_if_no_shipping_address(self):
        self.add_product_to_basket()
        response = self.client.get(reverse('checkout:shipping-method'))
        self.assertIsRedirect(response)
        self.assertRedirectUrlName(response, 'checkout:shipping-address')

    def test_redirects_by_default(self):
        self.add_product_to_basket()
        self.complete_shipping_address()
        response = self.client.get(reverse('checkout:shipping-method'))
        self.assertRedirectUrlName(response, 'checkout:payment-method')

    def test_user_must_have_a_nonempty_basket(self):
        response = self.client.get(reverse('checkout:shipping-method'))
        self.assertRedirectUrlName(response, 'basket:summary')


class TestPaymentMethodView(ClientTestCase, CheckoutMixin):

    def test_view_redirects_if_no_shipping_address(self):
        self.add_product_to_basket()
        response = self.client.get(reverse('checkout:payment-method'))
        self.assertIsRedirect(response)
        self.assertRedirectUrlName(response, 'checkout:shipping-address')

    def test_view_redirects_if_no_shipping_method(self):
        self.add_product_to_basket()
        self.complete_shipping_address()
        response = self.client.get(reverse('checkout:payment-method'))
        self.assertIsRedirect(response)
        self.assertRedirectUrlName(response, 'checkout:shipping-method')

    def test_user_must_have_a_nonempty_basket(self):
        response = self.client.get(reverse('checkout:payment-method'))
        self.assertRedirectUrlName(response, 'basket:summary')


class TestPreviewView(ClientTestCase, CheckoutMixin):

    def test_user_must_have_a_nonempty_basket(self):
        response = self.client.get(reverse('checkout:preview'))
        self.assertRedirectUrlName(response, 'basket:summary')

    def test_view_redirects_if_no_shipping_address(self):
        self.add_product_to_basket()
        response = self.client.get(reverse('checkout:preview'))
        self.assertIsRedirect(response)
        self.assertRedirectUrlName(response, 'checkout:shipping-address')

    def test_view_redirects_if_no_shipping_method(self):
        self.add_product_to_basket()
        self.complete_shipping_address()
        response = self.client.get(reverse('checkout:preview'))
        self.assertIsRedirect(response)
        self.assertRedirectUrlName(response, 'checkout:shipping-method')

    def test_ok_response_if_previous_steps_complete(self):
        self.add_product_to_basket()
        self.complete_shipping_address()
        self.complete_shipping_method()
        response = self.client.get(reverse('checkout:preview'))
        self.assertIsOk(response)


class TestPaymentDetailsView(ClientTestCase, CheckoutMixin):

    def test_user_must_have_a_nonempty_basket(self):
        response = self.client.get(reverse('checkout:payment-details'))
        self.assertRedirectUrlName(response, 'basket:summary')

    def test_view_redirects_if_no_shipping_address(self):
        self.add_product_to_basket()
        response = self.client.post(reverse('checkout:payment-details'))
        self.assertIsRedirect(response)
        self.assertRedirectUrlName(response, 'checkout:shipping-address')

    def test_view_redirects_if_no_shipping_method(self):
        self.add_product_to_basket()
        self.complete_shipping_address()
        response = self.client.post(reverse('checkout:payment-details'))
        self.assertIsRedirect(response)
        self.assertRedirectUrlName(response, 'checkout:shipping-method')

    def test_placing_order_with_empty_basket_redirects(self):
        response = self.client.post(reverse('checkout:preview'), {'action': 'place_order'})
        self.assertIsRedirect(response)
        self.assertRedirectUrlName(response, 'basket:summary')


class TestOrderPlacement(ClientTestCase, CheckoutMixin):

    def setUp(self):
        Order.objects.all().delete()

        super(TestOrderPlacement, self).setUp()
        self.basket = Basket.objects.create(owner=self.user)
        self.basket.add_product(create_product(price=D('12.00')))

        self.complete_shipping_address()
        self.complete_shipping_method()
        self.response = self.client.post(reverse('checkout:preview'), {'action': 'place_order'})

    def test_placing_order_with_nonempty_basket_redirects(self):
        self.assertIsRedirect(self.response)
        self.assertRedirectUrlName(self.response, 'checkout:thank-you')

    def test_order_is_created(self):
        self.assertIsRedirect(self.response)
        orders = Order.objects.all()
        self.assertEqual(1, len(orders))


class TestPlacingOrderUsingAVoucher(ClientTestCase, CheckoutMixin):

    def setUp(self):
        self.login()
        self.add_product_to_basket()
        voucher = create_voucher()
        self.add_voucher_to_basket(voucher)
        self.complete_shipping_address()
        self.complete_shipping_method()
        self.response = self.submit()

        # Reload voucher
        self.voucher = Voucher.objects.get(id=voucher.id)

    def test_is_successful(self):
        self.assertRedirectUrlName(self.response, 'checkout:thank-you')

    def test_records_use(self):
        self.assertEquals(1, self.voucher.num_orders)

    def test_records_discount(self):
        self.assertEquals(1, self.voucher.num_orders)
