"""
Shared pytest fixtures for topdownrbac tests.

Provides common setup fixtures and helper functions used across test modules.

NOTE: Django model imports are deferred to inside fixtures/functions so that
this module can be loaded by pytest's assertion rewriter before django.setup()
has been called by pytest-django.
"""

import pytest

# ---------------------------------------------------------------------------
# Permission assertion helpers (plain functions, not fixtures)
# ---------------------------------------------------------------------------


def assert_has_perm(user, perm_codename, obj):
    """Assert that user has the given permission on obj."""
    assert user.has_perm(perm_codename, obj=obj), f"{user.username} should have '{perm_codename}' on {obj}"


def assert_no_perm(user, perm_codename, obj):
    """Assert that user does NOT have the given permission on obj."""
    assert not user.has_perm(perm_codename, obj=obj), f"{user.username} should NOT have '{perm_codename}' on {obj}"


def assert_has_view(user, obj):
    assert_has_perm(user, "view_restrictedobject", obj)


def assert_no_view(user, obj):
    assert_no_perm(user, "view_restrictedobject", obj)


def assert_has_change(user, obj):
    assert_has_perm(user, "change_restrictedobject", obj)


def assert_no_change(user, obj):
    assert_no_perm(user, "change_restrictedobject", obj)


def assert_has_delete(user, obj):
    assert_has_perm(user, "delete_restrictedobject", obj)


def assert_no_delete(user, obj):
    assert_no_perm(user, "delete_restrictedobject", obj)


def assert_has_view_and_change(user, obj):
    assert_has_view(user, obj)
    assert_has_change(user, obj)


def assert_no_view_and_change(user, obj):
    assert_no_view(user, obj)
    assert_no_change(user, obj)


def assert_perms_on_objects(user, perm_codename, objects, expected):
    """Assert permission state on multiple objects."""
    for obj in objects:
        if expected:
            assert_has_perm(user, perm_codename, obj)
        else:
            assert_no_perm(user, perm_codename, obj)


# ---------------------------------------------------------------------------
# Permission fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def perm_view(db):
    from django.contrib.auth.models import Permission

    return Permission.objects.get(codename="view_restrictedobject")


@pytest.fixture
def perm_change(db):
    from django.contrib.auth.models import Permission

    return Permission.objects.get(codename="change_restrictedobject")


@pytest.fixture
def perm_delete(db):
    from django.contrib.auth.models import Permission

    return Permission.objects.get(codename="delete_restrictedobject")


# ---------------------------------------------------------------------------
# Base RBAC fixtures (flat structure)
# ---------------------------------------------------------------------------


@pytest.fixture
def user1(db):
    from topdownrbac.models import UserSubject

    return UserSubject.objects.create_user(username="user1", password="pass")


@pytest.fixture
def user2(db):
    from topdownrbac.models import UserSubject

    return UserSubject.objects.create_user(username="user2", password="pass")


@pytest.fixture
def role1(db):
    from topdownrbac.models import Role

    return Role.objects.create(name="role1")


@pytest.fixture
def role2(db):
    from topdownrbac.models import Role

    return Role.objects.create(name="role2")


@pytest.fixture
def obj1(db):
    from topdownrbac.models import RestrictedObject

    return RestrictedObject.objects.create()


@pytest.fixture
def obj2(db):
    from topdownrbac.models import RestrictedObject

    return RestrictedObject.objects.create()


# ---------------------------------------------------------------------------
# Hierarchy fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def hierarchy(db):
    """
    Create a hierarchical RestrictedObject structure.

    Hierarchy:
        obj1 (root)
        +-- obj2
        |   +-- obj4
        |   +-- obj5
        +-- obj3

    Returns a dict with all objects.
    """
    from topdownrbac.models import RestrictedObject

    obj1 = RestrictedObject.objects.create()
    obj2 = RestrictedObject.objects.create(parent=obj1)
    obj3 = RestrictedObject.objects.create(parent=obj1)
    obj4 = RestrictedObject.objects.create(parent=obj2)
    obj5 = RestrictedObject.objects.create(parent=obj2)
    return {
        "obj1": obj1,
        "obj2": obj2,
        "obj3": obj3,
        "obj4": obj4,
        "obj5": obj5,
        "all_objects": [obj1, obj2, obj3, obj4, obj5],
        "obj2_subtree": [obj2, obj4, obj5],
    }


# ---------------------------------------------------------------------------
# User group fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def user3(db):
    from topdownrbac.models import UserSubject

    return UserSubject.objects.create_user(username="user3", password="pass")


@pytest.fixture
def group1(user1, user2):
    from topdownrbac.models import UserGroupSubject

    group = UserGroupSubject.objects.create()
    group.add_user(user1)
    group.add_user(user2)
    return group


@pytest.fixture
def group2(user3):
    from topdownrbac.models import UserGroupSubject

    group = UserGroupSubject.objects.create()
    group.add_user(user3)
    return group


# ---------------------------------------------------------------------------
# Specialised role fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def editable_role(db):
    from topdownrbac.models import Role

    return Role.objects.create(name="editable")


@pytest.fixture
def immutable_role(db):
    from topdownrbac.models import Role

    return Role.objects.create(name="immutable", editable=False)


# ---------------------------------------------------------------------------
# DualInheritanceModel permission fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
def dual_perm_view(db):
    from django.contrib.auth.models import Permission

    return Permission.objects.get(codename="view_dualinheritancemodel")


@pytest.fixture
def dual_perm_change(db):
    from django.contrib.auth.models import Permission

    return Permission.objects.get(codename="change_dualinheritancemodel")


@pytest.fixture
def dual_role_viewer(dual_perm_view):
    from topdownrbac.models import Role

    role = Role.objects.create(name="viewer")
    role.add_permission(dual_perm_view)
    return role


# ---------------------------------------------------------------------------
# Extended hierarchy fixture (6 nodes) for consistency tests
# ---------------------------------------------------------------------------


@pytest.fixture
def consistency_hierarchy(db):
    """
    Create a 6-node hierarchical RestrictedObject structure.

    Hierarchy:
        obj1 (root)
        +-- obj2
        |   +-- obj4
        |   +-- obj5
        +-- obj3
            +-- obj6

    Returns a dict with all objects.
    """
    from topdownrbac.models import RestrictedObject

    obj1 = RestrictedObject.objects.create()
    obj2 = RestrictedObject.objects.create(parent=obj1)
    obj3 = RestrictedObject.objects.create(parent=obj1)
    obj4 = RestrictedObject.objects.create(parent=obj2)
    obj5 = RestrictedObject.objects.create(parent=obj2)
    obj6 = RestrictedObject.objects.create(parent=obj3)
    return {
        "obj1": obj1,
        "obj2": obj2,
        "obj3": obj3,
        "obj4": obj4,
        "obj5": obj5,
        "obj6": obj6,
        "all_objects": [obj1, obj2, obj3, obj4, obj5, obj6],
    }
