# Copyright 2023 Tecnativa - Pedro M. Baeza
# Copyright 2024 Tecnativa - Víctor Martínez
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import logging

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

from odoo.addons.account_chart_update.tests.common import TestAccountChartUpdateCommon

_logger = logging.getLogger(__name__)


@tagged("-at_install", "post_install")
class TestAccountChartUpdate(TestAccountChartUpdateCommon):
    def _get_record_for_xml_id(self, xml_id):
        # To read company-dependent fields correctly
        return self.env.ref(f"account.{self.company.id}_{xml_id}").with_company(
            self.company
        )

    @mute_logger("odoo.models.unlink")
    def test_01_chart_update(self):
        wizard = self.wizard_obj.with_company(self.company).create(self.wizard_vals)
        wizard.action_find_records()
        # Test ir.model.fields _compute_display_name
        field = wizard.fp_field_ids[:1]
        name = field.with_context(account_chart_update=True).display_name
        expected_name = f"{field.field_description} ({field.name})"
        self.assertEqual(name, expected_name)
        self.assertNotEqual(field.display_name, expected_name)
        # Test no changes
        self.assertEqual(wizard.state, "ready")
        self.assertFalse(wizard.tax_group_ids)
        self.assertFalse(wizard.tax_ids)
        self.assertFalse(wizard.account_ids)
        self.assertFalse(wizard.fiscal_position_ids)
        wizard.unlink()
        # Check that no action is performed if the option is not selected
        wizard_vals = self.wizard_vals.copy()
        wizard_vals.update(
            {
                "update_tax_group": False,
                "update_tax": False,
                "update_account": False,
                "update_fiscal_position": False,
            }
        )
        wizard = self.wizard_obj.with_company(self.company).create(wizard_vals)
        wizard.action_find_records()
        self.assertFalse(wizard.tax_group_ids)
        self.assertFalse(wizard.tax_ids)
        self.assertFalse(wizard.account_ids)
        self.assertFalse(wizard.fiscal_position_ids)
        # We delete the existing records so that they appear "to be created".
        domain = [("company_id", "=", self.company.id)]
        domain_account = [("company_ids", "in", self.company.ids)]
        # Before deleting taxes, delete the references in the models.
        self.env.cr.execute("DELETE FROM account_reconcile_model_line_account_tax_rel")
        self.env["account.tax"].search(domain).unlink()
        journals = self.env["account.journal"].search(domain)
        IrDefault = self.env["ir.default"]
        IrDefault.discard_records(journals)
        journals.unlink()
        accounts = self.env["account.account"].search(domain_account)
        IrDefault.discard_records(accounts)
        accounts.unlink()
        self.env["account.fiscal.position"].search(domain).unlink()
        self.env["account.group"].search(domain).unlink()
        wizard.unlink()
        # Now do the real one for detecting additions
        wizard = self.wizard_obj.with_company(self.company).create(self.wizard_vals)
        wizard.action_find_records()
        # account.tax data
        tax_data = self.chart_template_data["account.tax"]
        tax_data_key_0 = list(tax_data)[0]
        tax_data_0 = tax_data[tax_data_key_0]
        self.assertEqual(len(wizard.tax_ids), len(tax_data))
        tax_types = wizard.tax_ids.mapped("type")
        self.assertIn("new", tax_types)
        self.assertNotIn("updated", tax_types)
        self.assertNotIn("deleted", tax_types)
        self.assertEqual(wizard.tax_ids.mapped("xml_id"), list(tax_data.keys()))
        # account.account data
        account_data = self.chart_template_data["account.account"]
        account_data_key_0 = list(account_data)[0]
        account_data_0 = account_data[account_data_key_0]
        self.assertEqual(len(wizard.account_ids), len(account_data))
        account_types = wizard.account_ids.mapped("type")
        self.assertIn("new", account_types)
        self.assertNotIn("updated", account_types)
        self.assertNotIn("deleted", account_types)
        self.assertEqual(wizard.account_ids.mapped("xml_id"), list(account_data.keys()))
        # account.group data
        account_group_data = self.chart_template_data["account.group"]
        self.assertEqual(len(wizard.account_group_ids), len(account_group_data))
        account_group_types = wizard.account_group_ids.mapped("type")
        # generic_coa has no account.group data
        self.assertNotIn("new", account_group_types)
        self.assertNotIn("updated", account_group_types)
        self.assertEqual(
            wizard.account_group_ids.mapped("xml_id"), list(account_group_data.keys())
        )
        # fiscal.position
        fp_data = self.chart_template_data["fiscal.position"]
        self.assertEqual(len(wizard.fiscal_position_ids), len(fp_data))
        # generic_coa has no account.fiscal.position.data
        fp_types = wizard.fiscal_position_ids.mapped("type")
        self.assertNotIn("new", fp_types)
        self.assertNotIn("updated", fp_types)
        wizard.action_update_records()
        self.assertEqual(wizard.state, "done")
        self.assertEqual(wizard.new_taxes, len(tax_data))
        self.assertEqual(wizard.new_accounts, len(account_data))
        self.assertEqual(wizard.new_fps, len(fp_data))
        self.assertTrue(wizard.log)
        # Search new records: tax + account
        new_tax = self._get_record_for_xml_id(tax_data_key_0)
        self.assertTrue(new_tax)
        new_account = self._get_record_for_xml_id(account_data_key_0)
        self.assertEqual(len(new_account.code), wizard.code_digits)
        self.assertTrue(new_account)
        wizard.unlink()
        # Update objects
        new_account.name = "Account name (updated)"
        new_tax.name = "Tax name (updated)"
        new_tax_group = self.env["account.tax.group"].create(
            {"name": "Test 1", "country_id": new_tax.country_id.id}
        )
        new_tax.tax_group_id = new_tax_group
        repartition = new_tax.repartition_line_ids.filtered(
            lambda r: r.repartition_type == "tax"
        )[0]
        repartition.account_id = new_account.id
        wizard = self.wizard_obj.with_company(self.company).create(self.wizard_vals)
        wizard.tax_field_ids += self.env["ir.model.fields"].search(
            [("model", "=", "account.tax"), ("name", "=", "repartition_line_ids")]
        )
        wizard.action_find_records()
        self.assertEqual(len(wizard.tax_ids), 1)
        self.assertEqual(wizard.tax_ids.type, "updated")
        self.assertEqual(wizard.tax_ids.update_tax_id, new_tax)
        self.assertEqual(len(wizard.account_ids), 1)
        self.assertEqual(wizard.account_ids.type, "updated")
        self.assertEqual(wizard.account_ids.update_account_id, new_account)
        wizard.action_update_records()
        self.assertEqual(wizard.updated_taxes, 1)
        self.assertEqual(wizard.updated_accounts, 1)
        self.assertEqual(new_tax.name, tax_data_0["name"])
        self.assertNotEqual(new_tax.tax_group_id, new_tax_group)
        repartition = new_tax.repartition_line_ids.filtered(
            lambda r: r.repartition_type == "tax"
        )
        self.assertNotEqual(repartition.account_id, new_account)
        self.assertEqual(new_account.name, account_data_0["name"])
        wizard.unlink()
        # Exclude fields from check
        new_tax.description = "Test description 2"
        new_account.name = "Other name 2"
        wizard = self.wizard_obj.with_company(self.company).create(self.wizard_vals)
        wizard.action_find_records()
        wizard.tax_field_ids -= self.env["ir.model.fields"].search(
            [("model", "=", "account.tax"), ("name", "=", "description")]
        )
        wizard.account_field_ids -= self.env["ir.model.fields"].search(
            [("model", "=", "account.account"), ("name", "=", "name")]
        )
        wizard.action_find_records()
        self.assertFalse(wizard.tax_ids)
        self.assertFalse(wizard.account_ids)
        wizard.unlink()

    @mute_logger("odoo.models.unlink")
    def test_02_chart_update(self):
        # Test XML-ID matching + recreate
        # account.tax data
        tax_data = self.chart_template_data["account.tax"]
        tax_data_key_0 = list(tax_data)[0]
        tax_data_0 = tax_data[tax_data_key_0]
        # account.account data
        account_data = self.chart_template_data["account.account"]
        account_data_key_0 = list(account_data)[0]
        account_data_0 = account_data[account_data_key_0]
        new_tax = self._get_record_for_xml_id(tax_data_key_0)
        new_tax.name = "Test 1 tax name changed"
        new_account = self._get_record_for_xml_id(account_data_key_0)
        new_account.code = "200000"
        wizard = self.wizard_obj.with_company(self.company).create(self.wizard_vals)
        wizard.action_find_records()
        self.assertEqual(wizard.tax_ids.update_tax_id, new_tax)
        self.assertEqual(wizard.tax_ids.type, "updated")
        self.assertEqual(wizard.account_ids.update_account_id, new_account)
        self.assertEqual(wizard.account_ids.type, "updated")
        wizard.action_update_records()
        self.assertEqual(wizard.updated_taxes, 1)
        self.assertEqual(wizard.updated_accounts, 1)
        self.assertEqual(wizard.new_account_groups, 0)
        self.assertEqual(wizard.updated_account_groups, 0)
        self.assertEqual(wizard.updated_fps, 0)
        self.assertEqual(wizard.deleted_taxes, 0)
        self.assertEqual(new_tax.name, tax_data_0["name"])
        self.assertEqual(new_account.code, wizard.padded_code(account_data_0["code"]))
        # Test match by another field, there is no match by XML-ID
        self._get_model_data(new_tax).unlink()
        self._get_model_data(new_account).unlink()
        new_account.name = "Test 2 account name changed"
        wizard = self.wizard_obj.with_company(self.company).create(self.wizard_vals)
        wizard.action_find_records()
        self.assertEqual(wizard.tax_ids.update_tax_id, new_tax)
        self.assertEqual(wizard.tax_ids.type, "updated")
        self.assertEqual(wizard.account_ids.update_account_id, new_account)
        self.assertEqual(wizard.account_ids.type, "updated")
        wizard.action_update_records()
        self.assertEqual(wizard.updated_taxes, 1)
        self.assertEqual(wizard.updated_accounts, 1)
        self.assertEqual(new_tax.name, tax_data_0["name"])
        self.assertEqual(new_account.name, account_data_0["name"])
        wizard.unlink()
        # Test match by name, there is no match by XML-ID or by code
        self._get_model_data(new_account).unlink()
        new_account.code = "300000"
        wizard = self.wizard_obj.with_company(self.company).create(self.wizard_vals)
        wizard.action_find_records()
        self.assertEqual(wizard.account_ids[0].update_account_id, new_account)
        self.assertEqual(wizard.account_ids[0].type, "updated")
        wizard.action_update_records()
        self.assertEqual(wizard.updated_accounts, 1)
        self.assertEqual(new_account.code, wizard.padded_code(account_data_0["code"]))
        wizard.unlink()

    def test_03_installed_charts(self):
        wizard = self.wizard_obj.with_company(self.company).create(self.wizard_vals)
        chart_template_installed = wizard._chart_template_selection()
        all_chart_templates = self.env[
            "account.chart.template"
        ]._get_chart_template_mapping()
        only_installed = list(
            filter(lambda x: x["installed"], all_chart_templates.values())
        )
        self.assertEqual(len(chart_template_installed), len(only_installed))
