"""
dj-stripe Utilities Tests.
"""
import time
from copy import deepcopy
from datetime import datetime
from decimal import Decimal
from unittest import skipIf
from unittest.mock import patch

from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from django.test.utils import override_settings
from django.utils import timezone

from djstripe.models import Subscription
from djstripe.utils import (
    convert_tstamp,
    get_friendly_currency_amount,
    get_supported_currency_choices,
    subscriber_has_active_subscription,
)

from . import (
    FAKE_CUSTOMER,
    FAKE_PRODUCT,
    FAKE_SUBSCRIPTION,
    IS_STATICMETHOD_AUTOSPEC_SUPPORTED,
)
from .apps.testapp.models import Organization

TZ_IS_UTC = time.tzname == ("UTC", "UTC")


class TestTimestampConversion(TestCase):
    def test_conversion(self):
        stamp = convert_tstamp(1365567407)
        self.assertEqual(stamp, datetime(2013, 4, 10, 4, 16, 47, tzinfo=timezone.utc))

    # NOTE: These next two tests will fail if your system clock is not in UTC
    # Travis CI is, and coverage is good, so...

    @skipIf(not TZ_IS_UTC, "Skipped because timezone is not UTC.")
    @override_settings(USE_TZ=False)
    def test_conversion_no_tz(self):
        stamp = convert_tstamp(1365567407)
        self.assertEqual(stamp, datetime(2013, 4, 10, 4, 16, 47))


class TestUserHasActiveSubscription(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(
            username="pydanny", email="pydanny@gmail.com"
        )
        self.customer = FAKE_CUSTOMER.create_for_user(self.user)

    def test_user_has_inactive_subscription(self):
        self.assertFalse(subscriber_has_active_subscription(self.user))

    @patch(
        "stripe.Product.retrieve", return_value=deepcopy(FAKE_PRODUCT), autospec=True
    )
    def test_user_has_active_subscription(self, product_retrieve_mock):
        subscription = Subscription.sync_from_stripe_data(deepcopy(FAKE_SUBSCRIPTION))
        subscription.current_period_end = timezone.now() + timezone.timedelta(days=10)
        subscription.save()

        # Assert that the customer's subscription is valid
        self.assertTrue(subscriber_has_active_subscription(self.user))

    def test_custom_subscriber(self):
        """
        ``subscriber_has_active_subscription`` attempts to create a customer object
        for the current user. This causes a ValueError in this test because the
        database has already been established with auth.User.
        """

        subscriber = Organization.objects.create(email="email@test.com")
        self.assertRaises(ValueError, subscriber_has_active_subscription, subscriber)

    def test_anonymous_user(self):
        """
        This needs to throw an ImproperlyConfigured error so the developer
        can be guided to properly protect the subscription content.
        """

        anon_user = AnonymousUser()

        with self.assertRaises(ImproperlyConfigured):
            subscriber_has_active_subscription(anon_user)

    def test_staff_user(self):
        self.user.is_staff = True
        self.user.save()

        self.assertTrue(subscriber_has_active_subscription(self.user))

    def test_superuser(self):
        self.user.is_superuser = True
        self.user.save()

        self.assertTrue(subscriber_has_active_subscription(self.user))


class TestGetSupportedCurrencyChoices(TestCase):
    @patch(
        "stripe.CountrySpec.retrieve",
        return_value={"supported_payment_currencies": ["usd", "cad", "eur"]},
    )
    @patch(
        "stripe.Account.retrieve",
        return_value={"country": "US"},
        autospec=IS_STATICMETHOD_AUTOSPEC_SUPPORTED,
    )
    def test_get_choices(
        self, stripe_account_retrieve_mock, stripe_countryspec_retrieve_mock
    ):
        # Simple test to test sure that at least one currency choice tuple is returned.

        currency_choices = get_supported_currency_choices(None)
        stripe_account_retrieve_mock.assert_called_once_with()
        stripe_countryspec_retrieve_mock.assert_called_once_with("US")
        self.assertGreaterEqual(
            len(currency_choices), 1, "Currency choices pull returned an empty list."
        )
        self.assertEqual(
            tuple, type(currency_choices[0]), "Currency choices are not tuples."
        )
        self.assertIn(("usd", "USD"), currency_choices, "USD not in currency choices.")


class TestUtils(TestCase):
    def test_get_friendly_currency_amount(self):
        self.assertEqual(
            get_friendly_currency_amount(Decimal("1.001"), "usd"), "$1.00 USD"
        )
        self.assertEqual(
            get_friendly_currency_amount(Decimal("10"), "usd"), "$10.00 USD"
        )
        self.assertEqual(
            get_friendly_currency_amount(Decimal("10.50"), "usd"), "$10.50 USD"
        )
        self.assertEqual(
            get_friendly_currency_amount(Decimal("10.51"), "cad"), "$10.51 CAD"
        )
        self.assertEqual(
            get_friendly_currency_amount(Decimal("9.99"), "eur"), "€9.99 EUR"
        )
