from datetime import datetime, timedelta, timezone as tz
from decimal import Decimal
from unittest import mock

from django.contrib.auth import get_user_model
from django.test import TestCase
from django.utils import timezone

from kfet.models import Account, AccountNegative, Checkout

from .utils import create_user

User = get_user_model()


class AccountTests(TestCase):
    def setUp(self):
        self.account = Account(trigramme="000")
        self.account.save({"username": "user"})

    def test_password(self):
        self.account.change_pwd("anna")
        self.account.save()

        self.assertEqual(Account.objects.get_by_password("anna"), self.account)

        with self.assertRaises(Account.DoesNotExist):
            Account.objects.get_by_password(None)

        with self.assertRaises(Account.DoesNotExist):
            Account.objects.get_by_password("bernard")

    @mock.patch("django.utils.timezone.now")
    def test_negative_creation(self, mock_now):
        now = datetime(2005, 7, 15, tzinfo=tz.utc)
        mock_now.return_value = now
        self.account.balance = Decimal(-10)
        self.account.update_negative()

        self.assertTrue(hasattr(self.account, "negative"))
        self.assertEqual(self.account.negative.start, now)

    @mock.patch("django.utils.timezone.now")
    def test_negative_no_reset(self, mock_now):
        now = datetime(2005, 7, 15, tzinfo=tz.utc)
        mock_now.return_value = now

        self.account.balance = Decimal(-10)
        AccountNegative.objects.create(
            account=self.account, start=now - timedelta(minutes=3)
        )
        self.account.refresh_from_db()

        self.account.balance = Decimal(5)
        self.account.update_negative()
        self.assertTrue(hasattr(self.account, "negative"))

        self.account.balance = Decimal(-10)
        self.account.update_negative()
        self.assertEqual(self.account.negative.start, now - timedelta(minutes=3))

    @mock.patch("django.utils.timezone.now")
    def test_negative_eventually_resets(self, mock_now):
        now = datetime(2005, 7, 15, tzinfo=tz.utc)
        mock_now.return_value = now

        self.account.balance = Decimal(-10)
        AccountNegative.objects.create(
            account=self.account, start=now - timedelta(minutes=20)
        )
        self.account.refresh_from_db()
        self.account.balance = Decimal(5)

        mock_now.return_value = now - timedelta(minutes=10)
        self.account.update_negative()

        mock_now.return_value = now
        self.account.update_negative()
        self.account.refresh_from_db()

        self.assertFalse(hasattr(self.account, "negative"))


class CheckoutTests(TestCase):
    def setUp(self):
        self.now = timezone.now()

        self.u = create_user()
        self.u_acc = self.u.profile.account_kfet

        self.c = Checkout(
            created_by=self.u_acc,
            valid_from=self.now,
            valid_to=self.now + timedelta(days=1),
        )

    def test_initial_statement(self):
        """A statement is added with initial balance on creation."""
        self.c.balance = 10
        self.c.save()

        st = self.c.statements.get()
        self.assertEqual(st.balance_new, 10)
        self.assertEqual(st.amount_taken, 0)
        self.assertEqual(st.amount_error, 0)

        # Saving again doesn't create a new statement.
        self.c.save()

        self.assertEqual(self.c.statements.count(), 1)
