#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
RBAC System Test Suite

Comprehensive tests for all RBAC functionality
"""

from Identitas.provider.api.init import init
from Identitas.provider.api.auth import register
from Identitas.provider.api.rbac import *
import os
import sys

# Test counters
tests_passed = 0
tests_failed = 0


def test_case(name):
    """Decorator for test cases"""
    def decorator(func):
        def wrapper():
            global tests_passed, tests_failed
            print(f"\n[TEST] {name}...", end=" ")
            try:
                func()
                print("✅ PASSED")
                tests_passed += 1
            except AssertionError as e:
                print(f"❌ FAILED: {e}")
                tests_failed += 1
            except Exception as e:
                print(f"❌ ERROR: {e}")
                tests_failed += 1
        return wrapper
    return decorator


# Initialize test database
debug_mode = os.getenv("DEBUG", "false").lower() == "true"
engine = init("data/rbac_test_suite.db", debug=debug_mode)

# Create test users
print("=" * 80)
print("RBAC SYSTEM TEST SUITE")
print("=" * 80)

print("\n[SETUP] Creating test users...")
user1_token = register(engine, "testuser1", "TestPass123!@", "test1@example.com")
user2_token = register(engine, "testuser2", "TestPass123!@", "test2@example.com")
user3_token = register(engine, "testuser3", "TestPass123!@", "test3@example.com")

# Get user IDs (assuming first registrations start at ID 1)
user1_id = 1
user2_id = 2
user3_id = 3
print(f"✅ Created test users: user1 (ID: {user1_id}), user2 (ID: {user2_id}), user3 (ID: {user3_id})")

# ============================================================
# ROLE TESTS
# ============================================================

@test_case("Create Role")
def test_create_role():
    role = create_role(engine, "TestRole", "A test role")
    assert role is not None, "Role creation failed"
    assert role.name == "TestRole", "Role name mismatch"
    assert role.description == "A test role", "Role description mismatch"


@test_case("Get Role by ID")
def test_get_role():
    role = create_role(engine, "GetTestRole", "Test")
    retrieved_role = get_role(engine, role.id)
    assert retrieved_role is not None, "Failed to retrieve role"
    assert retrieved_role.id == role.id, "Role ID mismatch"


@test_case("Get Role by Name")
def test_get_role_by_name():
    role = create_role(engine, "NameTestRole", "Test")
    retrieved_role = get_role_by_name(engine, "NameTestRole")
    assert retrieved_role is not None, "Failed to retrieve role by name"
    assert retrieved_role.name == "NameTestRole", "Role name mismatch"


@test_case("List Roles")
def test_list_roles():
    initial_count = len(list_roles(engine))
    create_role(engine, "ListTestRole1", "Test")
    create_role(engine, "ListTestRole2", "Test")
    final_count = len(list_roles(engine))
    assert final_count > initial_count, "Role list not updated"


@test_case("Update Role")
def test_update_role():
    role = create_role(engine, "UpdateTestRole", "Original")
    updated_role = update_role(engine, role.id, name="UpdatedRole", description="Updated")
    assert updated_role is not None, "Role update failed"
    assert updated_role.name == "UpdatedRole", "Role name not updated"
    assert updated_role.description == "Updated", "Role description not updated"


@test_case("Delete Role")
def test_delete_role():
    role = create_role(engine, "DeleteTestRole", "Test")
    role_id = role.id
    success = delete_role(engine, role_id)
    assert success, "Role deletion failed"
    assert get_role(engine, role_id) is None, "Role still exists after deletion"


@test_case("Duplicate Role Creation Fails")
def test_duplicate_role():
    role = create_role(engine, "DuplicateRole", "Test")
    duplicate = create_role(engine, "DuplicateRole", "Test")
    assert duplicate is None, "Duplicate role was created"


# ============================================================
# RIGHT TESTS
# ============================================================

@test_case("Create Right")
def test_create_right():
    right = create_right(engine, "test_right", "resource", "action", "Test right")
    assert right is not None, "Right creation failed"
    assert right.name == "test_right", "Right name mismatch"
    assert right.resource == "resource", "Right resource mismatch"
    assert right.action == "action", "Right action mismatch"


@test_case("Get Right by ID")
def test_get_right():
    right = create_right(engine, "get_right_test", "res", "act")
    retrieved_right = get_right(engine, right.id)
    assert retrieved_right is not None, "Failed to retrieve right"
    assert retrieved_right.id == right.id, "Right ID mismatch"


@test_case("Get Right by Name")
def test_get_right_by_name():
    right = create_right(engine, "name_right_test", "res", "act")
    retrieved_right = get_right_by_name(engine, "name_right_test")
    assert retrieved_right is not None, "Failed to retrieve right by name"
    assert retrieved_right.name == "name_right_test", "Right name mismatch"


@test_case("List Rights")
def test_list_rights():
    initial_count = len(list_rights(engine))
    create_right(engine, "list_right_1", "res", "act")
    create_right(engine, "list_right_2", "res", "act")
    final_count = len(list_rights(engine))
    assert final_count > initial_count, "Right list not updated"


@test_case("List Rights by Resource")
def test_list_rights_by_resource():
    create_right(engine, "res_test_1", "testres", "act1")
    create_right(engine, "res_test_2", "testres", "act2")
    rights = list_rights_by_resource(engine, "testres")
    assert len(rights) >= 2, "Failed to retrieve rights by resource"


@test_case("Update Right")
def test_update_right():
    right = create_right(engine, "update_right_test", "res", "act", "Original")
    updated_right = update_right(engine, right.id, name="updated_right_test", description="Updated")
    assert updated_right is not None, "Right update failed"
    assert updated_right.name == "updated_right_test", "Right name not updated"


@test_case("Delete Right")
def test_delete_right():
    right = create_right(engine, "delete_right_test", "res", "act")
    right_id = right.id
    success = delete_right(engine, right_id)
    assert success, "Right deletion failed"
    assert get_right(engine, right_id) is None, "Right still exists after deletion"


@test_case("Duplicate Right Creation Fails")
def test_duplicate_right():
    right = create_right(engine, "dup_right", "res", "act")
    duplicate = create_right(engine, "dup_right", "res", "act")
    assert duplicate is None, "Duplicate right was created"


# ============================================================
# USER-ROLE ASSIGNMENT TESTS
# ============================================================

@test_case("Assign Role to User")
def test_assign_role_to_user():
    role = create_role(engine, "AssignTestRole", "Test")
    success = assign_role_to_user(engine, user1_id, role.id)
    assert success, "Failed to assign role to user"


@test_case("Get User Roles")
def test_get_user_roles():
    role = create_role(engine, "GetUserRoleTest", "Test")
    assign_role_to_user(engine, user2_id, role.id)
    roles = get_user_roles(engine, user2_id)
    assert len(roles) > 0, "No roles found for user"
    assert any(r.id == role.id for r in roles), "Assigned role not found"


@test_case("Remove Role from User")
def test_remove_role_from_user():
    role = create_role(engine, "RemoveRoleTest", "Test")
    assign_role_to_user(engine, user1_id, role.id)
    success = remove_role_from_user(engine, user1_id, role.id)
    assert success, "Failed to remove role from user"
    roles = get_user_roles(engine, user1_id)
    assert not any(r.id == role.id for r in roles), "Role still assigned after removal"


@test_case("Get Users with Role")
def test_get_users_with_role():
    role = create_role(engine, "GetUsersTest", "Test")
    assign_role_to_user(engine, user1_id, role.id)
    assign_role_to_user(engine, user2_id, role.id)
    users = get_users_with_role(engine, role.id)
    assert len(users) >= 2, "Not all users found with role"


@test_case("Cannot Assign Same Role Twice")
def test_duplicate_role_assignment():
    role = create_role(engine, "DupAssignTest", "Test")
    assign_role_to_user(engine, user3_id, role.id)
    success = assign_role_to_user(engine, user3_id, role.id)
    assert not success, "Same role was assigned twice"


# ============================================================
# ROLE-RIGHT ASSIGNMENT TESTS
# ============================================================

@test_case("Assign Right to Role")
def test_assign_right_to_role():
    role = create_role(engine, "RightRoleTest", "Test")
    right = create_right(engine, "test_right_assign", "res", "act")
    success = assign_right_to_role(engine, role.id, right.id)
    assert success, "Failed to assign right to role"


@test_case("Get Role Rights")
def test_get_role_rights():
    role = create_role(engine, "GetRoleRightTest", "Test")
    right = create_right(engine, "role_right_1", "res", "act")
    assign_right_to_role(engine, role.id, right.id)
    rights = get_role_rights(engine, role.id)
    assert len(rights) > 0, "No rights found for role"
    assert any(r.id == right.id for r in rights), "Assigned right not found"


@test_case("Remove Right from Role")
def test_remove_right_from_role():
    role = create_role(engine, "RemoveRightTest", "Test")
    right = create_right(engine, "remove_right_test", "res", "act")
    assign_right_to_role(engine, role.id, right.id)
    success = remove_right_from_role(engine, role.id, right.id)
    assert success, "Failed to remove right from role"
    rights = get_role_rights(engine, role.id)
    assert not any(r.id == right.id for r in rights), "Right still assigned after removal"


@test_case("Get Roles with Right")
def test_get_roles_with_right():
    role1 = create_role(engine, "RoleWithRightTest1", "Test")
    role2 = create_role(engine, "RoleWithRightTest2", "Test")
    right = create_right(engine, "right_in_roles", "res", "act")
    assign_right_to_role(engine, role1.id, right.id)
    assign_right_to_role(engine, role2.id, right.id)
    roles = get_roles_with_right(engine, right.id)
    assert len(roles) >= 2, "Not all roles found with right"


@test_case("Cannot Assign Same Right Twice")
def test_duplicate_right_assignment():
    role = create_role(engine, "DupRightAssignTest", "Test")
    right = create_right(engine, "dup_assign_right", "res", "act")
    assign_right_to_role(engine, role.id, right.id)
    success = assign_right_to_role(engine, role.id, right.id)
    assert not success, "Same right was assigned twice"


# ============================================================
# PERMISSION CHECKING TESTS
# ============================================================

@test_case("User Has Right")
def test_user_has_right():
    role = create_role(engine, "PermCheckRole", "Test")
    right = create_right(engine, "perm_check_right", "res", "act")
    assign_role_to_user(engine, user1_id, role.id)
    assign_right_to_role(engine, role.id, right.id)
    assert user_has_right(engine, user1_id, "perm_check_right"), "User should have right"


@test_case("User Missing Right")
def test_user_missing_right():
    right = create_right(engine, "missing_right_test", "res", "act")
    assert not user_has_right(engine, user2_id, "missing_right_test"), "User should not have right"


@test_case("User Has Resource:Action Permission")
def test_user_has_resource_action():
    role = create_role(engine, "ResourceActionRole", "Test")
    right = create_right(engine, "res_act_right", "document", "delete")
    assign_role_to_user(engine, user3_id, role.id)
    assign_right_to_role(engine, role.id, right.id)
    assert user_has_resource_action(engine, user3_id, "document", "delete"), "User should have resource:action"


@test_case("User Missing Resource:Action Permission")
def test_user_missing_resource_action():
    assert not user_has_resource_action(engine, user1_id, "nonexistent", "nonexistent"), "User should not have permission"


@test_case("Get User All Rights")
def test_get_user_all_rights():
    role = create_role(engine, "AllRightsRole", "Test")
    right1 = create_right(engine, "all_right_1", "res", "act1")
    right2 = create_right(engine, "all_right_2", "res", "act2")
    assign_role_to_user(engine, user1_id, role.id)
    assign_right_to_role(engine, role.id, right1.id)
    assign_right_to_role(engine, role.id, right2.id)
    rights = get_user_all_rights(engine, user1_id)
    assert len(rights) >= 2, "Not all user rights retrieved"


@test_case("Get User Permissions Summary")
def test_get_user_permissions_summary():
    role = create_role(engine, "PermSummaryRole", "Test")
    right = create_right(engine, "summary_right", "testresource", "testaction")
    assign_role_to_user(engine, user2_id, role.id)
    assign_right_to_role(engine, role.id, right.id)
    perms = get_user_permissions_summary(engine, user2_id)
    assert "testresource" in perms, "Resource not in permissions summary"
    assert "testaction" in perms["testresource"], "Action not in resource permissions"


@test_case("User with Multiple Roles Has Combined Rights")
def test_user_multiple_roles():
    role1 = create_role(engine, "MultiRole1", "Test")
    role2 = create_role(engine, "MultiRole2", "Test")
    right1 = create_right(engine, "multi_right_1", "res", "act1")
    right2 = create_right(engine, "multi_right_2", "res", "act2")
    
    assign_role_to_user(engine, user3_id, role1.id)
    assign_role_to_user(engine, user3_id, role2.id)
    assign_right_to_role(engine, role1.id, right1.id)
    assign_right_to_role(engine, role2.id, right2.id)
    
    assert user_has_right(engine, user3_id, "multi_right_1"), "Should have first right"
    assert user_has_right(engine, user3_id, "multi_right_2"), "Should have second right"


# ============================================================
# Run All Tests
# ============================================================

if __name__ == "__main__":
    # Role tests
    test_create_role()
    test_get_role()
    test_get_role_by_name()
    test_list_roles()
    test_update_role()
    test_delete_role()
    test_duplicate_role()
    
    # Right tests
    test_create_right()
    test_get_right()
    test_get_right_by_name()
    test_list_rights()
    test_list_rights_by_resource()
    test_update_right()
    test_delete_right()
    test_duplicate_right()
    
    # User-Role assignment tests
    test_assign_role_to_user()
    test_get_user_roles()
    test_remove_role_from_user()
    test_get_users_with_role()
    test_duplicate_role_assignment()
    
    # Role-Right assignment tests
    test_assign_right_to_role()
    test_get_role_rights()
    test_remove_right_from_role()
    test_get_roles_with_right()
    test_duplicate_right_assignment()
    
    # Permission checking tests
    test_user_has_right()
    test_user_missing_right()
    test_user_has_resource_action()
    test_user_missing_resource_action()
    test_get_user_all_rights()
    test_get_user_permissions_summary()
    test_user_multiple_roles()
    
    # Print results
    print("\n" + "=" * 80)
    print(f"TEST RESULTS: {tests_passed} passed, {tests_failed} failed")
    print("=" * 80)
    
    sys.exit(0 if tests_failed == 0 else 1)
