from django.test import TestCase
from django.contrib.auth import get_user_model
from datetime import datetime, date, timedelta

from .models import *
from uncloud_service.models import GenericServiceProduct

class NotABillingTC(TestCase):
#class BillingTestCase(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create(
                username='jdoe',
                email='john.doe@domain.tld')
        self.billing_address = BillingAddress.objects.create(
                owner=self.user,
                street="unknown",
                city="unknown",
                postal_code="unknown")

    def test_basic_monthly_billing(self):
        one_time_price = 10
        recurring_price = 20
        description = "Test Product 1"

        # Three months: full, full, partial.
#        starting_date = datetime.fromisoformat('2020-03-01')
        starting_date = datetime(2020,3,1)
        ending_date   = datetime(2020,5,8)

        # Create order to be billed.
        order = Order.objects.create(
                owner=self.user,
                starting_date=starting_date,
                ending_date=ending_date,
                recurring_period=RecurringPeriod.PER_30D,
                recurring_price=recurring_price,
                one_time_price=one_time_price,
                description=description,
                billing_address=self.billing_address)

        # Generate & check bill for first month: full recurring_price + setup.
        first_month_bills = order.generate_initial_bill()
        self.assertEqual(len(first_month_bills), 1)
        self.assertEqual(first_month_bills[0].amount, one_time_price + recurring_price)

        # Generate & check bill for second month: full recurring_price.
        second_month_bills = Bill.generate_for(2020, 4, self.user)
        self.assertEqual(len(second_month_bills), 1)
        self.assertEqual(second_month_bills[0].amount, recurring_price)

        # Generate & check bill for third and last month: partial recurring_price.
        third_month_bills = Bill.generate_for(2020, 5, self.user)
        self.assertEqual(len(third_month_bills), 1)
        # 31 days in May.
        self.assertEqual(float(third_month_bills[0].amount),
                round(round((7/31), AMOUNT_DECIMALS) * recurring_price, AMOUNT_DECIMALS))

        # Check that running Bill.generate_for() twice does not create duplicates.
        self.assertEqual(len(Bill.generate_for(2020, 3, self.user)), 0)

    def test_basic_yearly_billing(self):
        one_time_price = 10
        recurring_price = 150
        description = "Test Product 1"

        starting_date = datetime.fromisoformat('2020-03-31T08:05:23')

        # Create order to be billed.
        order = Order.objects.create(
                owner=self.user,
                starting_date=starting_date,
                recurring_period=RecurringPeriod.PER_365D,
                recurring_price=recurring_price,
                one_time_price=one_time_price,
                description=description,
                billing_address=self.billing_address)

        # Generate & check bill for first year: recurring_price + setup.
        first_year_bills = order.generate_initial_bill()
        self.assertEqual(len(first_year_bills), 1)
        self.assertEqual(first_year_bills[0].starting_date.date(),
                date.fromisoformat('2020-03-31'))
        self.assertEqual(first_year_bills[0].ending_date.date(),
                date.fromisoformat('2021-03-30'))
        self.assertEqual(first_year_bills[0].amount,
                recurring_price + one_time_price)

        # Generate & check bill for second year: recurring_price.
        second_year_bills = Bill.generate_for(2021, 3, self.user)
        self.assertEqual(len(second_year_bills), 1)
        self.assertEqual(second_year_bills[0].starting_date.date(),
                date.fromisoformat('2021-03-31'))
        self.assertEqual(second_year_bills[0].ending_date.date(),
                date.fromisoformat('2022-03-30'))
        self.assertEqual(second_year_bills[0].amount, recurring_price)

        # Check that running Bill.generate_for() twice does not create duplicates.
        self.assertEqual(len(Bill.generate_for(2020, 3, self.user)), 0)
        self.assertEqual(len(Bill.generate_for(2020, 4, self.user)), 0)
        self.assertEqual(len(Bill.generate_for(2020, 2, self.user)), 0)
        self.assertEqual(len(Bill.generate_for(2021, 3, self.user)), 0)

    def test_basic_hourly_billing(self):
        one_time_price = 10
        recurring_price = 1.4
        description = "Test Product 1"

        starting_date = datetime.fromisoformat('2020-03-31T08:05:23')
        ending_date   = datetime.fromisoformat('2020-04-01T11:13:32')

        # Create order to be billed.
        order = Order.objects.create(
                owner=self.user,
                starting_date=starting_date,
                ending_date=ending_date,
                recurring_period=RecurringPeriod.PER_HOUR,
                recurring_price=recurring_price,
                one_time_price=one_time_price,
                description=description,
                billing_address=self.billing_address)

        # Generate & check bill for first month: recurring_price + setup.
        first_month_bills = order.generate_initial_bill()
        self.assertEqual(len(first_month_bills), 1)
        self.assertEqual(float(first_month_bills[0].amount),
                round(16 * recurring_price, AMOUNT_DECIMALS) + one_time_price)

        # Generate & check bill for first month: recurring_price.
        second_month_bills = Bill.generate_for(2020, 4, self.user)
        self.assertEqual(len(second_month_bills), 1)
        self.assertEqual(float(second_month_bills[0].amount),
                round(12 * recurring_price, AMOUNT_DECIMALS))

class ProductActivationTestCase(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create(
                username='jdoe',
                email='john.doe@domain.tld')

        self.billing_address = BillingAddress.objects.create(
                owner=self.user,
                street="unknown",
                city="unknown",
                postal_code="unknown")

    def test_product_activation(self):
        starting_date = datetime.fromisoformat('2020-03-01')
        one_time_price = 0
        recurring_price = 1
        description = "Test Product"

        order = Order.objects.create(
                owner=self.user,
                starting_date=starting_date,
                recurring_period=RecurringPeriod.PER_30D,
                recurring_price=recurring_price,
                one_time_price=one_time_price,
                description=description,
                billing_address=self.billing_address)

        product = GenericServiceProduct(
                custom_description=description,
                custom_one_time_price=one_time_price,
                custom_recurring_price=recurring_price,
                owner=self.user,
                order=order)
        product.save()

        # Validate initial state: must be awaiting payment.
        self.assertEqual(product.status, UncloudStatus.AWAITING_PAYMENT)

        # Pay initial bill, check that product is activated.
        order.generate_initial_bill()
        amount = product.order.bills[0].amount
        payment = Payment(owner=self.user, amount=amount)
        payment.save()
        self.assertEqual(
                GenericServiceProduct.objects.get(uuid=product.uuid).status,
                UncloudStatus.PENDING
                )

class BillingAddressTestCase(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create(
                username='jdoe',
                email='john.doe@domain.tld')

        self.billing_address_01 = BillingAddress.objects.create(
                owner=self.user,
                street="unknown1",
                city="unknown1",
                postal_code="unknown1",
                country="CH")

        self.billing_address_02 = BillingAddress.objects.create(
                owner=self.user,
                street="unknown2",
                city="unknown2",
                postal_code="unknown2",
                country="CH")

    def test_billing_with_single_address(self):
        # Create new orders somewhere in the past so that we do not encounter
        # auto-created initial bills.
        starting_date = datetime.fromisoformat('2020-03-01')

        order_01 = Order.objects.create(
                owner=self.user,
                starting_date=starting_date,
                recurring_period=RecurringPeriod.PER_30D,
                billing_address=self.billing_address_01)
        order_02 = Order.objects.create(
                owner=self.user,
                starting_date=starting_date,
                recurring_period=RecurringPeriod.PER_30D,
                billing_address=self.billing_address_01)

        # We need a single bill since we work with a single address.
        bills = Bill.generate_for(2020, 4, self.user)
        self.assertEqual(len(bills), 1)

    def test_billing_with_multiple_addresses(self):
        # Create new orders somewhere in the past so that we do not encounter
        # auto-created initial bills.
        starting_date = datetime.fromisoformat('2020-03-01')

        order_01 = Order.objects.create(
                owner=self.user,
                starting_date=starting_date,
                recurring_period=RecurringPeriod.PER_30D,
                billing_address=self.billing_address_01)
        order_02 = Order.objects.create(
                owner=self.user,
                starting_date=starting_date,
                recurring_period=RecurringPeriod.PER_30D,
                billing_address=self.billing_address_02)

        # We need different bills since we work with different addresses.
        bills = Bill.generate_for(2020, 4, self.user)
        self.assertEqual(len(bills), 2)