from __future__ import annotations

from datetime import date
from unittest import TestCase
from unittest.mock import MagicMock
from unittest.mock import Mock
from unittest.mock import patch

import pytest

from payments import PaymentStatus
from payments import RedirectNeeded

from . import AuthorizeNetProvider

LOGIN_ID = "abcd1234"
TRANSACTION_KEY = "1234abdd"

PROCESS_DATA = {
    "number": "4007000000027",
    "expiration_0": "5",
    "expiration_1": date.today().year + 1,
    "cvv2": "123",
}

STATUS_CONFIRMED = "1"
STATUS_DECLINED = "2"
ERROR_PROCESSING = "3"


class Payment(Mock):
    id = 1
    variant = "authorizenet"
    currency = "USD"
    total = 100
    status = PaymentStatus.WAITING
    transaction_id = None
    captured_amount = 0
    message = ""

    def get_process_url(self):
        return "http://example.com"

    def get_failure_url(self):
        return "http://cancel.com"

    def get_success_url(self):
        return "http://success.com"

    def change_status(self, status, message=""):
        self.status = status
        self.message = message


class TestAuthorizeNetProvider(TestCase):
    def setUp(self):
        self.payment = Payment()

    def test_provider_redirects_to_success_on_payment_success(self):
        provider = AuthorizeNetProvider(
            login_id=LOGIN_ID, transaction_key=TRANSACTION_KEY
        )

        response_data = [
            STATUS_CONFIRMED,
            "",
            "",
            "This transaction has been approved.",
            "",
            "",
            "1234",
        ]

        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = "|".join(response_data)
            mocked_post.return_value = post
            with self.assertRaises(RedirectNeeded) as exc:
                provider.get_form(self.payment, data=PROCESS_DATA)
                self.assertEqual(exc.args[0], self.payment.get_success_url())
        self.assertEqual(self.payment.status, PaymentStatus.CONFIRMED)
        self.assertEqual(self.payment.captured_amount, self.payment.total)

    @pytest.mark.skip
    def test_provider_shows_validation_error_message(self):
        provider = AuthorizeNetProvider(
            login_id=LOGIN_ID, transaction_key=TRANSACTION_KEY
        )

        error_msg = "The merchant does not accept this type of credit card."
        response_data = [ERROR_PROCESSING, "", "", error_msg, "", "", "1234"]

        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = "|".join(response_data)
            mocked_post.return_value = post
            form = provider.get_form(self.payment, data=PROCESS_DATA)
            self.assertEqual(form.errors["__all__"][0], error_msg)
            self.assertFalse(form.is_valid())
        self.assertEqual(self.payment.status, "error")
        self.assertEqual(self.payment.captured_amount, 0)
        self.assertEqual(self.payment.message, error_msg)

    @pytest.mark.skip
    def test_provider_shows_rejection_error_message(self):
        provider = AuthorizeNetProvider(
            login_id=LOGIN_ID, transaction_key=TRANSACTION_KEY
        )

        error_msg = " This transaction has been declined."
        response_data = [STATUS_DECLINED, "", "", error_msg, "", "", "1234"]

        with patch("requests.post") as mocked_post:
            post = MagicMock()
            post.text = "|".join(response_data)
            mocked_post.return_value = post
            form = provider.get_form(self.payment, data=PROCESS_DATA)
            self.assertEqual(form.errors["__all__"][0], error_msg)
            self.assertFalse(form.is_valid())
        self.assertEqual(self.payment.status, "rejected")
        self.assertEqual(self.payment.captured_amount, 0)
        self.assertEqual(self.payment.message, error_msg)
