# Copyright 2023 Moduon Team S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0)
from freezegun import freeze_time
from psycopg2 import IntegrityError

from odoo import fields
from odoo.tests.common import Form, TransactionCase, new_test_user, users
from odoo.tools import mute_logger


@freeze_time("2023-01-01 12:00:00")
class TestProjectSequence(TransactionCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        new_test_user(
            cls.env,
            "manager",
            "project.group_project_manager,analytic.group_analytic_accounting",
        )
        cls.pjr_seq = cls.env.ref("project_sequence.seq_project_sequence")
        cls.pjr_seq.date_range_ids.unlink()
        default_plan_id = (
            cls.env["account.analytic.plan"]
            .sudo()
            .search(
                [
                    "|",
                    ("company_id", "=", False),
                    ("company_id", "=", cls.env.company.id),
                ],
                limit=1,
            )
        )
        cls.analytic_account = cls.env["account.analytic.account"].create(
            {
                "name": "aaa",
                "plan_id": default_plan_id.id,
                "company_id": cls.env.company.id,
                "create_uid": cls.env.uid,
                "write_uid": cls.env.uid,
                "create_date": fields.Datetime.now(),
                "write_date": fields.Datetime.now(),
            }
        )

    def setUp(self):
        super(TestProjectSequence, self).setUp()
        self.pjr_seq._get_current_sequence().number_next = 11

    @users("manager")
    def test_sequence_after_creation(self):
        """Sequence is applied only after project creation."""
        prj_f = Form(self.env["project.project"])
        self.assertFalse(prj_f.name)
        self.assertFalse(prj_f.sequence_code)
        proj = prj_f.save()
        self.assertTrue(proj.sequence_code)
        self.assertEqual(proj.name, proj.sequence_code)
        self.assertEqual(proj.sequence_code, "23-00011")
        self.assertEqual(proj.display_name, "23-00011")

    def test_analytic_account_after_creation_no_name(self):
        """Project's analytic account is named like project's default name."""
        proj = self.env["project.project"].create(
            {"analytic_account_id": self.analytic_account.id}
        )
        self.assertEqual(proj.sequence_code, "23-00011")
        self.assertEqual(proj.name, "23-00011")
        self.assertEqual(proj.display_name, "23-00011")
        self.assertEqual(proj.analytic_account_id.name, "23-00011")

    def test_analytic_account_after_creation_named(self):
        """Project's analytic account is named like project's display name."""
        proj = self.env["project.project"].create(
            {"name": "whatever", "analytic_account_id": self.analytic_account.id}
        )
        self.assertEqual(proj.sequence_code, "23-00011")
        self.assertEqual(proj.name, "whatever")
        self.assertEqual(proj.display_name, "23-00011 - whatever")
        self.assertEqual(proj.analytic_account_id.name, "23-00011 - whatever")

    @users("manager")
    def test_sequence_copied_to_name_if_emptied(self):
        """Sequence is copied to project name if user removes it."""
        proj = self.env["project.project"].create(
            {"name": "whatever", "analytic_account_id": self.analytic_account.id}
        )
        self.assertEqual(proj.name, "whatever")
        self.assertEqual(proj.sequence_code, "23-00011")
        self.assertEqual(proj.display_name, "23-00011 - whatever")
        self.assertEqual(proj.analytic_account_id.name, "23-00011 - whatever")
        with Form(proj) as prj_f:
            prj_f.name = False
        self.assertEqual(proj.name, "23-00011")
        self.assertEqual(proj.sequence_code, "23-00011")
        self.assertEqual(proj.display_name, "23-00011")
        self.assertEqual(proj.analytic_account_id.name, "23-00011")

    @users("manager")
    def test_sequence_not_copied_to_another_project(self):
        """Sequence is not duplicated to another project."""
        proj1 = self.env["project.project"].create({"name": "whatever"})
        proj2 = proj1.copy()
        self.assertEqual(proj1.sequence_code, "23-00011")
        self.assertEqual(proj2.sequence_code, "23-00012")

    @users("manager")
    @mute_logger("odoo.sql_db")
    def test_sequence_unique(self):
        """Sequence cannot have duplicates."""
        proj1 = self.env["project.project"].create({"name": "one"})
        self.assertEqual(proj1.sequence_code, "23-00011")
        self.pjr_seq._get_current_sequence().number_next = 11
        with self.assertRaises(IntegrityError), self.env.cr.savepoint():
            proj1 = self.env["project.project"].create({"name": "two"})

    @users("manager")
    def test_project_without_sequence(self):
        """Preexisting projects had no sequence, and they should display fine."""
        proj1 = self.env["project.project"].search(
            [
                ("sequence_code", "=", False),
            ],
            limit=1,
        )
        proj1.name = "one"
        self.assertFalse(proj1.sequence_code)
        self.assertEqual(proj1.display_name, "one")
        # Make sure that the sequence is not increased
        proj2 = self.env["project.project"].create({"name": "two"})
        self.assertEqual(proj2.sequence_code, "23-00011")
        self.assertEqual(proj2.display_name, "23-00011 - two")

    @users("manager")
    def test_project_with_empty_sequence(self):
        """Sequence is applied when creating project with an empty sequence"""
        proj1 = self.env["project.project"].create(
            {"name": "whatever", "sequence_code": ""}
        )
        self.assertEqual(proj1.sequence_code, "23-00011")
        self.assertEqual(proj1.display_name, "23-00011 - whatever")
        # Sequence is applied when creating project with sequence in False
        proj2 = self.env["project.project"].create(
            {"name": "whatever", "sequence_code": False}
        )
        self.assertEqual(proj2.sequence_code, "23-00012")
        self.assertEqual(proj2.display_name, "23-00012 - whatever")

    def test_custom_pattern(self):
        """Display name pattern can be customized."""
        self.env["ir.config_parameter"].set_param(
            "project_sequence.display_name_pattern", "%(name)s/%(sequence_code)s"
        )
        proj = self.env["project.project"].create({"name": "one"})
        self.assertEqual(proj.display_name, "one/23-00011")
        self.assertEqual(proj.sequence_code, "23-00011")
        self.env["ir.config_parameter"].set_param(
            "project_sequence.display_name_pattern", "%(name)s"
        )
        proj = self.env["project.project"].create({"name": "two"})
        self.assertEqual(proj.display_name, "two")
        self.assertEqual(proj.sequence_code, "23-00012")
        self.env["ir.config_parameter"].set_param(
            "project_sequence.display_name_pattern", "%(sequence_code)s"
        )
        proj = self.env["project.project"].create({"name": "three"})
        self.assertEqual(proj.display_name, "23-00013")
        self.assertEqual(proj.sequence_code, "23-00013")

    def test_sync_analytic_account_name(self):
        """Set analytic account name equal to project's display name."""
        proj = self.env["project.project"].create({"name": "one"})
        default_plan_id = (
            self.env["account.analytic.plan"]
            .sudo()
            .search(
                [
                    "|",
                    ("company_id", "=", False),
                    ("company_id", "=", self.env.company.id),
                ],
                limit=1,
            )
        )
        analytic_account = self.env["account.analytic.account"].create(
            {
                "name": proj.display_name,
                "plan_id": default_plan_id.id,
                "company_id": self.env.company.id,
                "create_uid": self.env.uid,
                "write_uid": self.env.uid,
                "create_date": fields.Datetime.now(),
                "write_date": fields.Datetime.now(),
            }
        )
        proj.analytic_account_id = analytic_account
        proj._sync_analytic_account_name()
        self.assertEqual(proj.analytic_account_id.name, proj.display_name)

        # Test when analytic_account_id is not set
        proj.analytic_account_id = False
        proj._sync_analytic_account_name()
        self.assertTrue(True)  # Placeholder assertion to ensure the code execution

    def test_name_search(self):
        """Allow searching by sequence code by default."""
        proj1 = self.env["project.project"].create({"name": "one"})
        self.assertEqual(proj1.sequence_code, "23-00011")
        proj2 = self.env["project.project"].create({"name": "two"})
        self.assertEqual(proj2.sequence_code, "23-00012")
        proj3 = self.env["project.project"].create({"name": "three"})
        self.assertEqual(proj3.sequence_code, "23-00013")

        # Search by name
        results = self.env["project.project"].name_search("two")
        self.assertIn((proj2.id, "23-00012 - two"), results)
        self.assertNotIn((proj1.id, "23-00011 - one"), results)
        self.assertNotIn((proj3.id, "23-00013 - three"), results)

        # Search by sequence code
        results = self.env["project.project"].name_search("23-00012")
        self.assertIn((proj2.id, "23-00012 - two"), results)
        self.assertNotIn((proj1.id, "23-00011 - one"), results)
        self.assertNotIn((proj3.id, "23-00013 - three"), results)
