from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.tests.common import tagged

from .common import QuickTemplateCommon


@tagged('post_install', '-at_install')
class TestMailQuickTemplate(QuickTemplateCommon):

    # ------------------------- Positive -------------------------
    def test_tp01_create_template(self):
        tmpl = self._make_template()
        self.assertTrue(tmpl.active)
        self.assertEqual(tmpl.use_count, 0)

    def test_tp02_global_template_is_global(self):
        tmpl = self._make_template(name='Global', model_id=False)
        self.assertTrue(tmpl.is_global)

    def test_tp03_model_specific(self):
        tmpl = self._make_template(name='Partner', model_id=self.model_partner.id)
        self.assertFalse(tmpl.is_global)
        self.assertEqual(tmpl.model, 'res.partner')

    def test_tp04_placeholder_record_field(self):
        partner = self.env['res.partner'].create({'name': 'Acme Corp'})
        tmpl = self._make_template(
            name='Greet', model_id=self.model_partner.id,
            body_html='<p>Dear {{name}},</p>',
        )
        resolved = tmpl._resolve_placeholders('res.partner', partner.id)
        self.assertIn('Acme Corp', resolved)

    def test_tp05_today_token(self):
        tmpl = self._make_template(name='Date', body_html='<p>{{today}}</p>')
        resolved = tmpl._resolve_placeholders(False, False)
        self.assertNotIn('{{today}}', resolved)

    def test_tp06_company_name_token(self):
        tmpl = self._make_template(name='Co', body_html='<p>{{company_name}}</p>')
        resolved = tmpl._resolve_placeholders(False, False)
        self.assertIn(self.env.company.name, resolved)

    def test_tp07_current_user_token(self):
        tmpl = self._make_template(name='Usr', body_html='<p>{{current_user}}</p>')
        resolved = tmpl._resolve_placeholders(False, False)
        self.assertIn(self.env.user.name, resolved)

    def test_tp09_tp10_archive_unarchive(self):
        tmpl = self._make_template(name='Arch')
        tmpl.action_archive()
        self.assertFalse(tmpl.active)
        tmpl.action_unarchive()
        self.assertTrue(tmpl.active)

    def test_tp15_duplicate(self):
        tmpl = self._make_template(name='Dup')
        copy = tmpl.copy()
        self.assertNotEqual(copy.id, tmpl.id)

    # ------------------------- Negative -------------------------
    def test_tn01_no_name(self):
        with self.assertRaises(Exception):
            self.Template.create({
                'category_id': self.category.id,
                'body_html': '<p>x</p>',
            })
            self.env.flush_all()

    def test_tn02_empty_body(self):
        with self.assertRaises(ValidationError):
            self._make_template(name='Empty', body_html='<p>   </p>')

    def test_tn03_no_category(self):
        with self.assertRaises(Exception):
            self.Template.create({'name': 'NoCat', 'body_html': '<p>x</p>'})
            self.env.flush_all()

    def test_tn04_duplicate_name(self):
        # With a model set the SQL UNIQUE(name, model_id, company_id) fires
        # first as an IntegrityError; catch broadly.
        self._make_template(name='Uniq', model_id=self.model_partner.id)
        with self.assertRaises(Exception):
            self._make_template(name='Uniq', model_id=self.model_partner.id)
            self.env.flush_all()

    def test_tn04b_duplicate_global(self):
        # Two global templates (model_id NULL): NULL is distinct in SQL, so the
        # Python @api.constrains is what enforces uniqueness (ValidationError).
        self._make_template(name='GUniq', model_id=False)
        with self.assertRaises(ValidationError):
            self._make_template(name='GUniq', model_id=False)

    def test_tn09_attachment_limit(self):
        atts = self.env['ir.attachment']
        for i in range(6):
            atts |= self.env['ir.attachment'].create({'name': 'a%s' % i})
        with self.assertRaises(ValidationError):
            self._make_template(name='Att', attachment_ids=[(6, 0, atts.ids)])

    def test_tn05_delete_used_non_admin(self):
        tmpl = self._make_template(name='Used')
        tmpl.use_count = 3
        with self.assertRaises(UserError):
            tmpl.with_user(self.manager).unlink()

    def test_tp05_admin_delete_used(self):
        tmpl = self._make_template(name='UsedAdmin')
        tmpl.use_count = 3
        # Superuser / system admin can delete.
        tmpl.unlink()
        self.assertFalse(tmpl.exists())

    # ------------------------- Boundary -------------------------
    def test_tb01_one_char_body(self):
        tmpl = self._make_template(name='One', body_html='<p>x</p>')
        self.assertTrue(tmpl.id)

    def test_tb06_unsaved_record(self):
        tmpl = self._make_template(
            name='Unsaved', model_id=self.model_partner.id,
            body_html='<p>{{name}} {{today}}</p>',
        )
        resolved = tmpl._resolve_placeholders('res.partner', 0)
        self.assertNotIn('{{today}}', resolved)
        self.assertNotIn('{{name}}', resolved)  # field token -> empty string

    def test_tb07_unknown_token(self):
        tmpl = self._make_template(
            name='Unknown', model_id=self.model_partner.id,
            body_html='<p>{{nonexistent_field}}</p>',
        )
        partner = self.env['res.partner'].create({'name': 'X'})
        resolved = tmpl._resolve_placeholders('res.partner', partner.id)
        self.assertIn('{{nonexistent_field}}', resolved)

    def test_tb08_false_many2one(self):
        partner = self.env['res.partner'].create({'name': 'NoParent'})
        tmpl = self._make_template(
            name='Parent', model_id=self.model_partner.id,
            body_html='<p>[{{parent_id}}]</p>',
        )
        resolved = tmpl._resolve_placeholders('res.partner', partner.id)
        self.assertIn('[]', resolved)

    # ------------------------- Permissions -------------------------
    def test_pp01_user_cannot_create(self):
        with self.assertRaises(AccessError):
            self.Template.with_user(self.user).create({
                'name': 'ByUser',
                'category_id': self.category.id,
                'body_html': '<p>x</p>',
            })

    def test_pp02_user_can_read(self):
        tmpl = self._make_template(name='Readable')
        self.assertTrue(tmpl.with_user(self.user).read(['name']))

    def test_pp03_manager_can_create(self):
        tmpl = self.Template.with_user(self.manager).create({
            'name': 'ByManager',
            'category_id': self.category.id,
            'body_html': '<p>x</p>',
        })
        self.assertTrue(tmpl.id)

    # ------------------------- Cron -------------------------
    def test_tw04_tw05_cron_use_count(self):
        tmpl = self._make_template(name='Counted')
        for _i in range(10):
            self.Log.create({
                'template_id': tmpl.id,
                'template_name': tmpl.name,
                'user_id': self.env.user.id,
                'res_model': 'res.partner',
                'res_id': 1,
                'company_id': self.company.id,
            })
        # The cron uses raw SQL, so pending ORM writes must be flushed first.
        self.env.flush_all()
        self.Template._cron_recompute_use_counts()
        tmpl.invalidate_recordset(['use_count'])
        self.assertEqual(tmpl.use_count, 10)

        # Template with no logs resets to 0.
        empty = self._make_template(name='NoLogs')
        empty.use_count = 99
        self.env.flush_all()
        self.Template._cron_recompute_use_counts()
        empty.invalidate_recordset(['use_count'])
        self.assertEqual(empty.use_count, 0)

    # ------------------------- BR-09 -------------------------
    def test_br09_future_last_used(self):
        from odoo import fields
        from datetime import timedelta
        tmpl = self._make_template(name='Future')
        with self.assertRaises(ValidationError):
            tmpl.write({
                'last_used_date': fields.Datetime.now() + timedelta(days=1),
            })
