# Part of Odoo. See LICENSE file for full copyright and licensing details.

from unittest.mock import patch

from werkzeug import urls

from odoo.tests import tagged
from odoo.tools import mute_logger

from odoo.addons.payment_nuvei.controllers.main import NuveiController
from odoo.addons.payment_nuvei.tests.common import NuveiCommon


@tagged('post_install', '-at_install')
class TestPaymentTransaction(NuveiCommon):

    def test_no_item_missing_from_rendering_values(self):
        """ Test that the rendering values match what we expect. """
        def make_uuid():
            return "0000-0000-0000-0000"

        tx = self._create_transaction(flow='redirect')
        return_url = self._build_url(NuveiController._return_url)
        webhook_url = self._build_url(NuveiController._webhook_url)
        cancel_url_params = {
            'tx_ref': tx.reference,
            'error_access_token': self._generate_test_access_token(tx.reference),
        }
        cancel_url = f'{return_url}?{urls.url_encode(cancel_url_params)}'
        first_name, last_name = " ".join(tx.partner_name.split()[:-1]), tx.partner_name.split()[-1]
        expected_values = {
            'api_url': 'https://ppp-test.safecharge.com/ppp/purchase.do',
            'url_params': {
                'address1': tx.partner_address,
                'city': tx.partner_city,
                'country': tx.partner_country_id.code,
                'currency': tx.currency_id.name,
                'email': tx.partner_email,
                'encoding': 'UTF-8',
                'first_name': first_name,
                'item_amount_1': tx.amount,
                'item_name_1': tx.reference,
                'item_quantity_1': 1,
                'invoice_id': tx.reference,
                'last_name': last_name,
                'merchantLocale': tx.partner_lang,
                'merchant_id': self.provider.nuvei_merchant_identifier,
                'merchant_site_id': self.provider.nuvei_site_identifier,
                'payment_method_mode': 'filter',
                'payment_method': 'unknown',
                'phone1': '+3212345678',
                'state': tx.partner_state_id.code or '',
                'user_token_id': make_uuid(),
                'time_stamp': tx.create_date.strftime('%Y-%m-%d.%H:%M:%S'),
                'total_amount': self.amount,
                'version': '4.0.0',
                'zip': tx.partner_zip,
                'back_url': cancel_url,
                'error_url': cancel_url,
                'notify_url': webhook_url,
                'pending_url': return_url,
                'success_url': return_url,
            }
        }
        checksum = self.provider._nuvei_calculate_signature(
            expected_values['url_params'], incoming=False
        )
        expected_values['checksum'] = checksum

        with patch(
            'odoo.addons.payment.utils.generate_access_token', new=self._generate_test_access_token
        ), patch('odoo.addons.payment_nuvei.models.payment_transaction.uuid4', make_uuid):
            processing_values = tx._get_specific_rendering_values(None)
        self.assertDictEqual(processing_values, expected_values)

    @mute_logger('odoo.addons.payment.models.payment_transaction')
    def test_no_input_missing_from_redirect_form(self):
        """ Test that no key is omitted from the rendering values. """
        tx = self._create_transaction(flow='redirect')
        expected_input_keys = [
            'checksum',
            'address1',
            'city',
            'country',
            'currency',
            'email',
            'encoding',
            'first_name',
            'item_amount_1',
            'item_name_1',
            'item_quantity_1',
            'invoice_id',
            'last_name',
            'merchantLocale',
            'merchant_id',
            'merchant_site_id',
            'payment_method_mode',
            'payment_method',
            'phone1',
            'state',
            'time_stamp',
            'user_token_id',
            'total_amount',
            'version',
            'zip',
            'notify_url',
            'success_url',
            'error_url',
            'pending_url',
            'back_url',
        ]
        expected_input_keys.sort()
        with patch(
            'odoo.addons.payment.utils.generate_access_token', new=self._generate_test_access_token
        ):
            processing_values = tx._get_processing_values()

        form_info = self._extract_values_from_html_form(processing_values['redirect_form_html'])
        self.assertEqual(form_info['action'], 'https://ppp-test.safecharge.com/ppp/purchase.do')
        self.assertEqual(form_info['method'], 'post')
        input_keys = list(form_info['inputs'].keys())
        input_keys.sort()
        self.assertListEqual(input_keys, expected_input_keys)

    def test_processing_notification_data_confirms_transaction(self):
        """ Test that the transaction state is set to 'done' when the notification data indicates a
        successful payment. """
        tx = self._create_transaction(flow='redirect')
        tx._process_notification_data(self.notification_data)
        self.assertEqual(tx.state, 'done')

    def test_processing_notification_data_sets_transaction_in_error(self):
        """ Test that the transaction state is set to 'error' when the notification data indicates
        that something went wrong. """
        tx = self._create_transaction(flow='redirect')
        payload = dict(self.notification_data, Status='ERROR', Reason='Invalid Card')
        tx._process_notification_data(payload)
        self.assertEqual(tx.state, 'error')

    def test_processing_notification_data_sets_unknown_transaction_in_error(self):
        """ Test that the transaction state is set to 'error' when the notification data returns
        something with an unknown state. """
        tx = self._create_transaction(flow='redirect')
        payload = dict(self.notification_data, Status='???', Reason='Invalid Card')
        tx._process_notification_data(payload)
        self.assertEqual(tx.state, 'error')

    def test_processing_notification_data_sets_transaction_to_cancel(self):
        """ Test that the transaction state is set to 'cancel' when the notification data is
        missing. """
        tx = self._create_transaction(flow='redirect')
        tx._process_notification_data({})
        self.assertEqual(tx.state_message, 'The customer left the payment page.')
        self.assertEqual(tx.state, 'cancel')

    def test_processing_values_contain_rounded_amount_usd_webpay(self):
        """ Ensure that for USD currency with Webpay payment method, processing_values should
        contain a value which is the amount rounded down to the nearest 0. """
        currency_usd = self.env.ref('base.USD')
        webpay_id = self.env.ref('payment.payment_method_webpay')
        tx = self._create_transaction(
            'redirect', amount=1000.50, currency_id=currency_usd.id, payment_method_id=webpay_id.id
        )
        with patch(
            'odoo.addons.payment.utils.generate_access_token', new=self._generate_test_access_token
        ):
            processing_values = tx._get_specific_rendering_values(None)
        self.assertEqual(processing_values.get('url_params').get('total_amount'), 1000)
