from decimal import Decimal
import requests
import random
from datetime import date
from datetime import timedelta
from io import StringIO

from internet_sabotage import no_connection
from unittest.mock import patch
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.messages.storage.fallback import FallbackStorage
from django.contrib.sessions.middleware import SessionMiddleware
from django.core import mail
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.management import call_command
from django.db import transaction
from django.db.models import Q
from django.test import RequestFactory, TestCase, TransactionTestCase, override_settings
from django.urls import reverse

from django_concurrent_tests.helpers import call_concurrently

from freezegun import freeze_time

from model_bakery import baker

from plans import tasks
from plans.models import BillingInfo, PlanPricing, Invoice, Order, Plan, UserPlan
from plans.plan_change import PlanChangePolicy, StandardPlanChangePolicy
from plans.taxation.eu import EUTaxationPolicy
from plans.quota import get_user_quota
from plans.validators import ModelCountValidator
from plans.views import CreateOrderView

from unittest import mock


User = get_user_model()


class PlansTestCase(TestCase):
    fixtures = ['initial_plan', 'test_django-plans_auth', 'test_django-plans_plans']

    def setUp(self):
        mail.outbox = []

    def test_create_userplans_command(self):
        """ Test that create_userplans command creates userplan for users that doesn't have it """
        u = User.objects.get(username='test1')
        UserPlan.objects.all().delete()
        with self.assertRaises(UserPlan.DoesNotExist):
            u.userplan
        u.refresh_from_db()
        out = StringIO()
        call_command('create_userplans', stdout=out)
        self.assertIn('2 user plans was created', out.getvalue())
        default_plan = Plan.objects.get(pk=1)
        self.assertEqual(u.userplan.plan, default_plan)

    def test_create_userplans(self):
        """ Test that create_for_users_without_plan method """
        u = User.objects.get(username='test1')
        UserPlan.objects.all().delete()
        with self.assertRaises(UserPlan.DoesNotExist):
            u.userplan
        u.refresh_from_db()
        created_plans = UserPlan.create_for_users_without_plan()
        self.assertEqual(created_plans.count(), 2)
        default_plan = Plan.objects.get(pk=1)
        self.assertEqual(u.userplan.plan, default_plan)

    def test_get_user_quota(self):
        u = User.objects.get(username='test1')
        self.assertEqual(get_user_quota(u),
                         {u'CUSTOM_WATERMARK': 1, u'MAX_GALLERIES_COUNT': 3, u'MAX_PHOTOS_PER_GALLERY': None})

    def test_get_user_quota_expired_no_default(self):
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() - timedelta(days=5)
        Plan.get_default_plan().delete()
        with self.assertRaises(ValidationError):
            get_user_quota(u)

    def test_get_user_quota_expired_free_plan(self):
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() - timedelta(days=5)
        with self.assertRaises(ValidationError):
            get_user_quota(u)

    def test_get_plan_quota(self):
        u = User.objects.get(username='test1')
        p = u.userplan.plan
        self.assertEqual(p.get_quota_dict(),
                         {u'CUSTOM_WATERMARK': 1, u'MAX_GALLERIES_COUNT': 3, u'MAX_PHOTOS_PER_GALLERY': None})

    def test_extend_account_same_plan_future(self):
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() + timedelta(days=50)
        u.userplan.active = False
        u.userplan.save()
        plan_pricing = PlanPricing.objects.get(plan=u.userplan.plan, pricing__period=30)
        u.userplan.extend_account(plan_pricing.plan, plan_pricing.pricing)
        self.assertEqual(u.userplan.expire,
                         date.today() + timedelta(days=50) + timedelta(days=plan_pricing.pricing.period))
        self.assertEqual(u.userplan.plan, plan_pricing.plan)
        self.assertEqual(u.userplan.active, True)
        self.assertEqual(len(mail.outbox), 1)

    def test_extend_account_same_plan_before(self):
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() - timedelta(days=50)
        u.userplan.active = False
        u.userplan.save()
        plan_pricing = PlanPricing.objects.get(plan=u.userplan.plan, pricing__period=30)
        u.userplan.extend_account(plan_pricing.plan, plan_pricing.pricing)
        self.assertEqual(u.userplan.expire, date.today() + timedelta(days=plan_pricing.pricing.period))
        self.assertEqual(u.userplan.plan, plan_pricing.plan)
        self.assertEqual(u.userplan.active, True)
        self.assertEqual(len(mail.outbox), 1)

    def test_extend_account_other(self):
        """
        Tests extending expired account with other Plan that user had before:
        Tests if expire date is set correctly
        Tests if mail has been send
        Tests if account has been activated
        """
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() - timedelta(days=50)
        u.userplan.active = False
        u.userplan.save()
        plan_pricing = PlanPricing.objects.filter(~Q(plan=u.userplan.plan) & Q(pricing__period=30))[0]
        u.userplan.extend_account(plan_pricing.plan, plan_pricing.pricing)
        self.assertEqual(u.userplan.expire, date.today() + timedelta(days=plan_pricing.pricing.period))
        self.assertEqual(u.userplan.plan, plan_pricing.plan)
        self.assertEqual(u.userplan.active, True)
        self.assertEqual(len(mail.outbox), 1)

    @freeze_time("2012-01-14")
    def test_extend_account_other_expire_none(self):
        """
        Tests extending expired=None account with other Plan that user had before and is expired:
        Tests if expire stays None
        Tests if mail has been send
        Tests if account stays activated
        """
        u = User.objects.get(username='test1')
        u.userplan.expire = None
        u.userplan.active = False
        u.userplan.save()
        plan_pricing = PlanPricing.objects.filter(~Q(plan=u.userplan.plan) & Q(pricing__period=30))[0]
        default_plan = Plan.objects.get(pk=1)
        u.userplan.extend_account(plan_pricing.plan, plan_pricing.pricing)
        self.assertEqual(u.userplan.expire, date(2012, 2, 13))
        self.assertEqual(u.userplan.plan, default_plan)
        self.assertEqual(u.userplan.active, True)
        self.assertEqual(len(mail.outbox), 1)

    def test_extend_account_other_expire_future(self):
        """
        Tests extending active account with other Plan that user had before:
        Tests if expire date stays the same
        Tests if mail has not been send
        Tests if account has not been activated
        """
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() + timedelta(days=5)
        u.userplan.active = False
        u.userplan.save()
        plan_pricing = PlanPricing.objects.filter(~Q(plan=u.userplan.plan) & Q(pricing__period=30))[0]
        default_plan = Plan.objects.get(pk=1)
        u.userplan.extend_account(plan_pricing.plan, plan_pricing.pricing)
        self.assertEqual(u.userplan.expire, date.today() + timedelta(days=5))
        self.assertEqual(u.userplan.plan, default_plan)
        self.assertEqual(u.userplan.active, False)
        self.assertEqual(len(mail.outbox), 0)

    def test_expire_account(self):
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() + timedelta(days=50)
        u.userplan.active = True
        u.userplan.save()
        u.userplan.expire_account()
        self.assertEqual(u.userplan.active, False)
        self.assertEqual(len(mail.outbox), 1)

    def test_remind_expire(self):
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() + timedelta(days=14)
        u.userplan.active = True
        u.userplan.save()
        u.userplan.remind_expire_soon()
        self.assertEqual(u.userplan.active, True)
        self.assertEqual(len(mail.outbox), 1)

    def test_disable_emails(self):
        with self.settings(SEND_PLANS_EMAILS=False):
            # Re-run the remind_expire test, but look for 0 emails sent
            u = User.objects.get(username='test1')
            u.userplan.expire = date.today() + timedelta(days=14)
            u.userplan.active = True
            u.userplan.save()
            u.userplan.remind_expire_soon()
            self.assertEqual(u.userplan.active, True)
            self.assertEqual(len(mail.outbox), 0)

    def test_switch_to_free_no_expiry(self):
        """
        Tests switching to a free Plan and checks that their expiry is cleared
        Tests if expire date is set correctly
        Tests if mail has been send
        Tests if account has been activated
        """
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() + timedelta(days=14)
        self.assertIsNotNone(u.userplan.expire)

        plan = Plan.objects.get(name="Free")
        self.assertTrue(plan.is_free())
        self.assertNotEqual(u.userplan.plan, plan)

        # Switch to Free Plan
        u.userplan.extend_account(plan, None)
        self.assertEqual(u.userplan.plan, plan)
        self.assertIsNone(u.userplan.expire)
        self.assertEqual(u.userplan.active, True)

    def test_switch_from_free_set_expiry(self):
        """
        Tests switching from a free Plan and should set the expiry date
        Tests if expire date is set correctly
        Tests if mail has been send
        Tests if account has been activated
        """
        u = User.objects.get(username='test1')
        u.userplan.expire = None
        u.userplan.plan = Plan.objects.get(name="Free")
        u.userplan.save()
        self.assertIsNone(u.userplan.expire)
        self.assertTrue(u.userplan.plan.is_free())

        plan = Plan.objects.get(name="Standard")
        self.assertFalse(plan.is_free())
        self.assertNotEqual(u.userplan.plan, plan)
        plan_pricing = PlanPricing.objects.filter(Q(plan=plan) & Q(pricing__period=30))[0]

        # Switch to Standard Plan
        u.userplan.extend_account(plan, plan_pricing.pricing)
        self.assertEqual(u.userplan.plan, plan)
        self.assertIsNotNone(u.userplan.expire)
        self.assertEqual(u.userplan.active, True)


class TestInvoice(TestCase):
    fixtures = ['initial_plan', 'test_django-plans_auth', 'test_django-plans_plans']

    def test_get_full_number(self):
        i = Invoice()
        i.number = 123
        i.issued = date(2010, 5, 30)
        self.assertEqual(i.get_full_number(), "123/FV/05/2010")

    def test_get_full_number_type1(self):
        i = Invoice()
        i.type = Invoice.INVOICE_TYPES.INVOICE
        i.number = 123
        i.issued = date(2010, 5, 30)
        self.assertEqual(i.get_full_number(), "123/FV/05/2010")

    def test_get_full_number_type2(self):
        i = Invoice()
        i.type = Invoice.INVOICE_TYPES.DUPLICATE
        i.number = 123
        i.issued = date(2010, 5, 30)
        self.assertEqual(i.get_full_number(), "123/FV/05/2010")

    def test_get_full_number_type3(self):
        i = Invoice()
        i.type = Invoice.INVOICE_TYPES.PROFORMA
        i.number = 123
        i.issued = date(2010, 5, 30)
        self.assertEqual(i.get_full_number(), "123/PF/05/2010")

    def test_get_full_number_with_settings(self):
        settings.PLANS_INVOICE_NUMBER_FORMAT = "{{ invoice.issued|date:'Y' }}." \
            "{{ invoice.number }}.{{ invoice.issued|date:'m' }}"
        i = Invoice()
        i.number = 123
        i.issued = date(2010, 5, 30)
        self.assertEqual(i.get_full_number(), "2010.123.05")

    def test_set_issuer_invoice_data_raise(self):
        issdata = settings.PLANS_INVOICE_ISSUER
        del settings.PLANS_INVOICE_ISSUER
        i = Invoice()
        self.assertRaises(ImproperlyConfigured, i.set_issuer_invoice_data)
        settings.PLANS_INVOICE_ISSUER = issdata

    def test_set_issuer_invoice_data(self):
        i = Invoice()
        i.set_issuer_invoice_data()
        self.assertEqual(i.issuer_name, settings.PLANS_INVOICE_ISSUER['issuer_name'])
        self.assertEqual(i.issuer_street, settings.PLANS_INVOICE_ISSUER['issuer_street'])
        self.assertEqual(i.issuer_zipcode, settings.PLANS_INVOICE_ISSUER['issuer_zipcode'])
        self.assertEqual(i.issuer_city, settings.PLANS_INVOICE_ISSUER['issuer_city'])
        self.assertEqual(i.issuer_country, settings.PLANS_INVOICE_ISSUER['issuer_country'])
        self.assertEqual(i.issuer_tax_number, settings.PLANS_INVOICE_ISSUER['issuer_tax_number'])

    def set_buyer_invoice_data(self):
        i = Invoice()
        u = User.objects.get(username='test1')
        i.set_buyer_invoice_data(u.billinginfo)
        self.assertEqual(i.buyer_name, u.billinginfo.name)
        self.assertEqual(i.buyer_street, u.billinginfo.street)
        self.assertEqual(i.buyer_zipcode, u.billinginfo.zipcode)
        self.assertEqual(i.buyer_city, u.billinginfo.city)
        self.assertEqual(i.buyer_country, u.billinginfo.country)
        self.assertEqual(i.buyer_tax_number, u.billinginfo.tax_number)
        self.assertEqual(i.buyer_name, u.billinginfo.shipping_name)
        self.assertEqual(i.buyer_street, u.billinginfo.shipping_street)
        self.assertEqual(i.buyer_zipcode, u.billinginfo.shipping_zipcode)
        self.assertEqual(i.buyer_city, u.billinginfo.shipping_city)
        self.assertEqual(i.buyer_country, u.billinginfo.shipping_country)

    def test_invoice_number(self):
        settings.PLANS_INVOICE_NUMBER_FORMAT = "{{ invoice.number }}/{% if " \
            "invoice.type == invoice.INVOICE_TYPES.PROFORMA %}PF{% else %}FV" \
            "{% endif %}/{{ invoice.issued|date:'m/Y' }}"
        o = Order.objects.all()[0]
        day = date(2010, 5, 3)
        i = Invoice(issued=day, selling_date=day, payment_date=day)
        i.copy_from_order(o)
        i.set_issuer_invoice_data()
        i.set_buyer_invoice_data(o.user.billinginfo)
        i.clean()
        i.save()

        self.assertEqual(i.number, 1)
        self.assertEqual(i.full_number, '1/FV/05/2010')

    def test_invoice_number_daily(self):
        settings.PLANS_INVOICE_NUMBER_FORMAT = "{{ invoice.number }}/{% if " \
            "invoice.type == invoice.INVOICE_TYPES.PROFORMA %}PF{% else %}FV" \
            "{% endif %}/{{ invoice.issued|date:'d/m/Y' }}"
        settings.PLANS_INVOICE_COUNTER_RESET = Invoice.NUMBERING.DAILY

        user = User.objects.get(username='test1')
        plan_pricing = PlanPricing.objects.all()[0]
        tax = getattr(settings, "PLANS_TAX")
        currency = getattr(settings, "PLANS_CURRENCY")
        o1 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o1.save()

        o2 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o2.save()

        o3 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o3.save()

        day = date(2001, 5, 3)
        i1 = Invoice(issued=day, selling_date=day, payment_date=day)
        i1.copy_from_order(o1)
        i1.set_issuer_invoice_data()
        i1.set_buyer_invoice_data(o1.user.billinginfo)
        i1.clean()
        i1.save()

        i2 = Invoice(issued=day, selling_date=day, payment_date=day)
        i2.copy_from_order(o2)
        i2.set_issuer_invoice_data()
        i2.set_buyer_invoice_data(o2.user.billinginfo)
        i2.clean()
        i2.save()

        day = date(2001, 5, 4)
        i3 = Invoice(issued=day, selling_date=day, payment_date=day)
        i3.copy_from_order(o1)
        i3.set_issuer_invoice_data()
        i3.set_buyer_invoice_data(o1.user.billinginfo)
        i3.clean()
        i3.save()

        self.assertEqual(i1.full_number, "1/FV/03/05/2001")
        self.assertEqual(i2.full_number, "2/FV/03/05/2001")
        self.assertEqual(i3.full_number, "1/FV/04/05/2001")

    def test_invoice_number_monthly(self):
        settings.PLANS_INVOICE_NUMBER_FORMAT = "{{ invoice.number }}/{% if " \
            "invoice.type == invoice.INVOICE_TYPES.PROFORMA %}PF{% else %}FV" \
            "{% endif %}/{{ invoice.issued|date:'m/Y' }}"
        settings.PLANS_INVOICE_COUNTER_RESET = Invoice.NUMBERING.MONTHLY

        user = User.objects.get(username='test1')
        plan_pricing = PlanPricing.objects.all()[0]
        tax = getattr(settings, "PLANS_TAX")
        currency = getattr(settings, "PLANS_CURRENCY")
        o1 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o1.save()

        o2 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o2.save()

        o3 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o3.save()

        day = date(2002, 5, 3)
        i1 = Invoice(issued=day, selling_date=day, payment_date=day)
        i1.copy_from_order(o1)
        i1.set_issuer_invoice_data()
        i1.set_buyer_invoice_data(o1.user.billinginfo)
        i1.clean()
        i1.save()

        day = date(2002, 5, 13)
        i2 = Invoice(issued=day, selling_date=day, payment_date=day)
        i2.copy_from_order(o2)
        i2.set_issuer_invoice_data()
        i2.set_buyer_invoice_data(o2.user.billinginfo)
        i2.clean()
        i2.save()

        day = date(2002, 6, 1)
        i3 = Invoice(issued=day, selling_date=day, payment_date=day)
        i3.copy_from_order(o1)
        i3.set_issuer_invoice_data()
        i3.set_buyer_invoice_data(o1.user.billinginfo)
        i3.clean()
        i3.save()

        self.assertEqual(i1.full_number, "1/FV/05/2002")
        self.assertEqual(i2.full_number, "2/FV/05/2002")
        self.assertEqual(i3.full_number, "1/FV/06/2002")

    def test_invoice_number_annually(self):
        settings.PLANS_INVOICE_NUMBER_FORMAT = "{{ invoice.number }}/{% if " \
            "invoice.type == invoice.INVOICE_TYPES.PROFORMA %}PF{% else %}FV" \
            "{% endif %}/{{ invoice.issued|date:'Y' }}"
        settings.PLANS_INVOICE_COUNTER_RESET = Invoice.NUMBERING.ANNUALLY

        user = User.objects.get(username='test1')
        plan_pricing = PlanPricing.objects.all()[0]
        tax = getattr(settings, "PLANS_TAX")
        currency = getattr(settings, "PLANS_CURRENCY")
        o1 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o1.save()

        o2 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o2.save()

        o3 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o3.save()

        day = date(1991, 5, 3)
        i1 = Invoice(issued=day, selling_date=day, payment_date=day)
        i1.copy_from_order(o1)
        i1.set_issuer_invoice_data()
        i1.set_buyer_invoice_data(o1.user.billinginfo)
        i1.clean()
        i1.save()

        day = date(1991, 7, 13)
        i2 = Invoice(issued=day, selling_date=day, payment_date=day)
        i2.copy_from_order(o2)
        i2.set_issuer_invoice_data()
        i2.set_buyer_invoice_data(o2.user.billinginfo)
        i2.clean()
        i2.save()

        day = date(1992, 6, 1)
        i3 = Invoice(issued=day, selling_date=day, payment_date=day)
        i3.copy_from_order(o1)
        i3.set_issuer_invoice_data()
        i3.set_buyer_invoice_data(o1.user.billinginfo)
        i3.clean()
        i3.save()

        self.assertEqual(i1.full_number, "1/FV/1991")
        self.assertEqual(i2.full_number, "2/FV/1991")
        self.assertEqual(i3.full_number, "1/FV/1992")

    def test_invoice_number_custom(self):
        settings.PLANS_INVOICE_NUMBER_FORMAT = "{{ invoice.number }}/{% if " \
            "invoice.type == invoice.INVOICE_TYPES.PROFORMA %}PF{% else %}FV" \
            "{% endif %}/{{ invoice.issued|date:'Y' }}"

        def plans_invoice_counter_reset_function(invoice):
            from plans.models import Invoice, get_initial_number
            older_invoices = Invoice.objects.filter(
                type=invoice.type,
                issued__year=invoice.issued.year,
                issued__month=invoice.issued.month,
                currency=invoice.currency,
            )
            sequence_name = f"{invoice.issued.year}_{invoice.issued.month}_{invoice.currency}"
            return sequence_name, get_initial_number(older_invoices)

        settings.PLANS_INVOICE_COUNTER_RESET = plans_invoice_counter_reset_function

        user = User.objects.get(username='test1')
        plan_pricing = PlanPricing.objects.all()[0]
        tax = getattr(settings, "PLANS_TAX")
        currency = getattr(settings, "PLANS_CURRENCY")
        currency1 = 'CZK'
        o1 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o1.save()

        o2 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency1)
        o2.save()

        o3 = Order(user=user, plan=plan_pricing.plan,
                   pricing=plan_pricing.pricing, amount=plan_pricing.price,
                   tax=tax, currency=currency)
        o3.save()

        day = date(1991, 7, 13)
        i1 = Invoice(issued=day, selling_date=day, payment_date=day)
        i1.copy_from_order(o1)
        i1.set_issuer_invoice_data()
        i1.set_buyer_invoice_data(o1.user.billinginfo)
        i1.clean()
        i1.save()

        day = date(1991, 7, 13)
        i2 = Invoice(issued=day, selling_date=day, payment_date=day)
        i2.copy_from_order(o2)
        i2.set_issuer_invoice_data()
        i2.set_buyer_invoice_data(o2.user.billinginfo)
        i2.clean()
        i2.save()

        day = date(1991, 7, 13)
        i3 = Invoice(issued=day, selling_date=day, payment_date=day)
        i3.copy_from_order(o1)
        i3.set_issuer_invoice_data()
        i3.set_buyer_invoice_data(o1.user.billinginfo)
        i3.clean()
        i3.save()

        self.assertEqual(i1.full_number, "1/FV/1991")
        self.assertEqual(i2.full_number, "1/FV/1991")
        self.assertEqual(i3.full_number, "2/FV/1991")

    def test_set_order(self):
        o = Order.objects.all()[0]

        i = Invoice()
        i.copy_from_order(o)

        self.assertEqual(i.order, o)
        self.assertEqual(i.user, o.user)
        self.assertEqual(i.total_net, o.amount)
        self.assertEqual(i.unit_price_net, o.amount)
        self.assertEqual(i.total, o.total())
        self.assertEqual(i.tax, o.tax)
        self.assertEqual(i.tax_total, o.total() - o.amount)
        self.assertEqual(i.currency, o.currency)


@transaction.atomic
def complete_order():
    user = User.objects.get(username='test1')

    plan_pricing = PlanPricing.objects.all()[0]
    o1 = Order(user=user, plan=plan_pricing.plan,
               pricing=plan_pricing.pricing, amount=plan_pricing.price,
               )
    o1.save()
    with freeze_time(random.choice(["2012-01-14", "2012-02-14"])):
        o1.complete_order()


class ConcurrentTestInvoice(TransactionTestCase):
    fixtures = ['initial_plan', 'test_django-plans_auth', 'test_django-plans_plans']

    def test_invoice_number_monthly_duplicity(self):
        """
        Test for problem where two simultaneously created invoices had duplicate number
        """
        call_concurrently(15, complete_order)
        invoices = Invoice.objects.filter(type=Invoice.INVOICE_TYPES.INVOICE).order_by("issued", "number")

        first_december_number = 0
        for i in range(1, 15):
            invoice = invoices[i - 1]
            if invoice.issued.month == 2 and first_december_number == 0:
                first_december_number = i - 1
            invoice_number = i - first_december_number
            self.assertEqual(invoice.number, invoice_number)
            self.assertEqual(invoice.full_number, f"{invoice_number}/FV/0{1 if first_december_number == 0 else 2}/2012")


class OrderTestCase(TestCase):
    fixtures = ['initial_plan', 'test_django-plans_auth', 'test_django-plans_plans']

    def test_order_complete_order(self):
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() + timedelta(days=50)
        u.userplan.active = False
        u.userplan.save()
        plan_pricing = PlanPricing.objects.get(plan=u.userplan.plan, pricing__period=30)
        order = Order.objects.create(
            user=u,
            pricing=plan_pricing.pricing,
            amount=100,
            plan=plan_pricing.plan,
        )
        self.assertTrue(order.complete_order())
        self.assertEqual(u.userplan.expire,
                         date.today() + timedelta(days=50) + timedelta(days=plan_pricing.pricing.period))
        self.assertEqual(u.userplan.plan, plan_pricing.plan)
        self.assertEqual(u.userplan.active, True)
        self.assertEqual(order.status, 2)  # completed
        self.assertEqual(order.plan_extended_from, date.today() + timedelta(days=50))
        self.assertEqual(order.plan_extended_until, date.today() + timedelta(days=50) +
                         timedelta(days=plan_pricing.pricing.period))
        self.assertEqual(len(mail.outbox), 3)

    def test_order_complete_order_invalid(self):
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() + timedelta(days=5)
        u.userplan.active = False
        u.userplan.save()
        plan_pricing = PlanPricing.objects.get(plan=u.userplan.plan, pricing__period=30)
        order = Order.objects.create(
            user=u,
            pricing=plan_pricing.pricing,
            amount=100,
            plan=PlanPricing.objects.all()[0].plan,
        )
        self.assertTrue(order.complete_order())
        self.assertEqual(u.userplan.expire,
                         date.today() + timedelta(days=5))
        self.assertEqual(u.userplan.plan, plan_pricing.plan)
        self.assertEqual(u.userplan.active, False)
        self.assertEqual(order.status, 3)  # not valid

    def test_order_complete_order_completed(self):
        """ Completed order doesn't get completed any more """
        u = User.objects.get(username='test1')
        u.userplan.expire = date.today() + timedelta(days=50)
        u.userplan.active = False
        u.userplan.save()
        plan_pricing = PlanPricing.objects.get(plan=u.userplan.plan, pricing__period=30)
        order = Order.objects.create(
            user=u,
            pricing=plan_pricing.pricing,
            amount=100,
            plan=plan_pricing.plan,
            completed=date(2010, 10, 10),
        )
        self.assertFalse(order.complete_order())

    def test_amount_taxed_none(self):
        o = Order()
        o.amount = Decimal(123)
        o.tax = None
        self.assertEqual(o.total(), Decimal('123'))

    def test_amount_taxed_0(self):
        o = Order()
        o.amount = Decimal(123)
        o.tax = Decimal(0)
        self.assertEqual(o.total(), Decimal('123'))

    def test_amount_taxed_23(self):
        o = Order()
        o.amount = Decimal(123)
        o.tax = Decimal(23)
        self.assertEqual(o.total(), Decimal('151.29'))


class PlanChangePolicyTestCase(TestCase):
    fixtures = ['initial_plan', 'test_django-plans_auth', 'test_django-plans_plans']

    def setUp(self):
        self.policy = PlanChangePolicy()

    def test_calculate_day_cost(self):
        plan = Plan.objects.get(pk=5)
        self.assertEqual(self.policy._calculate_day_cost(plan, 13), Decimal('6.67'))

    def test_get_change_price(self):
        p1 = Plan.objects.get(pk=3)
        p2 = Plan.objects.get(pk=4)
        self.assertEqual(self.policy.get_change_price(p1, p2, 23), Decimal('7.82'))
        self.assertEqual(self.policy.get_change_price(p2, p1, 23), None)

    def test_get_change_price1(self):
        p1 = Plan.objects.get(pk=3)
        p2 = Plan.objects.get(pk=4)
        self.assertEqual(self.policy.get_change_price(p1, p2, 53), Decimal('18.02'))
        self.assertEqual(self.policy.get_change_price(p2, p1, 53), None)

    def test_get_change_price2(self):
        p1 = Plan.objects.get(pk=3)
        p2 = Plan.objects.get(pk=4)
        self.assertEqual(self.policy.get_change_price(p1, p2, -53), None)
        self.assertEqual(self.policy.get_change_price(p1, p2, 0), None)


class StandardPlanChangePolicyTestCase(TestCase):
    fixtures = ['initial_plan', 'test_django-plans_auth', 'test_django-plans_plans']

    def setUp(self):
        self.policy = StandardPlanChangePolicy()

    def test_get_change_price(self):
        p1 = Plan.objects.get(pk=3)
        p2 = Plan.objects.get(pk=4)
        self.assertEqual(self.policy.get_change_price(p1, p2, 23), Decimal('8.60'))
        self.assertEqual(self.policy.get_change_price(p2, p1, 23), None)


class EUTaxationPolicyTestCase(TestCase):
    def setUp(self):
        self.policy = EUTaxationPolicy()

    def test_none(self):
        with self.settings(PLANS_TAX=Decimal('23.0'), PLANS_TAX_COUNTRY='PL'):
            self.assertEqual(self.policy.get_tax_rate(None, None), (Decimal('23.0'), True))

    def test_private_nonEU(self):
        with self.settings(PLANS_TAX=Decimal('23.0'), PLANS_TAX_COUNTRY='PL'):
            self.assertEqual(self.policy.get_tax_rate(None, 'RU'), (None, True))

    def test_private_EU_same(self):
        with self.settings(PLANS_TAX=Decimal('23.0'), PLANS_TAX_COUNTRY='PL'):
            self.assertEqual(self.policy.get_tax_rate(None, 'PL'), (Decimal('23.0'), True))

    def test_private_EU_notsame(self):
        with self.settings(PLANS_TAX=Decimal('23.0'), PLANS_TAX_COUNTRY='PL'):
            self.assertEqual(self.policy.get_tax_rate(None, 'AT'), (Decimal('20.0'), True))

    def test_company_nonEU(self):
        with self.settings(PLANS_TAX=Decimal('23.0'), PLANS_TAX_COUNTRY='PL'):
            self.assertEqual(self.policy.get_tax_rate('123456', 'RU'), (None, True))

    def test_company_EU_same(self):
        with self.settings(PLANS_TAX=Decimal('23.0'), PLANS_TAX_COUNTRY='PL'):
            self.assertEqual(self.policy.get_tax_rate('123456', 'PL'), (Decimal('23.0'), True))

    def test_company_EU_GR_vies_tax(self):
        """
        Test, that greece has VAT OK. Greece has code GR in django-countries, but EL in VIES
        Tax ID is not valid VAT ID, so tax is counted
        """
        self.assertEqual(self.policy.get_tax_rate('123456', 'GR'), (24, False))

    def test_company_EU_GR_vies_zero(self):
        """
        Test, that greece has VAT OK. Greece has code GR in django-countries, but EL in VIES
        Tax ID is valid VAT ID, so no tax is counted
        """
        self.assertEqual(self.policy.get_tax_rate('EL090145420', 'GR'), (None, True))

    @mock.patch("stdnum.eu.vat.check_vies")
    def test_company_EU_notsame_vies_ok(self, mock_check):
        mock_check.return_value = {'valid': True}
        with self.settings(PLANS_TAX=Decimal('23.0'), PLANS_TAX_COUNTRY='PL'):
            self.assertEqual(self.policy.get_tax_rate('123456', 'AT'), (None, True))

    @mock.patch("stdnum.eu.vat.check_vies")
    def test_company_EU_notsame_vies_not_ok(self, mock_check):
        mock_check.return_value = {'valid': False}
        with self.settings(PLANS_TAX=Decimal('23.0'), PLANS_TAX_COUNTRY='PL'):
            self.assertEqual(self.policy.get_tax_rate('123456', 'AT'), (Decimal('20.0'), True))


class BillingInfoTestCase(TestCase):
    def test_clean_tax_number(self):
        with self.assertRaises(ValidationError):
            BillingInfo.clean_tax_number('123456', 'CZ')

    def test_clean_tax_number_valid(self):
        self.assertEqual(BillingInfo.clean_tax_number('48136450', 'CZ'), 'CZ48136450')

    def test_clean_tax_number_valid_space(self):
        self.assertEqual(BillingInfo.clean_tax_number('48 136 450', 'CZ'), 'CZ48136450')

    def test_clean_tax_number_valid_with_country(self):
        self.assertEqual(BillingInfo.clean_tax_number('CZ48136450', 'CZ'), 'CZ48136450')

    def test_clean_tax_number_valid_with_country_GR(self):
        self.assertEqual(BillingInfo.clean_tax_number('GR104594676', 'GR'), 'EL104594676')

    def test_clean_tax_number_valid_with_country_EL(self):
        self.assertEqual(BillingInfo.clean_tax_number('EL104594676', 'GR'), 'EL104594676')

    def test_clean_tax_number_vat_id_is_not_correct(self):
        with self.assertRaisesRegex(ValidationError, 'VAT ID is not correct'):
            BillingInfo.clean_tax_number('GR48136450', 'GR')

    def test_clean_tax_number_country_code_does_not_equal_as_country(self):
        with self.assertRaisesRegex(ValidationError, 'VAT ID country code doesn\'t corespond with country'):
            BillingInfo.clean_tax_number('AT48136450', 'CZ')


def timeout(*args, **kwargs):
    raise requests.Timeout


class CreateOrderViewTestCase(TestCase):
    def setUp(self):
        self.factory = RequestFactory()
        request = self.factory.get('')
        middleware = SessionMiddleware(lambda x: x)
        middleware.process_request(request)
        messages = FallbackStorage(request)
        setattr(request, '_messages', messages)
        self.create_view = CreateOrderView()
        self.create_view.request = request

    def test_recalculate_order_no_connection(self):
        # VAT is right, but with no internet connection
        with no_connection():
            with patch('plans.taxation.eu.logger') as mock_logger:
                o = self.create_view.recalculate(10, BillingInfo(tax_number='48136450', country='CZ'))
                self.assertEqual(o.tax, 21)
                mock_logger.exception.assert_called_with("TAX_ID=CZ48136450")
        message = self.create_view.request._messages._queued_messages[0].message
        self.assertEqual(
            message,
            'There was an error during determining validity of your VAT ID.<br/>'
            'If you think, you have european VAT ID and should not be taxed, '
            'please try resaving billing info later.<br/><br/>'
            'European VAT Information Exchange System throw following error: '
            '&lt;urlopen error Internet is disabled&gt;',
        )

    def test_recalculate_order(self):
        # BE 0203.201.340 is VAT ID of Belgium national bank.
        # It is used, because national provider for VIES seems to be stable enough
        c = self.create_view
        o = c.recalculate(10, BillingInfo(tax_number='BE 0203 201 340', country='BE'))
        self.assertEqual(o.tax, None)

        o = c.recalculate(10, BillingInfo(tax_number='0203 201 340', country='BE'))
        self.assertEqual(o.tax, None)

        o = c.recalculate(10, BillingInfo(tax_number='1234565', country='BE'))
        self.assertEqual(o.tax, 21)

        o = c.recalculate(10, BillingInfo(tax_number='1234567', country='GR'))
        self.assertEqual(o.tax, 24)

        o = c.recalculate(10, BillingInfo(tax_number='090145420', country='GR'))
        self.assertEqual(o.tax, None)


class ValidatorsTestCase(TestCase):
    fixtures = ['test_django-plans_auth']

    def test_model_count_validator(self):
        """
        We create a test model validator for User. It will raise ValidationError when QUOTA_NAME value
        will be lower than number of elements of model User.
        """

        class TestValidator(ModelCountValidator):
            code = 'QUOTA_NAME'
            model = User

        validator_object = TestValidator()
        self.assertRaises(ValidationError, validator_object, user=None, quota_dict={'QUOTA_NAME': 1})
        self.assertEqual(validator_object(user=None, quota_dict={'QUOTA_NAME': 2}), None)
        self.assertEqual(validator_object(user=None, quota_dict={'QUOTA_NAME': 3}), None)

        #   TODO: FIX this test not to use Pricing for testing  ModelAttributeValidator
        # def test_model_attribute_validator(self):
        #     """
        #     We create a test attribute validator which will validate if Pricing objects has a specific value set.
        #     """
        #
        #     class TestValidator(ModelAttributeValidator):
        #         code = 'QUOTA_NAME'
        #         attribute = 'period'
        #         model = Pricing
        #
        #     validator_object = TestValidator()
        #     self.assertRaises(ValidationError, validator_object, user=None, quota_dict={'QUOTA_NAME': 360})
        #     self.assertEqual(validator_object(user=None, quota_dict={'QUOTA_NAME': 365}), None)


class BillingInfoViewTestCase(TestCase):
    fixtures = ['test_django-plans_auth']

    def setUp(self):
        self.user = User.objects.create_user('foo', 'myemail@test.com', 'bar')
        self.client.login(username='foo', password='bar')

    @override_settings(
        PLANS_GET_COUNTRY_FROM_IP=True,
        PLANS_DEFAULT_COUNTRY='PL',
    )
    def test_default_country_set(self):
        """
        Test, that default country is PL
        """
        response = self.client.get(reverse('billing_info_create'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '<option value="PL" selected>Poland</option>', html=True)

    @override_settings(
        PLANS_DEFAULT_COUNTRY='PL',
    )
    def test_default_country_set_no_ip(self):
        """
        Test, that default country is PL
        """
        response = self.client.get(reverse('billing_info_create'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '<option value="PL" selected>Poland</option>', html=True)

    @override_settings(PLANS_GET_COUNTRY_FROM_IP=True)
    def test_default_country_unset(self):
        """
        Test, that default country is None
        """
        response = self.client.get(reverse('billing_info_create'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '<option value="" selected>---------</option>', html=True)

    @override_settings(PLANS_GET_COUNTRY_FROM_IP=True)
    def test_default_country_by_ip(self):
        """
        Test, that default country is determined by German IP
        """

        response = self.client.get(reverse('billing_info_create'), HTTP_X_FORWARDED_FOR='85.214.132.117')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '<option value="DE" selected>Germany</option>', html=True)

        response = self.client.get(reverse('billing_info_create'), REMOTE_ADDR='85.214.132.117')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '<option value="DE" selected>Germany</option>', html=True)

    def test_default_country_by_ip_no_settings(self):
        """
        Test, that default country is not determined
        """

        response = self.client.get(reverse('billing_info_create'), HTTP_X_FORWARDED_FOR='85.214.132.117')
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, '<option value="" selected>---------</option>', html=True)

    def test_redirect_view_create(self):
        """
        Test, BillingInfoRedirectView
        """

        response = self.client.get(reverse('billing_info'))
        self.assertRedirects(
            response, '/plan/billing/create/', status_code=302,
            target_status_code=200, fetch_redirect_response=True,
        )

    def test_redirect_view_update_next(self):
        """
        Test, BillingInfoRedirectView redirects to next url, BillingInfo already exists
        """

        baker.make('BillingInfo', user=self.user)
        response = self.client.get(reverse('billing_info') + "?next=foo")
        self.assertRedirects(
            response, '/plan/billing/update/?next=foo', status_code=302,
            target_status_code=200, fetch_redirect_response=True,
        )


class RecurringPlansTestCase(TestCase):

    def test_set_plan_renewal(self):
        """ Test that UserPlan.set_plan_renewal() method """
        up = baker.make('UserPlan')
        o = baker.make('Order', amount=10)
        up.set_plan_renewal(order=o, card_masked_number="1234")
        self.assertEqual(up.recurring.amount, 10)
        self.assertEqual(up.recurring.card_masked_number, "1234")

        # test setting new values
        up.set_plan_renewal(order=o)
        self.assertEqual(up.recurring.amount, 10)
        self.assertEqual(up.recurring.card_masked_number, None)

    def test_plan_autorenew_at(self):
        """ Test that UserPlan.plan_autorenew_at() method """
        up = baker.make('UserPlan')
        self.assertEqual(up.plan_autorenew_at(), None)

    def test_plan_autorenew_at_expire(self):
        """ Test that UserPlan.plan_autorenew_at() method """
        up = baker.make('UserPlan', expire=date(2020, 1, 1))
        self.assertEqual(up.plan_autorenew_at(), date(2020, 1, 1))

    @override_settings(
        PLANS_AUTORENEW_BEFORE_DAYS=3,
        PLANS_AUTORENEW_BEFORE_HOURS=24,
    )
    def test_plan_autorenew_at_settings(self):
        """ Test that UserPlan.plan_autorenew_at() method """
        up = baker.make('UserPlan', expire=date(2020, 1, 5))
        self.assertEqual(up.plan_autorenew_at(), date(2020, 1, 1))

    def test_has_automatic_renewal(self):
        """ Test UserPlan.has_automatic_renewal() method """
        user_plan = baker.make('UserPlan')
        order = baker.make('Order', amount=10)
        user_plan.set_plan_renewal(order=order, card_masked_number="1234")
        self.assertEqual(user_plan.has_automatic_renewal(), False)

        user_plan.recurring.token_verified = True
        self.assertEqual(user_plan.has_automatic_renewal(), True)

    def test_create_new_order(self):
        rup = baker.make(
            'RecurringUserPlan',
            user_plan__user__billinginfo__country='CZ',
            amount=10,
        )
        order = rup.create_renew_order()
        self.assertEqual(order.tax, 21)


class TasksTestCase(TestCase):
    def test_expire_account_task(self):
        order = baker.make('Order', amount=10)
        userplan = baker.make('UserPlan', user=baker.make('User'))
        userplan.expire = date.today() - timedelta(days=1)
        userplan.active = True

        # If the automatic renewal didn't go through, even automatic renewal plans has to go
        userplan.set_plan_renewal(order=order, card_masked_number="1234")

        userplan.save()
        tasks.expire_account()

        userplan.refresh_from_db()
        self.assertEqual(userplan.active, False)
        # self.assertEqual(len(mail.outbox), 1)
