from unittest.mock import patch

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase

from vbv_lernwelt.core.admin import User
from vbv_lernwelt.core.model_utils import add_countries
from vbv_lernwelt.shop.const import VV_DE_PRODUCT_SKU
from vbv_lernwelt.shop.models import CheckoutInformation, CheckoutState, Product
from vbv_lernwelt.shop.services import InitTransactionException

USER_USERNAME = "testuser"
USER_EMAIL = "test@example.com"
USER_PASSWORD = "testpassword"

TEST_ADDRESS_DATA = {
    "first_name": "Test",
    "last_name": "User",
    "street": "Test Street",
    "street_number": "1",
    "postal_code": "1234",
    "city": "Test City",
    "country": "209",
    "company_name": "Test Company",
    "company_street": "Test Company Street",
    "company_street_number": "1",
    "company_postal_code": "1234",
    "company_city": "Test Company City",
    "company_country": "209",
}

REDIRECT_URL = "http://testserver/redirect-url"


class CheckoutAPITestCase(APITestCase):
    def setUp(self) -> None:
        Product.objects.create(
            sku=VV_DE_PRODUCT_SKU,
            price=324_30,
            description="VV",
            name="VV",
        )

        self.user = User.objects.create_user(
            username=USER_USERNAME,
            email=USER_EMAIL,
            password=USER_PASSWORD,
            is_active=True,
        )

        self.client.login(username=USER_USERNAME, password=USER_PASSWORD)
        add_countries()

    @patch("vbv_lernwelt.shop.views.init_transaction")
    def test_checkout_no_company_address_updates_user(self, mock_init_transaction):
        # GIVEN
        mock_init_transaction.return_value = "1234567890"

        # WHEN
        response = self.client.post(
            path=reverse("checkout-vv"),
            format="json",
            data={
                "redirect_url": REDIRECT_URL,
                "product": VV_DE_PRODUCT_SKU,
                "address": {
                    "first_name": "Test",
                    "last_name": "User",
                    "street": "Test Street",
                    "street_number": "1",
                    "postal_code": "1234",
                    "city": "Test City",
                    "country": "209",
                    # NO company data
                },
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        user = User.objects.get(username=USER_USERNAME)
        self.assertEqual(user.invoice_address, User.INVOICE_ADDRESS_PRIVATE)

    @patch("vbv_lernwelt.shop.views.init_transaction")
    def test_checkout_happy_case(self, mock_init_transaction):
        # GIVEN
        mock_init_transaction.return_value = "1234567890"

        # WHEN
        response = self.client.post(
            path=reverse("checkout-vv"),
            format="json",
            data={
                "redirect_url": REDIRECT_URL,
                "product": VV_DE_PRODUCT_SKU,
                "address": TEST_ADDRESS_DATA,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            f"https://pay.sandbox.datatrans.com/v1/start/1234567890",
            response.json()["next_step_url"],
        )

        self.assertTrue(
            CheckoutInformation.objects.filter(
                user=self.user,
                product_sku=VV_DE_PRODUCT_SKU,
                state=CheckoutState.ONGOING,
            ).exists()
        )

        mock_init_transaction.assert_called_once_with(
            user=self.user,
            amount_chf_centimes=324_30,
            redirect_url_success=f"{REDIRECT_URL}/onboarding/{VV_DE_PRODUCT_SKU}/checkout/complete",
            redirect_url_error=f"{REDIRECT_URL}/onboarding/{VV_DE_PRODUCT_SKU}/checkout/address?error",
            redirect_url_cancel=f"{REDIRECT_URL}/",
            webhook_url=f"{REDIRECT_URL}/api/shop/transaction/webhook/",
        )

        user = User.objects.get(username=USER_USERNAME)

        self.assertEqual(user.street, TEST_ADDRESS_DATA["street"])
        self.assertEqual(str(user.country.country_id), TEST_ADDRESS_DATA["country"])
        self.assertEqual(user.invoice_address, User.INVOICE_ADDRESS_ORGANISATION)

    @patch("vbv_lernwelt.shop.views.init_transaction")
    def test_incomplete_setup(self, mock_init_transaction):
        # GIVEN
        Product.objects.all().delete()
        mock_init_transaction.return_value = "1234567890"

        # WHEN
        response = self.client.post(
            path=reverse("checkout-vv"),
            format="json",
            data={
                "redirect_url": REDIRECT_URL,
                "product": VV_DE_PRODUCT_SKU,
                "address": TEST_ADDRESS_DATA,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)

        expected = (
            f"{REDIRECT_URL}/onboarding/{VV_DE_PRODUCT_SKU}/checkout/address?error&"
            f"message=vv-de_product_sku_does_not_exist_needs_to_be_created_first"
        )

        self.assertEqual(expected, response.json()["next_step_url"])

    @patch("vbv_lernwelt.shop.views.init_transaction")
    def test_checkout_init_transaction_exception(self, mock_init_transaction):
        # GIVEN
        mock_init_transaction.side_effect = InitTransactionException(
            "Something went wrong"
        )

        # WHEN
        response = self.client.post(
            path=reverse("checkout-vv"),
            format="json",
            data={
                "redirect_url": REDIRECT_URL,
                "product": VV_DE_PRODUCT_SKU,
                "address": TEST_ADDRESS_DATA,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            f"{REDIRECT_URL}/onboarding/{VV_DE_PRODUCT_SKU}/checkout/address?error",
            response.json()["next_step_url"],
        )

        self.assertEqual(
            0,
            CheckoutInformation.objects.count(),
        )

    def test_checkout_already_paid(self):
        # GIVEN
        CheckoutInformation.objects.create(
            user=self.user,
            product_sku=VV_DE_PRODUCT_SKU,
            product_price=0,
            state=CheckoutState.PAID,
        )

        # WHEN
        response = self.client.post(
            path=reverse("checkout-vv"),
            format="json",
            data={
                "redirect_url": REDIRECT_URL,
                "product": VV_DE_PRODUCT_SKU,
                "address": TEST_ADDRESS_DATA,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            "/",
            response.json()["next_step_url"],
        )

    @patch("vbv_lernwelt.shop.views.init_transaction")
    def test_checkout_double_checkout(self, mock_init_transaction):
        """Advise by Datatrans: Just create a new transaction."""
        # GIVEN

        # existing checkout
        transaction_id_previous = "1234567890"
        CheckoutInformation.objects.create(
            user=self.user,
            product_sku=VV_DE_PRODUCT_SKU,
            product_price=0,
            state=CheckoutState.ONGOING,
            transaction_id=transaction_id_previous,
        )

        # new checkout / transaction
        transaction_id_next = "9999999999"
        mock_init_transaction.return_value = transaction_id_next

        # WHEN
        response = self.client.post(
            path=reverse("checkout-vv"),
            format="json",
            data={
                "redirect_url": REDIRECT_URL,
                "product": VV_DE_PRODUCT_SKU,
                "address": TEST_ADDRESS_DATA,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id_next}",
            response.json()["next_step_url"],
        )

        # check that we have two checkouts
        # (one previous and one new)
        self.assertEqual(
            2,
            CheckoutInformation.objects.count(),
        )

        # previous checkout
        self.assertTrue(
            CheckoutInformation.objects.filter(
                user=self.user,
                product_sku=VV_DE_PRODUCT_SKU,
                state=CheckoutState.ONGOING,
                transaction_id=transaction_id_previous,
            ).exists()
        )

        # new checkout
        self.assertTrue(
            CheckoutInformation.objects.filter(
                user=self.user,
                product_sku=VV_DE_PRODUCT_SKU,
                state=CheckoutState.ONGOING,
                transaction_id=transaction_id_next,
            ).exists()
        )

    @patch("vbv_lernwelt.shop.views.init_transaction")
    def test_checkout_failed_creates_new(self, mock_init_transaction):
        # GIVEN
        state = CheckoutState.FAILED
        transaction_id = "1234567890"
        mock_init_transaction.return_value = transaction_id

        CheckoutInformation.objects.create(
            user=self.user,
            product_sku=VV_DE_PRODUCT_SKU,
            product_price=0,
            state=state,
            transaction_id="0000000000",
        )

        # WHEN
        response = self.client.post(
            path=reverse("checkout-vv"),
            format="json",
            data={
                "redirect_url": REDIRECT_URL,
                "product": VV_DE_PRODUCT_SKU,
                "address": TEST_ADDRESS_DATA,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id}",
            response.json()["next_step_url"],
        )

    @patch("vbv_lernwelt.shop.views.init_transaction")
    def test_checkout_cancelled_creates_new(self, mock_init_transaction):
        # GIVEN
        state = CheckoutState.CANCELED
        transaction_id = "1234567899"
        mock_init_transaction.return_value = transaction_id

        CheckoutInformation.objects.create(
            user=self.user,
            product_sku=VV_DE_PRODUCT_SKU,
            product_price=0,
            state=state,
            transaction_id="1111111111",
        )

        # WHEN
        response = self.client.post(
            path=reverse("checkout-vv"),
            format="json",
            data={
                "redirect_url": REDIRECT_URL,
                "product": VV_DE_PRODUCT_SKU,
                "address": TEST_ADDRESS_DATA,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            f"https://pay.sandbox.datatrans.com/v1/start/{transaction_id}",
            response.json()["next_step_url"],
        )
