# -*- 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)

    @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_sales_team_member_search(self):
        """ when a search is triggered on the member_ids field in crm.team
        it is currently returning the archived records also. this test will
        ensure that the search wont return archived record.

        this is to fix unwanted ORM behavior
        """
        self.env['res.partner'].create({'name': 'Test Partner', 'team_id': self.new_team.id})
        self.env['crm.team.member'].create({
            'user_id': self.env.uid,
            'crm_team_id': self.new_team.id,
            'active': False,
        })
        partner_exists = self.env['res.partner'].search([
            ('team_id.member_ids', 'in', [self.env.uid])
        ])
        self.assertFalse(partner_exists, msg="Partner should return empty as current user is removed from team")

    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)
