# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
from lxml import etree

from odoo import tools
from odoo.tests import tagged
from odoo.tools.misc import file_open
from odoo.addons.account.tests.common import AccountTestInvoicingCommon


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

    class RepartitionLine:
        def __init__(self, factor_percent, repartition_type, tag_ids):
            self.factor_percent = factor_percent
            self.repartition_type = repartition_type
            self.tag_ids = tag_ids

    @classmethod
    def get_tag_ids(cls, tag_codes):
        """ Helper function to define tag ids for taxes """
        return cls.env['account.account.tag'].search([
            ('applicability', '=', 'taxes'),
            ('country_id.code', '=', 'IT'),
            ('name', 'in', tag_codes)]).ids

    @classmethod
    def repartition_lines(cls, *lines):
        """ Helper function to define repartition lines in taxes """
        return ([(5, 0, 0)] + [(0, 0, {
            **line.__dict__,
            'tag_ids': cls.get_tag_ids(line.tag_ids)
        }) for line in lines])

    @classmethod
    @AccountTestInvoicingCommon.setup_country('it')
    def setUpClass(cls):
        super().setUpClass()

        # Company data ------
        cls.company_data_2 = cls.setup_other_company(
            name='company_2_data',
            vat='IT01234560157',
            phone='0266766700',
            email='test@test.it',
            street="1234 Test Street",
            zip="12345",
            city="Prova",
            l10n_it_codice_fiscale='01234560157',
            l10n_it_tax_system="RF01",
        )
        cls.company = cls.company_data_2['company']
        cls.company.partner_id.write({
            'l10n_it_pa_index': "0803HR0"
        })

        cls.test_bank = cls.env['res.partner.bank'].create({
            'partner_id': cls.company.partner_id.id,
            'acc_number': 'IT1212341234123412341234123',
            'bank_name': 'BIG BANK',
            'bank_bic': 'BIGGBANQ',
        })

        # Partners
        cls.italian_partner_a = cls.env['res.partner'].create({
            'name': 'Alessi',
            'vat': 'IT00465840031',
            'l10n_it_codice_fiscale': '93026890017',
            'country_id': cls.env.ref('base.it').id,
            'street': 'Via Privata Alessi 6',
            'zip': '28887',
            'city': 'Milan',
            'company_id': False,
            'is_company': True,
            'invoice_edi_format': 'it_edi_xml',
        })

        cls.italian_partner_b = cls.env['res.partner'].create({
            'name': 'pa partner',
            'vat': 'IT06655971007',
            'l10n_it_codice_fiscale': '06655971007',
            'l10n_it_pa_index': '123456',
            'country_id': cls.env.ref('base.it').id,
            'street': 'Via Test PA',
            'zip': '32121',
            'city': 'PA Town',
            'is_company': True,
            'invoice_edi_format': 'it_edi_xml',
        })

        cls.italian_partner_no_address_codice = cls.env['res.partner'].create({
            'name': 'Alessi',
            'l10n_it_codice_fiscale': '00465840031',
            'is_company': True,
        })

        cls.italian_partner_no_address_VAT = cls.env['res.partner'].create({
            'name': 'Alessi',
            'vat': 'IT00465840031',
            'is_company': True,
        })

        cls.american_partner = cls.env['res.partner'].create({
            'name': 'Alessi',
            'vat': '00465840031',
            'country_id': cls.env.ref('base.us').id,
            'is_company': True,
        })

        # We create this because we are unable to post without a proxy user existing
        cls.private_key_id = cls.env['certificate.key'].create({
            'name': 'IT test key',
            'content': base64.b64encode(file_open('l10n_it_edi/data/pkey.key', 'rb').read()),
        })
        cls.proxy_user = cls.env['account_edi_proxy_client.user'].create({
            'proxy_type': 'l10n_it_edi',
            'id_client': 'l10n_it_edi_test',
            'company_id': cls.company.id,
            'edi_identification': 'l10n_it_edi_test',
            'private_key_id': cls.private_key_id.id,
        })

        cls.default_tax = cls.env['account.tax'].with_company(cls.company).create({
            'name': "22% default",
            'amount': 22.0,
            'amount_type': 'percent',
        })

        cls.module = 'l10n_it_edi'

    def _assert_export_invoice(self, invoice, filename):
        path = f'{self.module}/tests/export_xmls/{filename}'
        with tools.file_open(path, mode='rb') as fd:
            expected_tree = etree.fromstring(fd.read())
        xml = invoice._l10n_it_edi_render_xml()
        invoice_etree = etree.fromstring(xml)
        try:
            self.assertXmlTreeEqual(invoice_etree, expected_tree)
        except AssertionError as ae:
            ae.args = (ae.args[0] + f"\nFile used for comparison: {filename}", )
            raise

    def _assert_import_invoice(self, filename, expected_values_list, xml_to_apply=None, move_type="in_invoice"):
        """ Tests an invoice imported from an XML vendor bill file on the filesystem
            against expected values. XPATHs can be applied with the `xml_to_apply`
            argument to the XML content before it's imported.
        """
        path = f'{self.module}/tests/import_xmls/{filename}'
        with tools.file_open(path, mode='rb') as fd:
            import_content = fd.read()

        if xml_to_apply:
            tree = self.with_applied_xpath(
                etree.fromstring(import_content),
                xml_to_apply
            )
            import_content = etree.tostring(tree)

        attachment = self.env['ir.attachment'].create({
            'name': filename,
            'raw': import_content,
        })

        journal_type = {
            'in_invoice': 'default_journal_purchase',
            'out_invoice': 'default_journal_sale',
        }[move_type]
        purchase_journal = self.company_data_2[journal_type].with_context(default_move_type=move_type)
        invoices = purchase_journal._create_document_from_attachment(attachment.ids)

        expected_invoice_values_list = []
        expected_invoice_line_ids_values_list = []
        for expected_values in expected_values_list:
            invoice_values = dict(expected_values)
            if 'invoice_line_ids' in invoice_values:
                expected_invoice_line_ids_values_list += invoice_values.pop('invoice_line_ids')
            expected_invoice_values_list.append(invoice_values)
        try:
            self.assertRecordValues(invoices, expected_invoice_values_list)
            if expected_invoice_line_ids_values_list:
                self.assertRecordValues(invoices.invoice_line_ids, expected_invoice_line_ids_values_list)
        except AssertionError as ae:
            ae.args = (ae.args[0] + f"\nFile used for comparison: {filename}", )
            raise

        return invoices
