# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import datetime
from decimal import Decimal
from django.test import TestCase, Client
from coin.members.tests import MemberTestsUtils
from coin.members.models import Member, LdapUser
from coin.billing.models import Invoice
from coin.offers.models import Offer, OfferSubscription
from coin.billing.create_subscriptions_invoices import create_member_invoice_for_a_period
from coin.billing.create_subscriptions_invoices import create_all_members_invoices_for_a_period


class BillingInvoiceCreationTests(TestCase):

    def setUp(self):
        # Créé une offre
        self.offer = Offer(name='Offre', billing_period=3, period_fees=30,
                           initial_fees=50)
        self.offer.save()
        # Créé un membre
        self.username = MemberTestsUtils.get_random_username()
        self.member = Member(first_name='Balthazar', last_name='Picsou',
                             username=self.username)
        self.member.save()
        # Créé un abonnement
        self.subscription = OfferSubscription(
            subscription_date=datetime.date(2014, 1, 10),
            member=self.member,
            offer=self.offer)
        self.subscription.save()

    def tearDown(self):
        # Supprime l'utilisateur LDAP créé
        LdapUser.objects.get(pk=self.username).delete()

    def test_first_subscription_invoice_has_initial_fees(self):
        """
        Test que la première facture générée pour un abonnement possède les
        frais de mise en service
        """
        # Demande la création de la première facture
        invoice = create_member_invoice_for_a_period(
            self.member, datetime.date(2014, 1, 1))
        # La facture doit avoir les frais de mise en service
        # Pour tester cela on tri par montant d'item décroissant.
        # Comme dans l'offre créé, les initial_fees sont plus élevées que
        # les period_fees, il doit sortir en premier
        self.assertEqual(invoice.details.order_by('-amount').first().amount, 50)

    def test_prorata_for_first_month_subscription(self):
        """
        Test que la première facture d'un abonnement est facturée au prorata du
        nombre de jours restants
        """
        # Créé la facture pour le mois de janvier
        invoice = create_member_invoice_for_a_period(
            self.member, datetime.date(2014, 1, 1))
        # Comme l'abonnement a été souscris le 10/01 et que la période de
        # facturation est de 3 mois, alors le prorata doit être :
        # janvier :  22j (31-9)
        # fevrier :  28j
        # mars :     31j
        #22+28+31 / 31+28+31
        quantity = Decimal((22.0 + 28.0 + 31.0) / (31.0 + 28.0 + 31.0))
        for detail in invoice.details.all():
            if detail.amount != 50:
                self.assertEqual(detail.quantity.quantize(Decimal('0.01')),
                                 quantity.quantize(Decimal('0.01')))

    def test_subscription_cant_be_charged_twice(self):
        """
        Test qu'un abonnement ne peut pas être facturé deux fois
        (pas de chevauchement possible)
        """
        # Créé une facture
        invoice = Invoice(member=self.member)
        invoice.save()
        # Créé une facturation pour cet abonnement pour la première période
        # de janvier à mars
        invoice.details.create(label=self.offer.name,
                               amount=self.offer.period_fees,
                               offersubscription=self.subscription,
                               period_from=datetime.date(2014, 1, 1),
                               period_to=datetime.date(2014, 3, 31))

        # Créé une facturation pour cet abonnement pour une seconde période
        # de juin à aout
        invoice.details.create(label=self.offer.name,
                               amount=self.offer.period_fees,
                               offersubscription=self.subscription,
                               period_from=datetime.date(2014, 6, 1),
                               period_to=datetime.date(2014, 8, 31))

        # Demande la génération d'une facture pour février
        # Elle doit renvoyer None car l'offre est déjà facturée de
        # janvier à mars
        invoice_test_1 = create_member_invoice_for_a_period(
            self.member, datetime.date(2014, 2, 1))
        self.assertEqual(invoice_test_1, None)

        # Demande la création d'une facture pour avril
        # Elle doit fonctionner, mais devrait avoir une période de facturation
        # que de 2 mois, d'avril à mai car il y a déjà une facture pour
        # la période de juin à aout
        invoice_test_2 = create_member_invoice_for_a_period(
            self.member, datetime.date(2014, 4, 1))
        self.assertEqual(invoice_test_2.details.first().period_from,
                         datetime.date(2014, 4, 1))
        self.assertEqual(invoice_test_2.details.first().period_to,
                         datetime.date(2014, 5, 31))


class BillingPDFTests(TestCase):

    # def test_download_invoice_pdf_return_a_pdf(self):
    #     """
    #     Test que le téléchargement d'une facture en format pdf retourne bien un
    #     pdf
    #     """
    #     # Créé un membre
    #     username = MemberTestsUtils.get_random_username()
    #     member = Member(first_name='A', last_name='A',
    #                     username=username)
    #     member.set_password('1234')
    #     member.save()

    #     # Créé une facture
    #     invoice = Invoice(member=member)
    #     invoice.save()
    #     invoice.validate()

    #     # Se connect en tant que le membre
    #     client = Client()
    #     client.login(username=username, password='1234')
    #     # Tente de télécharger la facture
    #     response = client.get('/billing/invoice/%i/pdf' % invoice.id)
    #     # Vérifie return code 200 et contient chaine %PDF-1.
    #     self.assertContains(response, '%PDF-1.', status_code=200, html=False)
    #     member.delete()

    def test_that_only_owner_of_invoice_can_download_it_as_pdf(self):
        """
        Test qu'une facture ne peut pas être téléchargée par quelqu'un qui n'en
        est pas le propriétaire.
        Test qu'une erreur 403 est bien retournée en cas de tentative
        infructueuse
        """
        # Créé un membre A
        member_a_login = MemberTestsUtils.get_random_username()
        member_a_pwd = '1234'
        member_a = Member(first_name='A', last_name='A', email='a@a.com',
                          username=member_a_login)
        member_a.set_password(member_a_pwd)
        member_a.save()

        # Créé un membre B
        member_b_login = MemberTestsUtils.get_random_username()
        member_b_pwd = '1234'
        member_b = Member(first_name='B', last_name='B', email='b@b.com',
                          username=member_b_login)
        member_b.set_password(member_b_pwd)
        member_b.save()

        # Créé une facture pour le membre A
        invoice_a = Invoice(member=member_a)
        invoice_a.save()
        invoice_a.validate()

        # Simule une connexion en tant que A
        client = Client()
        client.login(username=member_a_login, password=member_a_pwd)
        # Tente de télécharger la facture de A en tant que A
        response = client.get('/billing/invoice/%i/pdf' % invoice_a.id)
        # Vérifie que B a reçu retour OK 200
        self.assertEqual(response.status_code, 200)

        # Simule une connexion en tant que B
        client = Client()
        client.login(username=member_b_login, password=member_b_pwd)
        # Tente de télécharger la facture de A en tant que B
        response = client.get('/billing/invoice/%i/pdf' % invoice_a.id)
        # Vérifie que B a reçu retour Forbidden 403
        self.assertEqual(response.status_code, 403)

        member_a.delete()
        member_b.delete()