# (c) cavaliba.com - test_group_autogroup.py

import yaml
from django.test import TestCase

import app_home.cache as cache
from app_data.group import Group
from app_data.loader import load_broker
from app_data.user import User


class GroupGetUsersComputedTest(TestCase):
    """
    Schema layout:
        project.owner  -> user   (G1 target: project:*:owner)
            proj1.owner = [user01]
            proj2.owner = [user02]
            proj3.owner = [user01]   <- duplicate of proj1, tests deduplication

        team.leader    -> user   (G2 target: team:team1:*)
        team.deputy    -> user
        team.codename  -> string  <- non-user field, must be ignored by G2
            team1.leader = [user01]
            team1.deputy = [user02]
            team1.codename = alpha

        grp_g1   autogroup: 'project:*:owner'
        grp_g2   autogroup: 'team:team1:*'
        grp_none autogroup: (empty)
        grp_bad  autogroup: 'bad_grammar'
    """

    def setUp(self):
        cache.clear()
        datalist = yaml.safe_load("""
            - classname: user
              keyname: user01
              displayname: User One

            - classname: user
              keyname: user02
              displayname: User Two

            - classname: user
              keyname: user03
              displayname: User Three

            - classname: _schema
              keyname: project
              displayname: Project
              owner:
                displayname: Owner
                dataformat: schema
                dataformat_ext: user
                cardinal_max: 0

            - classname: project
              keyname: proj1
              owner:
                - user01

            - classname: project
              keyname: proj2
              owner:
                - user02

            - classname: project
              keyname: proj3
              owner:
                - user01

            - classname: _schema
              keyname: team
              displayname: Team
              leader:
                displayname: Leader
                dataformat: schema
                dataformat_ext: user
              deputy:
                displayname: Deputy
                dataformat: schema
                dataformat_ext: user
              codename:
                displayname: Codename
                dataformat: string

            - classname: team
              keyname: team1
              leader: user01
              deputy: user02
              codename: alpha

            - classname: group
              keyname: grp_g1
              displayname: Group G1
              autogroup: 'project:*:owner'

            - classname: group
              keyname: grp_g2
              displayname: Group G2
              autogroup: 'team:team1:*'

            - classname: group
              keyname: grp_none
              displayname: Group No Autogroup

            - classname: group
              keyname: grp_bad
              displayname: Group Bad Grammar
              autogroup: 'bad_grammar'
            """)
        aaa = {"perms": ["p_user_create", "p_group_create", "p_schema_write", "p_data_admin"]}
        load_broker(datalist=datalist, aaa=aaa)

    # -----------------------------------
    # get_users_computed_g1
    # -----------------------------------

    def test_g1_returns_list(self):
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed_g1("project", "owner")
        self.assertIsInstance(result, list)

    def test_g1_finds_users_from_all_instances(self):
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed_g1("project", "owner")
        keynames = {u.keyname for u in result}
        self.assertIn("user01", keynames)
        self.assertIn("user02", keynames)

    def test_g1_deduplicates_users(self):
        # user01 appears in proj1 and proj3 — must appear once
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed_g1("project", "owner")
        keynames = [u.keyname for u in result]
        self.assertEqual(keynames.count("user01"), 1)

    def test_g1_excludes_non_member_users(self):
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed_g1("project", "owner")
        keynames = {u.keyname for u in result}
        self.assertNotIn("user03", keynames)

    def test_g1_unknown_schema_returns_empty(self):
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed_g1("no_such_schema", "owner")
        self.assertEqual(result, [])

    def test_g1_unknown_fieldname_returns_empty(self):
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed_g1("project", "no_such_field")
        self.assertEqual(result, [])

    def test_g1_returns_user_instances(self):
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed_g1("project", "owner")
        for u in result:
            self.assertIsInstance(u, User)
            self.assertTrue(u.is_bound)

    # -----------------------------------
    # get_users_computed_g2
    # -----------------------------------

    def test_g2_returns_list(self):
        g = Group.from_keyname(keyname="grp_g2")
        result = g.get_users_computed_g2("team", "team1")
        self.assertIsInstance(result, list)

    def test_g2_finds_users_from_all_user_fields(self):
        g = Group.from_keyname(keyname="grp_g2")
        result = g.get_users_computed_g2("team", "team1")
        keynames = {u.keyname for u in result}
        self.assertIn("user01", keynames)
        self.assertIn("user02", keynames)

    def test_g2_ignores_non_user_fields(self):
        # codename is a string field — its value must not appear as a user
        g = Group.from_keyname(keyname="grp_g2")
        result = g.get_users_computed_g2("team", "team1")
        keynames = {u.keyname for u in result}
        self.assertNotIn("alpha", keynames)

    def test_g2_unknown_instance_returns_empty(self):
        g = Group.from_keyname(keyname="grp_g2")
        result = g.get_users_computed_g2("team", "no_such_instance")
        self.assertEqual(result, [])

    def test_g2_unknown_schema_returns_empty(self):
        g = Group.from_keyname(keyname="grp_g2")
        result = g.get_users_computed_g2("no_such_schema", "team1")
        self.assertEqual(result, [])

    def test_g2_returns_user_instances(self):
        g = Group.from_keyname(keyname="grp_g2")
        result = g.get_users_computed_g2("team", "team1")
        for u in result:
            self.assertIsInstance(u, User)
            self.assertTrue(u.is_bound)

    # -----------------------------------
    # get_users_computed (dispatcher)
    # -----------------------------------

    def test_computed_dispatches_to_g1(self):
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed()
        keynames = {u.keyname for u in result}
        self.assertIn("user01", keynames)
        self.assertIn("user02", keynames)

    def test_computed_dispatches_to_g2(self):
        g = Group.from_keyname(keyname="grp_g2")
        result = g.get_users_computed()
        keynames = {u.keyname for u in result}
        self.assertIn("user01", keynames)
        self.assertIn("user02", keynames)

    def test_computed_empty_autogroup_returns_empty(self):
        g = Group.from_keyname(keyname="grp_none")
        result = g.get_users_computed()
        self.assertEqual(result, [])

    def test_computed_invalid_grammar_returns_empty(self):
        g = Group.from_keyname(keyname="grp_bad")
        result = g.get_users_computed()
        self.assertEqual(result, [])

    def test_computed_returns_list(self):
        g = Group.from_keyname(keyname="grp_g1")
        result = g.get_users_computed()
        self.assertIsInstance(result, list)

    # -----------------------------------
    # autogroup_update
    # -----------------------------------

    def test_autogroup_update_returns_count_of_groups_with_users(self):
        # grp_g1 and grp_g2 have autogroup rules that yield users; grp_none and grp_bad yield none
        count = Group.autogroup_update()
        self.assertEqual(count, 2)

    def test_autogroup_update_persists_autogroup_users(self):
        Group.autogroup_update()
        g = Group.from_keyname(keyname="grp_g1")
        stored = g.get_attribute("autogroup_users")
        # get_attribute returns DataInstance objects; compare by keyname
        keynames = {obj.keyname for obj in stored if obj}
        self.assertIn("user01", keynames)
        self.assertIn("user02", keynames)

    def test_autogroup_update_with_specific_group(self):
        g = Group.from_keyname(keyname="grp_g1")
        count = Group.autogroup_update(groups=[g])
        self.assertEqual(count, 1)

    def test_autogroup_update_empty_list_updates_all(self):
        count = Group.autogroup_update(groups=[])
        self.assertEqual(count, 2)

    def test_autogroup_update_group_without_rule_stores_empty(self):
        Group.autogroup_update()
        g = Group.from_keyname(keyname="grp_none")
        stored = g.get_attribute("autogroup_users")
        self.assertEqual(stored, [])
