# (c) cavaliba.com - test_group_class.py

import yaml
from django.test import TestCase, override_settings

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


class GroupFromTest(TestCase):
    def setUp(self):
        cache.clear()
        datalist = yaml.safe_load("""
            - classname: group
              keyname: group01
              displayname: Group One
            """)
        aaa = {"perms": ["p_group_create"]}
        load_broker(datalist=datalist, aaa=aaa)

    def test_from_keyname(self):
        group = Group.from_keyname(keyname="group01")
        self.assertIsNotNone(group)
        self.assertIsInstance(group, Group)
        self.assertEqual(group.keyname, "group01")

    def test_from_keyname_missing(self):
        group = Group.from_keyname(keyname="does_not_exist")
        self.assertIsNone(group)

    def test_from_id(self):
        ref = Group.from_keyname(keyname="group01")
        group = Group.from_id(id=ref.id)
        self.assertIsNotNone(group)
        self.assertIsInstance(group, Group)
        self.assertEqual(group.keyname, "group01")

    def test_from_id_missing(self):
        group = Group.from_id(id=999999)
        self.assertIsNone(group)

    def test_from_iobj(self):
        iobj = DataInstance.objects.filter(classname="group", keyname="group01").first()
        self.assertIsNotNone(iobj)
        group = Group.from_iobj(iobj=iobj)
        self.assertIsNotNone(group)
        self.assertIsInstance(group, Group)
        self.assertEqual(group.keyname, "group01")

    def test_displayname(self):
        group = Group.from_keyname(keyname="group01")
        self.assertEqual(group.displayname, "Group One")


class GroupCRUDTest(TestCase):
    def setUp(self):
        cache.clear()

    def test_create(self):
        group = Group(keyname="group_create")
        group.displayname = "Create Test"
        result = group.create()
        self.assertTrue(result)
        self.assertTrue(group.is_bound)
        found = Group.from_keyname(keyname="group_create")
        self.assertIsNotNone(found)
        self.assertEqual(found.displayname, "Create Test")

    def test_update(self):
        group = Group(keyname="group_update")
        group.create()
        group.displayname = "Updated Name"
        result = group.update()
        self.assertTrue(result)
        found = Group.from_keyname(keyname="group_update")
        self.assertEqual(found.displayname, "Updated Name")

    def test_delete(self):
        group = Group(keyname="group_delete")
        group.create()
        self.assertTrue(group.is_bound)
        result = group.delete()
        self.assertTrue(result)
        self.assertFalse(group.is_bound)
        found = Group.from_keyname(keyname="group_delete")
        self.assertIsNone(found)

    def test_enable(self):
        group = Group(keyname="group_enable")
        group.is_enabled = False
        group.create()
        result = group.enable()
        self.assertTrue(result)
        self.assertTrue(group.is_enabled)
        found = Group.from_keyname(keyname="group_enable")
        self.assertTrue(found.is_enabled)

    def test_disable(self):
        group = Group(keyname="group_disable")
        group.create()
        self.assertTrue(group.is_enabled)
        result = group.disable()
        self.assertTrue(result)
        self.assertFalse(group.is_enabled)
        found = Group.from_keyname(keyname="group_disable")
        self.assertFalse(found.is_enabled)


class GroupGetSubgroupsTest(TestCase):
    """
    Hierarchy:
        parent  subgroups: [child1, child2]
        child1  subgroups: [grandchild]
        child2  (leaf)
        grandchild  (leaf)
        unrelated  (no relation to parent)
    """

    def setUp(self):
        cache.clear()
        datalist = yaml.safe_load("""
            - classname: group
              keyname: grandchild
              displayname: Grandchild

            - classname: group
              keyname: child1
              displayname: Child One
              subgroups:
                - grandchild

            - classname: group
              keyname: child2
              displayname: Child Two

            - classname: group
              keyname: parent
              displayname: Parent
              subgroups:
                - child1
                - child2

            - classname: group
              keyname: unrelated
              displayname: Unrelated
            """)
        aaa = {"perms": ["p_group_create"]}
        load_broker(datalist=datalist, aaa=aaa)

    def test_returns_list(self):
        g = Group.from_keyname(keyname="parent")
        self.assertIsInstance(g.get_subgroups(), list)

    def test_direct_subgroups_included(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {s.keyname for s in g.get_subgroups()}
        self.assertIn("child1", keynames)
        self.assertIn("child2", keynames)

    def test_transitive_subgroup_included(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {s.keyname for s in g.get_subgroups()}
        self.assertIn("grandchild", keynames)

    def test_unrelated_excluded(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {s.keyname for s in g.get_subgroups()}
        self.assertNotIn("unrelated", keynames)

    def test_self_not_in_subgroups(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {s.keyname for s in g.get_subgroups()}
        self.assertNotIn("parent", keynames)

    def test_no_duplicates(self):
        g = Group.from_keyname(keyname="parent")
        keynames = [s.keyname for s in g.get_subgroups()]
        self.assertEqual(len(keynames), len(set(keynames)))

    def test_leaf_group_returns_empty(self):
        g = Group.from_keyname(keyname="child2")
        self.assertEqual(g.get_subgroups(), [])

    def test_returns_group_instances(self):
        g = Group.from_keyname(keyname="parent")
        for s in g.get_subgroups():
            self.assertIsInstance(s, Group)
            self.assertTrue(s.is_bound)

    def test_sets_class_variable(self):
        g = Group.from_keyname(keyname="parent")
        self.assertIsNone(g.subgroups)
        g.get_subgroups()
        self.assertIsNotNone(g.subgroups)


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

            - classname: user
              keyname: user02
              displayname: User Two

            - classname: group
              keyname: grp_with_users
              displayname: Group With Users
              users:
                - user01
                - user02

            - classname: group
              keyname: grp_empty
              displayname: Group Empty
            """)
        aaa = {"perms": ["p_user_create", "p_group_create"]}
        load_broker(datalist=datalist, aaa=aaa)

    def test_returns_list(self):
        g = Group.from_keyname(keyname="grp_with_users")
        self.assertIsInstance(g.get_direct_users(), list)

    def test_finds_direct_users(self):
        g = Group.from_keyname(keyname="grp_with_users")
        keynames = {u.keyname for u in g.get_direct_users()}
        self.assertIn("user01", keynames)
        self.assertIn("user02", keynames)

    def test_empty_group_returns_empty(self):
        g = Group.from_keyname(keyname="grp_empty")
        self.assertEqual(g.get_direct_users(), [])

    def test_no_duplicates(self):
        g = Group.from_keyname(keyname="grp_with_users")
        keynames = [u.keyname for u in g.get_direct_users()]
        self.assertEqual(len(keynames), len(set(keynames)))

    def test_returns_user_instances(self):
        g = Group.from_keyname(keyname="grp_with_users")
        for u in g.get_direct_users():
            self.assertIsInstance(u, User)
            self.assertTrue(u.is_bound)

    def test_sets_class_variable(self):
        g = Group.from_keyname(keyname="grp_with_users")
        self.assertIsNone(g.direct_users)
        g.get_direct_users()
        self.assertIsNotNone(g.direct_users)


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

            - classname: user
              keyname: user02
              displayname: User Two

            - classname: group
              keyname: grp_auto
              displayname: Group Auto
              autogroup_users:
                - user01
                - user02

            - classname: group
              keyname: grp_empty
              displayname: Group Empty
            """)
        aaa = {"perms": ["p_user_create", "p_group_create"]}
        load_broker(datalist=datalist, aaa=aaa)

    def test_returns_list(self):
        g = Group.from_keyname(keyname="grp_auto")
        self.assertIsInstance(g.get_auto_users(), list)

    def test_finds_auto_users(self):
        g = Group.from_keyname(keyname="grp_auto")
        keynames = {u.keyname for u in g.get_auto_users()}
        self.assertIn("user01", keynames)
        self.assertIn("user02", keynames)

    def test_empty_group_returns_empty(self):
        g = Group.from_keyname(keyname="grp_empty")
        self.assertEqual(g.get_auto_users(), [])

    def test_no_duplicates(self):
        g = Group.from_keyname(keyname="grp_auto")
        keynames = [u.keyname for u in g.get_auto_users()]
        self.assertEqual(len(keynames), len(set(keynames)))

    def test_returns_user_instances(self):
        g = Group.from_keyname(keyname="grp_auto")
        for u in g.get_auto_users():
            self.assertIsInstance(u, User)
            self.assertTrue(u.is_bound)

    def test_sets_class_variable(self):
        g = Group.from_keyname(keyname="grp_auto")
        self.assertIsNone(g.auto_users)
        g.get_auto_users()
        self.assertIsNotNone(g.auto_users)


class GroupGetUsersTest(TestCase):
    """
    grp_mixed  users: [user01, user02]  autogroup_users: [user02, user03]
    user02 in both — dedup check.
    """

    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: group
              keyname: grp_mixed
              displayname: Group Mixed
              users:
                - user01
                - user02
              autogroup_users:
                - user02
                - user03

            - classname: group
              keyname: grp_empty
              displayname: Group Empty
            """)
        aaa = {"perms": ["p_user_create", "p_group_create"]}
        load_broker(datalist=datalist, aaa=aaa)

    def test_returns_list(self):
        g = Group.from_keyname(keyname="grp_mixed")
        self.assertIsInstance(g.get_users(), list)

    def test_includes_direct_users(self):
        g = Group.from_keyname(keyname="grp_mixed")
        keynames = {u.keyname for u in g.get_users()}
        self.assertIn("user01", keynames)

    def test_includes_auto_users(self):
        g = Group.from_keyname(keyname="grp_mixed")
        keynames = {u.keyname for u in g.get_users()}
        self.assertIn("user03", keynames)

    def test_no_duplicates_across_sources(self):
        g = Group.from_keyname(keyname="grp_mixed")
        keynames = [u.keyname for u in g.get_users()]
        self.assertEqual(len(keynames), len(set(keynames)))
        self.assertEqual(keynames.count("user02"), 1)

    def test_empty_group_returns_empty(self):
        g = Group.from_keyname(keyname="grp_empty")
        self.assertEqual(g.get_users(), [])

    def test_returns_user_instances(self):
        g = Group.from_keyname(keyname="grp_mixed")
        for u in g.get_users():
            self.assertIsInstance(u, User)
            self.assertTrue(u.is_bound)

    def test_sets_class_variable(self):
        g = Group.from_keyname(keyname="grp_mixed")
        self.assertIsNone(g.users)
        g.get_users()
        self.assertIsNotNone(g.users)


class GroupGetUsersAllTest(TestCase):
    """
    Hierarchy:
        parent    users: [user01]   subgroups: [child]
          child   users: [user02]   autogroup_users: [user01, user03]
                  subgroups: [grandchild]
            grandchild  users: [user04]
        unrelated users: [user05]
    user01 in both parent.users and child.autogroup_users — cross-group dedup check.
    """

    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: user
              keyname: user04
              displayname: User Four

            - classname: user
              keyname: user05
              displayname: User Five

            - classname: group
              keyname: grandchild
              displayname: Grandchild
              users:
                - user04

            - classname: group
              keyname: child
              displayname: Child
              users:
                - user02
              autogroup_users:
                - user01
                - user03
              subgroups:
                - grandchild

            - classname: group
              keyname: parent
              displayname: Parent
              users:
                - user01
              subgroups:
                - child

            - classname: group
              keyname: unrelated
              displayname: Unrelated
              users:
                - user05
            """)
        aaa = {"perms": ["p_user_create", "p_group_create"]}
        load_broker(datalist=datalist, aaa=aaa)

    def test_returns_list(self):
        g = Group.from_keyname(keyname="parent")
        self.assertIsInstance(g.get_users_all(), list)

    def test_includes_self_users(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {u.keyname for u in g.get_users_all()}
        self.assertIn("user01", keynames)

    def test_includes_subgroup_direct_users(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {u.keyname for u in g.get_users_all()}
        self.assertIn("user02", keynames)

    def test_includes_subgroup_auto_users(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {u.keyname for u in g.get_users_all()}
        self.assertIn("user03", keynames)

    def test_includes_transitive_subgroup_users(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {u.keyname for u in g.get_users_all()}
        self.assertIn("user04", keynames)

    def test_excludes_unrelated_group_users(self):
        g = Group.from_keyname(keyname="parent")
        keynames = {u.keyname for u in g.get_users_all()}
        self.assertNotIn("user05", keynames)

    def test_no_duplicates_across_groups(self):
        g = Group.from_keyname(keyname="parent")
        keynames = [u.keyname for u in g.get_users_all()]
        self.assertEqual(len(keynames), len(set(keynames)))
        self.assertEqual(keynames.count("user01"), 1)

    def test_leaf_group_returns_own_users_only(self):
        g = Group.from_keyname(keyname="grandchild")
        keynames = {u.keyname for u in g.get_users_all()}
        self.assertEqual(keynames, {"user04"})

    def test_returns_user_instances(self):
        g = Group.from_keyname(keyname="parent")
        for u in g.get_users_all():
            self.assertIsInstance(u, User)
            self.assertTrue(u.is_bound)

    def test_sets_class_variable(self):
        g = Group.from_keyname(keyname="parent")
        self.assertIsNone(g.users_all)
        g.get_users_all()
        self.assertIsNotNone(g.users_all)


class GroupMaxComputedUserTest(TestCase):
    def setUp(self):
        cache.clear()
        datalist = yaml.safe_load("""
            - classname: user
              keyname: userA
              displayname: User A

            - classname: user
              keyname: userB
              displayname: User B

            - classname: user
              keyname: userC
              displayname: User C

            - classname: group
              keyname: grp_limit
              displayname: Group Limit
              users:
                - userA
                - userB
                - userC
            """)
        aaa = {"perms": ["p_user_create", "p_group_create"]}
        load_broker(datalist=datalist, aaa=aaa)

    @override_settings(CAVALIBA_MAX_COMPUTED_USER=2)
    def test_direct_users_capped_at_max(self):
        g = Group.from_keyname(keyname="grp_limit")
        result = g.get_direct_users()
        self.assertLessEqual(len(result), 2)

    @override_settings(CAVALIBA_MAX_COMPUTED_USER=2)
    def test_users_capped_at_max(self):
        g = Group.from_keyname(keyname="grp_limit")
        result = g.get_users()
        self.assertLessEqual(len(result), 2)
