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

from odoo import exceptions
from odoo.addons.sales_team.tests.common import TestSalesCommon
from odoo.tests.common import users
from odoo.tools import mute_logger


class TestMembership(TestSalesCommon):
    """Tests to ensure membership behavior """

    @classmethod
    def setUpClass(cls):
        super(TestMembership, cls).setUpClass()
        cls.new_team = cls.env['crm.team'].create({
            'name': 'Test Specific',
            'sequence': 10,
        })
        cls.env['ir.config_parameter'].set_param('sales_team.membership_multi', True)

    def test_archive_user_archives_team_member(self):
        """Test that archiving a user also archives their linked team member."""
        self.assertTrue(self.sales_team_1_m1.active)
        self.user_sales_leads.action_archive()
        self.assertFalse(self.sales_team_1_m1.active)

    @users('user_sales_manager')
    def test_fields(self):
        self.assertTrue(self.sales_team_1.with_user(self.env.user).is_membership_multi)
        self.assertTrue(self.new_team.with_user(self.env.user).is_membership_multi)

        self.env['ir.config_parameter'].sudo().set_param('sales_team.membership_multi', False)
        self.assertFalse(self.sales_team_1.with_user(self.env.user).is_membership_multi)
        self.assertFalse(self.new_team.with_user(self.env.user).is_membership_multi)

    @users('user_sales_manager')
    def test_members_mono(self):
        """ Test mono mode using the user m2m relationship """
        self.env['ir.config_parameter'].sudo().set_param('sales_team.membership_multi', False)
        # ensure initial data
        sales_team_1 = self.sales_team_1.with_user(self.env.user)
        new_team = self.new_team.with_user(self.env.user)
        self.assertEqual(sales_team_1.member_ids, self.user_sales_leads | self.user_admin)

        # test various add / remove on computed m2m
        self.assertEqual(new_team.member_ids, self.env['res.users'])
        new_team.write({'member_ids': [(4, self.env.uid)]})
        self.assertEqual(new_team.member_ids, self.env.user)
        new_team.write({'member_ids': [(4, self.user_sales_leads.id)]})
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)
        new_team.write({'member_ids': [(3, self.user_sales_leads.id)]})
        self.assertEqual(new_team.member_ids, self.env.user)
        new_team.write({'member_ids': [(6, 0, (self.user_sales_leads | self.env.user).ids)]})
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)

        # archived memberships on sales_team_1 for user_sales_leads
        self.assertEqual(sales_team_1.member_ids, self.user_admin)

        # create a new user on the fly, just for testing
        self.user_sales_manager.write({'groups_id': [(4, self.env.ref('base.group_system').id)]})
        new_team.write({'member_ids': [(0, 0, {
            'name': 'Marty OnTheMCFly',
            'login': 'mcfly@test.example.com',
        })]})
        new_user = self.env['res.users'].search([('login', '=', 'mcfly@test.example.com')])
        self.assertTrue(len(new_user))
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads | new_user)
        self.user_sales_manager.write({'groups_id': [(3, self.env.ref('base.group_system').id)]})

        self.env.flush_all()
        memberships = self.env['crm.team.member'].with_context(active_test=False).search([('user_id', '=', self.user_sales_leads.id)])
        self.assertEqual(len(memberships), 3)  # subscribed twice to new_team + subscribed to sales_team_1
        self.assertEqual(memberships.crm_team_id, sales_team_1 | new_team)
        self.assertFalse(memberships.filtered(lambda m: m.crm_team_id == sales_team_1).active)
        new_team_memberships = memberships.filtered(lambda m: m.crm_team_id == new_team)
        self.assertEqual(len(new_team_memberships), 2)  # subscribed, removed, then subscribed again
        self.assertTrue(set(new_team_memberships.mapped('active')), set([False, True]))

        # still avoid duplicated team / user entries
        with self.assertRaises(exceptions.UserError):
            self.env['crm.team.member'].create({'crm_team_id': new_team.id, 'user_id': new_user.id})

    @users('user_sales_manager')
    def test_members_multi(self):
        # ensure initial data
        sales_team_1 = self.sales_team_1.with_user(self.env.user)
        new_team = self.new_team.with_user(self.env.user)
        self.assertEqual(sales_team_1.member_ids, self.user_sales_leads | self.user_admin)

        # test various add / remove on computed m2m
        self.assertEqual(new_team.member_ids, self.env['res.users'])
        new_team.write({'member_ids': [(4, self.env.uid), (4, self.user_sales_leads.id)]})
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)
        new_team.write({'member_ids': [(3, self.user_sales_leads.id)]})
        self.assertEqual(new_team.member_ids, self.env.user)
        new_team.write({'member_ids': [(6, 0, (self.user_sales_leads | self.env.user).ids)]})
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)

        # nothing changed on sales_team_1
        self.assertEqual(sales_team_1.member_ids, self.user_sales_leads | self.user_admin)

        # create a new user on the fly, just for testing
        self.user_sales_manager.write({'groups_id': [(4, self.env.ref('base.group_system').id)]})
        new_team.write({'member_ids': [(0, 0, {
            'name': 'Marty OnTheMCFly',
            'login': 'mcfly@test.example.com',
        })]})
        new_user = self.env['res.users'].search([('login', '=', 'mcfly@test.example.com')])
        self.assertTrue(len(new_user))
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads | new_user)
        self.user_sales_manager.write({'groups_id': [(3, self.env.ref('base.group_system').id)]})
        self.env.flush_all()

        # still avoid duplicated team / user entries
        with self.assertRaises(exceptions.UserError):
            self.env['crm.team.member'].create({'crm_team_id': new_team.id, 'user_id': new_user.id})

    @users('user_sales_manager')
    def test_memberships_mono(self):
        """ Test mono mode: updating crm_team_member_ids field """
        self.env['ir.config_parameter'].sudo().set_param('sales_team.membership_multi', False)
        # ensure initial data
        sales_team_1 = self.env['crm.team'].browse(self.sales_team_1.ids)
        new_team = self.env['crm.team'].browse(self.new_team.ids)
        self.assertEqual(sales_team_1.member_ids, self.user_sales_leads | self.user_admin)

        # subscribe on new team (user_sales_leads will have two memberships -> old one deactivated)
        self.assertEqual(new_team.member_ids, self.env['res.users'])
        new_team.write({'crm_team_member_ids': [
            (0, 0, {'user_id': self.user_sales_leads.id}),
            (0, 0, {'user_id': self.uid}),
        ]})
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)
        self.assertEqual(sales_team_1.member_ids, self.user_admin)
        self.env.flush_all()

        memberships = self.env['crm.team.member'].with_context(active_test=False).search([('user_id', '=', self.user_sales_leads.id)])
        self.assertEqual(memberships.crm_team_id, sales_team_1 | new_team)
        self.assertFalse(memberships.filtered(lambda m: m.crm_team_id == sales_team_1).active)
        self.assertTrue(memberships.filtered(lambda m: m.crm_team_id == new_team).active)

        # subscribe user_sales_leads on old team -> old membership still archived and kept
        sales_team_1.write({'crm_team_member_ids': [(0, 0, {'user_id': self.user_sales_leads.id})]})
        memberships_new = self.env['crm.team.member'].with_context(active_test=False).search([('user_id', '=', self.user_sales_leads.id)])
        self.assertTrue(memberships < memberships_new)
        self.assertEqual(memberships.crm_team_id, sales_team_1 | new_team)

        # old membership is still inactive, new membership is active
        old_st_1 = memberships_new.filtered(lambda m: m.crm_team_id == sales_team_1 and m in memberships)
        new_st_1 = memberships_new.filtered(lambda m: m.crm_team_id == sales_team_1 and m not in memberships)
        new_nt = memberships_new.filtered(lambda m: m.crm_team_id == new_team)
        self.assertFalse(old_st_1.active)
        self.assertTrue(new_st_1.active)
        self.assertFalse(new_nt.active)

        # check members fields
        self.assertEqual(new_team.member_ids, self.env.user)
        self.assertEqual(sales_team_1.member_ids, self.user_admin | self.user_sales_leads)

        # activate another team membership: previous team membership should be de activated
        new_nt.toggle_active()
        self.assertTrue(new_nt.active)
        self.assertFalse(old_st_1.active)
        self.assertFalse(new_st_1.active)
        # activate another team membership: previous team membership should be de activated
        old_st_1.toggle_active()
        self.assertFalse(new_nt.active)
        self.assertTrue(old_st_1.active)
        self.assertFalse(new_st_1.active)

        # try to activate duplicate memberships again, which should trigger issues
        with self.assertRaises(exceptions.UserError):
            new_st_1.toggle_active()

    @users('user_sales_manager')
    def test_memberships_multi(self):
        # ensure initial data
        sales_team_1 = self.env['crm.team'].browse(self.sales_team_1.ids)
        new_team = self.env['crm.team'].browse(self.new_team.ids)
        self.assertEqual(sales_team_1.member_ids, self.user_sales_leads | self.user_admin)

        # subscribe on new team (user_sales_leads will have two memberships -> old one deactivated)
        self.assertEqual(new_team.member_ids, self.env['res.users'])
        new_team.write({'crm_team_member_ids': [
            (0, 0, {'user_id': self.user_sales_leads.id}),
            (0, 0, {'user_id': self.uid}),
        ]})
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)
        self.assertEqual(sales_team_1.member_ids, self.user_sales_leads | self.user_admin)
        self.env.flush_all()

        memberships = self.env['crm.team.member'].with_context(active_test=False).search([('user_id', '=', self.user_sales_leads.id)])
        self.assertEqual(memberships.crm_team_id, sales_team_1 | new_team)
        self.assertTrue(memberships.filtered(lambda m: m.crm_team_id == sales_team_1).active)
        self.assertTrue(memberships.filtered(lambda m: m.crm_team_id == new_team).active)

        # archive membership on sales_team_1 and try creating a new one
        memberships.filtered(lambda m: m.crm_team_id == sales_team_1).write({'active': False})
        # subscribe user_sales_leads on old team -> old membership still archived and kept
        sales_team_1.write({'crm_team_member_ids': [(0, 0, {'user_id': self.user_sales_leads.id})]})
        memberships_new = self.env['crm.team.member'].with_context(active_test=False).search([('user_id', '=', self.user_sales_leads.id)])
        self.assertTrue(memberships < memberships_new)
        self.assertEqual(memberships.crm_team_id, sales_team_1 | new_team)

        # old membership is still inactive, new membership is active
        old_st_1 = memberships_new.filtered(lambda m: m.crm_team_id == sales_team_1 and m in memberships)
        new_st_1 = memberships_new.filtered(lambda m: m.crm_team_id == sales_team_1 and m not in memberships)
        new_nt = memberships_new.filtered(lambda m: m.crm_team_id == new_team)
        self.assertFalse(old_st_1.active)
        self.assertTrue(new_st_1.active)
        self.assertTrue(new_nt.active)

        # check members fields
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)
        self.assertEqual(sales_team_1.member_ids, self.user_admin | self.user_sales_leads)

        # try to activate duplicate memberships again, which should trigger issues
        with self.assertRaises(exceptions.UserError):
            old_st_1.toggle_active()

    @users('user_sales_manager')
    def test_memberships_sync(self):
        sales_team_1 = self.env['crm.team'].browse(self.sales_team_1.ids)
        new_team = self.env['crm.team'].browse(self.new_team.ids)
        self.assertEqual(sales_team_1.member_ids, self.user_sales_leads | self.user_admin)
        self.assertEqual(new_team.crm_team_member_ids, self.env['crm.team.member'])
        self.assertEqual(new_team.crm_team_member_all_ids, self.env['crm.team.member'])
        self.assertEqual(new_team.member_ids, self.env['res.users'])

        # creating memberships correctly updates m2m without any refresh
        new_member = self.env['crm.team.member'].create({
            'user_id': self.env.user.id,
            'crm_team_id': self.new_team.id,
        })
        self.assertEqual(new_team.crm_team_member_ids, new_member)
        self.assertEqual(new_team.crm_team_member_all_ids, new_member)
        self.assertEqual(new_team.member_ids, self.env.user)

        # adding members correctly update o2m with right values
        new_team.write({
            'member_ids': [(4, self.user_sales_leads.id)]
        })
        added = self.env['crm.team.member'].search([('crm_team_id', '=', new_team.id), ('user_id', '=', self.user_sales_leads.id)])
        self.assertEqual(new_team.crm_team_member_ids, new_member + added)
        self.assertEqual(new_team.crm_team_member_all_ids, new_member + added)
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)

        # archiving membership correctly updates m2m and o2m
        added.write({'active': False})
        self.assertEqual(new_team.crm_team_member_ids, new_member)
        self.assertEqual(new_team.crm_team_member_all_ids, new_member + added)
        self.assertEqual(new_team.member_ids, self.env.user)

        # reactivating correctly updates m2m and o2m
        added.write({'active': True})
        self.assertEqual(new_team.crm_team_member_ids, new_member + added)
        self.assertEqual(new_team.crm_team_member_all_ids, new_member + added)
        self.assertEqual(new_team.member_ids, self.env.user | self.user_sales_leads)

        # archived are kept if duplicated on write
        admin_original = self.env['crm.team.member'].search([
            ('crm_team_id', '=', sales_team_1.id),
            ('user_id', '=', self.user_admin.id)
        ])
        self.assertTrue(bool(admin_original))
        admin_archived = self.env['crm.team.member'].create({
            'crm_team_id': new_team.id,
            'user_id': self.user_admin.id,
            'active': False,
        })
        admin_original.write({'crm_team_id': new_team.id})
        # send to db as errors may pop at that step (like trying to set NULL on a m2o inverse of o2m)
        self.env.flush_all()
        self.assertTrue(self.user_admin in new_team.member_ids)
        self.assertTrue(admin_original.active)
        self.assertTrue(admin_archived.exists())
        self.assertFalse(admin_archived.active)

        # change team of membership should raise unicity constraint
        with self.assertRaises(exceptions.UserError), mute_logger('odoo.sql_db'):
            added.write({'crm_team_id': sales_team_1.id})

    def test_users_sale_team_id(self):
        self.assertTrue(self.sales_team_1.sequence < self.new_team.sequence)

        self.assertEqual(self.user_sales_leads.crm_team_ids, self.sales_team_1)
        self.assertEqual(self.user_sales_leads.sale_team_id, self.sales_team_1)

        # subscribe to new team -> default team is still the old one
        self.new_team.write({
            'member_ids': [(4, self.user_sales_leads.id)]
        })
        self.assertEqual(self.user_sales_leads.crm_team_ids, self.sales_team_1 | self.new_team)
        self.assertEqual(self.user_sales_leads.sale_team_id, self.sales_team_1)

        # archive membership to first team -> second one becomes default
        self.sales_team_1_m1.write({'active': False})
        self.assertEqual(self.user_sales_leads.crm_team_ids, self.new_team)
        self.assertEqual(self.user_sales_leads.sale_team_id, self.new_team)

        # activate membership to first team -> first one becomes default again
        self.sales_team_1_m1.write({'active': True})
        self.assertEqual(self.user_sales_leads.crm_team_ids, self.sales_team_1 | self.new_team)
        self.assertEqual(self.user_sales_leads.sale_team_id, self.sales_team_1)

        # keep only one membership -> default team
        self.sales_team_1_m1.unlink()
        self.assertEqual(self.user_sales_leads.crm_team_ids, self.new_team)
        self.assertEqual(self.user_sales_leads.sale_team_id, self.new_team)
