# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time

from odoo.addons.account.tests.common import AccountTestInvoicingCommon
from odoo.exceptions import UserError
from odoo.tests import tagged
from odoo.tools.misc import mod10r

CH_IBAN = 'CH15 3881 5158 3845 3843 7'
QR_IBAN = 'CH21 3080 8001 2345 6782 7'


@tagged('post_install_l10n', 'post_install', '-at_install')
class TestSwissQR(AccountTestInvoicingCommon):

    @classmethod
    def setUpClass(cls, chart_template_ref='l10n_ch.l10nch_chart_template'):
        super().setUpClass(chart_template_ref=chart_template_ref)

    def setUp(self):
        super(TestSwissQR, self).setUp()
        # Activate SwissQR in Swiss invoices
        self.env['ir.config_parameter'].create(
            {'key': 'l10n_ch.print_qrcode', 'value': '1'}
        )
        self.customer = self.env['res.partner'].create(
            {
                "name": "Partner",
                "street": "Route de Berne 41",
                "street2": "",
                "zip": "1000",
                "city": "Lausanne",
                "country_id": self.env.ref("base.ch").id,
            }
        )
        self.env.user.company_id.partner_id.write(
            {
                "street": "Route de Berne 88",
                "street2": "",
                "zip": "2000",
                "city": "Neuchâtel",
                "country_id": self.env.ref('base.ch').id,
            }
        )
        self.product = self.env['product.product'].create({
            'name': 'Customizable Desk',
        })
        self.invoice1 = self.create_invoice('base.CHF')
        sale_journal = self.env['account.journal'].search([("type", "=", "sale")])
        sale_journal.invoice_reference_model = "ch"

    def create_invoice(self, currency_to_use='base.CHF'):
        """ Generates a test invoice """

        account = self.env['account.account'].search(
            [('account_type', '=', 'asset_current')], limit=1
        )
        invoice = (
            self.env['account.move']
            .create(
                {
                    'move_type': 'out_invoice',
                    'partner_id': self.customer.id,
                    'currency_id': self.env.ref(currency_to_use).id,
                    'date': time.strftime('%Y') + '-12-22',
                    'invoice_line_ids': [
                        (
                            0,
                            0,
                            {
                                'name': self.product.name,
                                'product_id': self.product.id,
                                'account_id': account.id,
                                'quantity': 1,
                                'price_unit': 42.0,
                            },
                        )
                    ],
                }
            )
        )

        return invoice

    def create_account(self, number):
        """ Generates a test res.partner.bank. """
        return self.env['res.partner.bank'].create(
            {
                'acc_number': number,
                'partner_id': self.env.user.company_id.partner_id.id,
            }
        )

    def swissqr_not_generated(self, invoice):
        """ Prints the given invoice and tests that no Swiss QR generation is triggered. """
        self.assertFalse(
            invoice.partner_bank_id._eligible_for_qr_code('ch_qr', invoice.partner_id, invoice.currency_id),
            'No Swiss QR should be generated for this invoice',
        )

    def swissqr_generated(self, invoice, ref_type='NON'):
        """ Ensure correct params for Swiss QR generation. """

        self.assertTrue(
            invoice.partner_bank_id._eligible_for_qr_code('ch_qr', invoice.partner_id, invoice.currency_id), 'A Swiss QR can be generated'
        )

        if ref_type == 'QRR':
            self.assertTrue(invoice.payment_reference)
            struct_ref = invoice.payment_reference
            unstr_msg = invoice.ref or invoice.name or ''
        else:
            struct_ref = ''
            unstr_msg = invoice.payment_reference or invoice.ref or invoice.name or ''
        unstr_msg = unstr_msg or invoice.number

        payload = (
            "SPC\n"
            "0200\n"
            "1\n"
            "{iban}\n"
            "K\n"
            "company_1_data\n"
            "Route de Berne 88\n"
            "2000 Neuchâtel\n"
            "\n\n"
            "CH\n"
            "\n\n\n\n\n\n\n"
            "42.00\n"
            "CHF\n"
            "K\n"
            "Partner\n"
            "Route de Berne 41\n"
            "1000 Lausanne\n"
            "\n\n"
            "CH\n"
            "{ref_type}\n"
            "{struct_ref}\n"
            "{unstr_msg}\n"
            "EPD"
        ).format(
            iban=invoice.partner_bank_id.sanitized_acc_number,
            ref_type=ref_type,
            struct_ref=struct_ref or '',
            unstr_msg=unstr_msg,
        )

        expected_params = {
            'barcode_type': 'QR',
            'barLevel': 'M',
            'width': 256,
            'height': 256,
            'quiet': 1,
            'mask': 'ch_cross',
            'value': payload,
        }

        params = invoice.partner_bank_id._get_qr_code_generation_params(
            'ch_qr', 42.0, invoice.currency_id, invoice.partner_id, unstr_msg, struct_ref
        )

        self.assertEqual(params, expected_params)

    def test_swissQR_missing_bank(self):
        # Let us test the generation of a SwissQR for an invoice, first by showing an
        # QR is included in the invoice is only generated when Odoo has all the data it needs.
        with self.assertRaises(UserError), self.cr.savepoint():
            self.invoice1.action_post()
            self.swissqr_not_generated(self.invoice1)

    def test_swissQR_iban(self):
        # Now we add an account for payment to our invoice
        # Here we don't use a structured reference
        iban_account = self.create_account(CH_IBAN)
        self.invoice1.partner_bank_id = iban_account
        self.invoice1.action_post()
        self.swissqr_generated(self.invoice1, ref_type="NON")

    def test_swissQR_qriban(self):
        # Now use a proper QR-IBAN, we are good to print a QR Bill
        qriban_account = self.create_account(QR_IBAN)
        self.assertTrue(qriban_account.l10n_ch_qr_iban)
        self.invoice1.partner_bank_id = qriban_account
        self.invoice1.action_post()
        self.swissqr_generated(self.invoice1, ref_type="QRR")

    def test_swiss_order_reference_isr_for_qr_code(self):
        """
        Test that the order reference is correctly generated for QR-Code
        We summon the skipTest if Sale is not installed (instead of creating a whole module for one test)
        """
        if 'sale.order' not in self.env:
            self.skipTest('`sale` is not installed')

        payment_custom = self.env['ir.module.module']._get('payment_custom')
        if payment_custom.state != 'installed':
            self.skipTest("payment_custom module is not installed")

        provider = self.env['payment.provider'].create({
            'name': 'Test',
            'code': 'custom',
        })
        invoice_journal = self.env['account.journal'].search(
            [('type', '=', 'sale'), ('company_id', '=', self.env.company.id)], limit=1)
        invoice_journal.write({'invoice_reference_model': 'ch'})
        order = self.env['sale.order'].create({
            'name': "S00001",
            'partner_id': self.env['res.partner'].search([("name", '=', 'Partner')])[0].id,
            'order_line': [
                (0, 0, {'product_id': self.product_a.id, 'price_unit': 100}),
            ],
        })
        payment_transaction = self.env['payment.transaction'].create({
            'provider_id': provider.id,
            'sale_order_ids': [order.id],
            'partner_id': self.env['res.partner'].search([("name", '=', 'Partner')])[0].id,
            'amount': 100,
            'currency_id': self.env.company.currency_id.id,
        })
        payment_transaction._set_pending()

        self.assertEqual(order.reference, mod10r(order.reference[:-1]))
