from unittest.mock import ANY, 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.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
from vbv_lernwelt.course.creators.test_utils import create_course, create_course_session
from vbv_lernwelt.course.models import CourseSessionUser
from vbv_lernwelt.notify.email.email_services import EmailTemplate
from vbv_lernwelt.shop.const import VV_DE_PRODUCT_SKU
from vbv_lernwelt.shop.models import CheckoutInformation, CheckoutState, Product


def create_checkout_information(
    user: User,
    transaction_id: str,
    state: CheckoutState,
) -> CheckoutInformation:
    return CheckoutInformation.objects.create(
        user=user,
        transaction_id=transaction_id,
        product_sku=VV_DE_PRODUCT_SKU,
        product_price=324_30,
        state=state.value,
    )


class DatatransWebhookTestCase(APITestCase):
    def setUp(self) -> None:
        course, _ = create_course(
            title="VV_in_DE",
            # needed for VV_DE_PRODUCT_SKU
            _id=COURSE_VERSICHERUNGSVERMITTLERIN_ID,
        )

        create_course_session(course=course, title="Versicherungsvermittler/-in DE")

        self.user = User.objects.create_user(
            username="testuser",
            email="test@user.com",
            password="testpassword",
            is_active=True,
        )

        self.product = Product.objects.create(
            sku=VV_DE_PRODUCT_SKU,
            price=324_30,
            description="VV",
            name="VV",
        )

    def test_webhook_unsigned_payload(self):
        # GIVEN
        payload = {
            "transactionId": "1234567890",
            "status": "settled",
        }

        headers = {"Datatrans-Signature": "t=1605697463367,s0=guessed"}

        # WHEN
        response = self.client.post(
            path=reverse("shop-transaction-webhook"),
            format="json",
            data=payload,
            headers=headers,
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
        self.assertEqual(
            response.json(),
            {"status": "invalid signature"},
        )

    @patch("vbv_lernwelt.shop.views.is_signature_valid")
    @patch("vbv_lernwelt.shop.views.send_email")
    def test_webhook_settled_transmitted_paid(
        self, mock_send_mail, mock_is_signature_valid
    ):
        # GIVEN
        transaction_id = "1234567890"

        create_checkout_information(
            user=self.user,
            transaction_id=transaction_id,
            state=CheckoutState.ONGOING,
        )

        checkout_info = CheckoutInformation.objects.get(transaction_id=transaction_id)
        checkout_info.first_name = "Max"
        checkout_info.last_name = "Mustermann"
        checkout_info.street = "Musterstrasse"
        checkout_info.street_number = "1"
        checkout_info.postal_code = "1234"
        checkout_info.city = "Musterstadt"
        checkout_info.country = "Schweiz"
        checkout_info.company_name = "Musterfirma"
        checkout_info.company_street = "Firmastrasse"
        checkout_info.company_street_number = "2"
        checkout_info.company_postal_code = "5678"
        checkout_info.company_city = "Firmastadt"
        checkout_info.company_country = "Schweiz"
        checkout_info.save()

        mock_is_signature_valid.return_value = True

        # WHEN

        # ~immediately after successful payment
        response_settled = self.client.post(
            path=reverse("shop-transaction-webhook"),
            format="json",
            headers={"Datatrans-Signature": "<signature>"},
            data={
                "status": "settled",
                "transactionId": transaction_id,
            },
        )

        # ~24h later
        response_transmitted = self.client.post(
            path=reverse("shop-transaction-webhook"),
            format="json",
            headers={"Datatrans-Signature": "<signature>"},
            data={
                "status": "transmitted",
                "transactionId": transaction_id,
            },
        )

        # THEN
        self.assertEqual(status.HTTP_200_OK, response_settled.status_code)
        self.assertEqual(status.HTTP_200_OK, response_transmitted.status_code)

        self.assertEqual(
            CheckoutState.PAID.value,
            CheckoutInformation.objects.get(transaction_id=transaction_id).state,
        )

        self.assertEqual(
            1,
            CourseSessionUser.objects.count(),
        )

        self.assertEqual(
            self.user,
            CourseSessionUser.objects.first().user,
        )

        self.assertEqual(
            COURSE_VERSICHERUNGSVERMITTLERIN_ID,
            CourseSessionUser.objects.first().course_session.course.id,
        )

        self.assertEqual(
            1,
            mock_send_mail.call_count,
        )

        mock_send_mail.assert_called_once_with(
            template=EmailTemplate.WELCOME_MAIL_VV,
            recipient_email=self.user.email,
            template_data={
                "course": "Versicherungsvermittler/-in VBV (Deutsch)",
                "target_url": "https://my.vbv-afa.ch/",
                "name": "Max Mustermann",
                "private_street": "Musterstrasse 1",
                "private_city": "1234 Musterstadt Schweiz",
                "company_name": "Musterfirma",
                "company_street": "Firmastrasse 2",
                "company_city": "5678 Firmastadt Schweiz",
            },
            template_language=self.user.language,
            fail_silently=ANY,
        )

    @patch("vbv_lernwelt.shop.views.is_signature_valid")
    def test_webhook_updates_webhook_history(self, mock_is_signature_valid):
        # GIVEN
        transaction_id = "1234567890"

        create_checkout_information(
            user=self.user,
            transaction_id=transaction_id,
            state=CheckoutState.ONGOING,
        )

        mock_is_signature_valid.return_value = True

        # WHEN
        response_1 = self.client.post(
            path=reverse("shop-transaction-webhook"),
            format="json",
            headers={"Datatrans-Signature": "<signature>"},
            data={
                "transactionId": transaction_id,
                "status": "failed",
                "whatever": "1",
            },
        )

        response_2 = self.client.post(
            path=reverse("shop-transaction-webhook"),
            format="json",
            headers={"Datatrans-Signature": "<signature>"},
            data={
                "transactionId": transaction_id,
                "status": "failed",
                "whatever": "2",
            },
        )

        self.assertEqual(response_1.status_code, status.HTTP_200_OK)
        self.assertEqual(response_2.status_code, status.HTTP_200_OK)

        # THEN

        self.assertEqual(
            CheckoutInformation.objects.get(
                transaction_id=transaction_id
            ).webhook_history,
            [
                {
                    "transactionId": transaction_id,
                    "status": "failed",
                    "whatever": "1",
                },
                {
                    "transactionId": transaction_id,
                    "status": "failed",
                    "whatever": "2",
                },
            ],
        )

    @patch("vbv_lernwelt.shop.views.is_signature_valid")
    def test_webhook_failed(self, mock_is_signature_valid):
        # GIVEN
        transaction_id = "1234567890"
        state_received = "failed"

        create_checkout_information(
            user=self.user,
            transaction_id=transaction_id,
            state=CheckoutState.ONGOING,
        )

        mock_is_signature_valid.return_value = True

        # WHEN
        response = self.client.post(
            path=reverse("shop-transaction-webhook"),
            format="json",
            headers={"Datatrans-Signature": "<signature>"},
            data={
                "transactionId": transaction_id,
                "status": state_received,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            CheckoutInformation.objects.get(transaction_id=transaction_id).state,
            CheckoutState.FAILED,
        )

    @patch("vbv_lernwelt.shop.views.is_signature_valid")
    def test_webhook_cancelled(self, mock_is_signature_valid):
        # GIVEN
        transaction_id = "1234567890"
        state_received = "canceled"

        create_checkout_information(  # noqa
            user=self.user,
            transaction_id=transaction_id,
            state=CheckoutState.ONGOING,
        )

        mock_is_signature_valid.return_value = True

        # WHEN
        response = self.client.post(
            path=reverse("shop-transaction-webhook"),
            format="json",
            headers={"Datatrans-Signature": "<signature>"},
            data={
                "transactionId": transaction_id,
                "status": state_received,
            },
        )

        # THEN
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(
            CheckoutInformation.objects.get(transaction_id=transaction_id).state,
            CheckoutState.CANCELED,
        )
