# tests/api/test_admin.py
"""
Comprehensive tests for admin endpoints.
"""

from datetime import UTC, datetime, timedelta
from uuid import uuid4

import pytest
import pytest_asyncio
from fastapi import status

from app.models.organization import Organization
from app.models.user_organization import OrganizationRole, UserOrganization
from app.models.user_session import UserSession


@pytest_asyncio.fixture
async def superuser_token(client, async_test_superuser):
    """Get access token for superuser."""
    response = await client.post(
        "/api/v1/auth/login",
        json={"email": "superuser@example.com", "password": "SuperPassword123!"},
    )
    assert response.status_code == 200, f"Login failed: {response.json()}"
    return response.json()["access_token"]


# ===== USER MANAGEMENT TESTS =====


class TestAdminListUsers:
    """Tests for GET /admin/users endpoint."""

    @pytest.mark.asyncio
    async def test_admin_list_users_success(self, client, superuser_token):
        """Test successfully listing users as admin."""
        response = await client.get(
            "/api/v1/admin/users",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert "data" in data
        assert "pagination" in data
        assert isinstance(data["data"], list)

    @pytest.mark.asyncio
    async def test_admin_list_users_with_filters(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test listing users with filters."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create inactive user
        async with AsyncTestingSessionLocal() as session:
            from app.core.auth import get_password_hash
            from app.models.user import User

            inactive_user = User(
                email="inactive@example.com",
                password_hash=get_password_hash("TestPassword123!"),
                first_name="Inactive",
                last_name="User",
                is_active=False,
            )
            session.add(inactive_user)
            await session.commit()

        response = await client.get(
            "/api/v1/admin/users?is_active=false",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert len(data["data"]) >= 1

    @pytest.mark.asyncio
    async def test_admin_list_users_with_search(
        self, client, async_test_superuser, superuser_token
    ):
        """Test searching users."""
        response = await client.get(
            "/api/v1/admin/users?search=superuser",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert "data" in data

    @pytest.mark.asyncio
    async def test_admin_list_users_unauthorized(self, client, async_test_user):
        """Test non-admin cannot list users."""
        # Login as regular user
        login_response = await client.post(
            "/api/v1/auth/login",
            json={"email": async_test_user.email, "password": "TestPassword123!"},
        )
        token = login_response.json()["access_token"]

        response = await client.get(
            "/api/v1/admin/users", headers={"Authorization": f"Bearer {token}"}
        )

        assert response.status_code == status.HTTP_403_FORBIDDEN


class TestAdminCreateUser:
    """Tests for POST /admin/users endpoint."""

    @pytest.mark.asyncio
    async def test_admin_create_user_success(
        self, client, async_test_superuser, superuser_token
    ):
        """Test successfully creating a user as admin."""
        response = await client.post(
            "/api/v1/admin/users",
            json={
                "email": "newadminuser@example.com",
                "password": "SecurePassword123!",
                "first_name": "New",
                "last_name": "User",
            },
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_201_CREATED
        data = response.json()
        assert data["email"] == "newadminuser@example.com"

    @pytest.mark.asyncio
    async def test_admin_create_user_duplicate_email(
        self, client, async_test_superuser, async_test_user, superuser_token
    ):
        """Test creating user with duplicate email fails."""
        response = await client.post(
            "/api/v1/admin/users",
            json={
                "email": async_test_user.email,
                "password": "SecurePassword123!",
                "first_name": "Duplicate",
                "last_name": "User",
            },
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminGetUser:
    """Tests for GET /admin/users/{user_id} endpoint."""

    @pytest.mark.asyncio
    async def test_admin_get_user_success(
        self, client, async_test_superuser, async_test_user, superuser_token
    ):
        """Test successfully getting user details."""
        response = await client.get(
            f"/api/v1/admin/users/{async_test_user.id}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["id"] == str(async_test_user.id)
        assert data["email"] == async_test_user.email

    @pytest.mark.asyncio
    async def test_admin_get_user_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test getting non-existent user."""
        response = await client.get(
            f"/api/v1/admin/users/{uuid4()}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminUpdateUser:
    """Tests for PUT /admin/users/{user_id} endpoint."""

    @pytest.mark.asyncio
    async def test_admin_update_user_success(
        self, client, async_test_superuser, async_test_user, superuser_token
    ):
        """Test successfully updating a user."""
        response = await client.put(
            f"/api/v1/admin/users/{async_test_user.id}",
            json={"first_name": "Updated"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["first_name"] == "Updated"

    @pytest.mark.asyncio
    async def test_admin_update_user_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test updating non-existent user."""
        response = await client.put(
            f"/api/v1/admin/users/{uuid4()}",
            json={"first_name": "Updated"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminDeleteUser:
    """Tests for DELETE /admin/users/{user_id} endpoint."""

    @pytest.mark.asyncio
    async def test_admin_delete_user_success(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test successfully deleting a user."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create user to delete
        async with AsyncTestingSessionLocal() as session:
            from app.core.auth import get_password_hash
            from app.models.user import User

            user_to_delete = User(
                email="todelete@example.com",
                password_hash=get_password_hash("TestPassword123!"),
                first_name="To",
                last_name="Delete",
            )
            session.add(user_to_delete)
            await session.commit()
            user_id = user_to_delete.id

        response = await client.delete(
            f"/api/v1/admin/users/{user_id}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["success"] is True

    @pytest.mark.asyncio
    async def test_admin_delete_user_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test deleting non-existent user."""
        response = await client.delete(
            f"/api/v1/admin/users/{uuid4()}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_admin_delete_self_forbidden(
        self, client, async_test_superuser, superuser_token
    ):
        """Test admin cannot delete their own account."""
        response = await client.delete(
            f"/api/v1/admin/users/{async_test_superuser.id}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_403_FORBIDDEN


class TestAdminActivateUser:
    """Tests for POST /admin/users/{user_id}/activate endpoint."""

    @pytest.mark.asyncio
    async def test_admin_activate_user_success(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test successfully activating a user."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create inactive user
        async with AsyncTestingSessionLocal() as session:
            from app.core.auth import get_password_hash
            from app.models.user import User

            inactive_user = User(
                email="toactivate@example.com",
                password_hash=get_password_hash("TestPassword123!"),
                first_name="To",
                last_name="Activate",
                is_active=False,
            )
            session.add(inactive_user)
            await session.commit()
            user_id = inactive_user.id

        response = await client.post(
            f"/api/v1/admin/users/{user_id}/activate",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["success"] is True

    @pytest.mark.asyncio
    async def test_admin_activate_user_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test activating non-existent user."""
        response = await client.post(
            f"/api/v1/admin/users/{uuid4()}/activate",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminDeactivateUser:
    """Tests for POST /admin/users/{user_id}/deactivate endpoint."""

    @pytest.mark.asyncio
    async def test_admin_deactivate_user_success(
        self, client, async_test_superuser, async_test_user, superuser_token
    ):
        """Test successfully deactivating a user."""
        response = await client.post(
            f"/api/v1/admin/users/{async_test_user.id}/deactivate",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["success"] is True

    @pytest.mark.asyncio
    async def test_admin_deactivate_user_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test deactivating non-existent user."""
        response = await client.post(
            f"/api/v1/admin/users/{uuid4()}/deactivate",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_admin_deactivate_self_forbidden(
        self, client, async_test_superuser, superuser_token
    ):
        """Test admin cannot deactivate their own account."""
        response = await client.post(
            f"/api/v1/admin/users/{async_test_superuser.id}/deactivate",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_403_FORBIDDEN


class TestAdminBulkUserAction:
    """Tests for POST /admin/users/bulk-action endpoint."""

    @pytest.mark.asyncio
    async def test_admin_bulk_activate_users(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test bulk activating users."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create inactive users
        user_ids = []
        async with AsyncTestingSessionLocal() as session:
            from app.core.auth import get_password_hash
            from app.models.user import User

            for i in range(3):
                user = User(
                    email=f"bulk{i}@example.com",
                    password_hash=get_password_hash("TestPassword123!"),
                    first_name=f"Bulk{i}",
                    last_name="User",
                    is_active=False,
                )
                session.add(user)
                await session.flush()
                user_ids.append(str(user.id))
            await session.commit()

        response = await client.post(
            "/api/v1/admin/users/bulk-action",
            json={"action": "activate", "user_ids": user_ids},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["affected_count"] == 3

    @pytest.mark.asyncio
    async def test_admin_bulk_deactivate_users(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test bulk deactivating users."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create active users
        user_ids = []
        async with AsyncTestingSessionLocal() as session:
            from app.core.auth import get_password_hash
            from app.models.user import User

            for i in range(2):
                user = User(
                    email=f"deactivate{i}@example.com",
                    password_hash=get_password_hash("TestPassword123!"),
                    first_name=f"Deactivate{i}",
                    last_name="User",
                    is_active=True,
                )
                session.add(user)
                await session.flush()
                user_ids.append(str(user.id))
            await session.commit()

        response = await client.post(
            "/api/v1/admin/users/bulk-action",
            json={"action": "deactivate", "user_ids": user_ids},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["affected_count"] == 2

    @pytest.mark.asyncio
    async def test_admin_bulk_delete_users(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test bulk deleting users."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create users to delete
        user_ids = []
        async with AsyncTestingSessionLocal() as session:
            from app.core.auth import get_password_hash
            from app.models.user import User

            for i in range(2):
                user = User(
                    email=f"bulkdelete{i}@example.com",
                    password_hash=get_password_hash("TestPassword123!"),
                    first_name=f"BulkDelete{i}",
                    last_name="User",
                )
                session.add(user)
                await session.flush()
                user_ids.append(str(user.id))
            await session.commit()

        response = await client.post(
            "/api/v1/admin/users/bulk-action",
            json={"action": "delete", "user_ids": user_ids},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["affected_count"] >= 0


# ===== ORGANIZATION MANAGEMENT TESTS =====


class TestAdminListOrganizations:
    """Tests for GET /admin/organizations endpoint."""

    @pytest.mark.asyncio
    async def test_admin_list_organizations_success(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test successfully listing organizations."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create test organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Test Org", slug="test-org")
            session.add(org)
            await session.commit()

        response = await client.get(
            "/api/v1/admin/organizations",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert "data" in data
        assert "pagination" in data

    @pytest.mark.asyncio
    async def test_admin_list_organizations_with_search(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test searching organizations."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create test organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Searchable Org", slug="searchable-org")
            session.add(org)
            await session.commit()

        response = await client.get(
            "/api/v1/admin/organizations?search=Searchable",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK


class TestAdminCreateOrganization:
    """Tests for POST /admin/organizations endpoint."""

    @pytest.mark.asyncio
    async def test_admin_create_organization_success(
        self, client, async_test_superuser, superuser_token
    ):
        """Test successfully creating an organization."""
        response = await client.post(
            "/api/v1/admin/organizations",
            json={
                "name": "New Admin Org",
                "slug": "new-admin-org",
                "description": "Created by admin",
            },
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_201_CREATED
        data = response.json()
        assert data["name"] == "New Admin Org"
        assert data["member_count"] == 0

    @pytest.mark.asyncio
    async def test_admin_create_organization_duplicate_slug(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test creating organization with duplicate slug fails."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create existing organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Existing", slug="duplicate-slug")
            session.add(org)
            await session.commit()

        response = await client.post(
            "/api/v1/admin/organizations",
            json={"name": "Duplicate", "slug": "duplicate-slug"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminGetOrganization:
    """Tests for GET /admin/organizations/{org_id} endpoint."""

    @pytest.mark.asyncio
    async def test_admin_get_organization_success(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test successfully getting organization details."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create test organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Get Test Org", slug="get-test-org")
            session.add(org)
            await session.commit()
            org_id = org.id

        response = await client.get(
            f"/api/v1/admin/organizations/{org_id}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["name"] == "Get Test Org"

    @pytest.mark.asyncio
    async def test_admin_get_organization_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test getting non-existent organization."""
        response = await client.get(
            f"/api/v1/admin/organizations/{uuid4()}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminUpdateOrganization:
    """Tests for PUT /admin/organizations/{org_id} endpoint."""

    @pytest.mark.asyncio
    async def test_admin_update_organization_success(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test successfully updating an organization."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create test organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Update Test", slug="update-test")
            session.add(org)
            await session.commit()
            org_id = org.id

        response = await client.put(
            f"/api/v1/admin/organizations/{org_id}",
            json={"name": "Updated Name"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["name"] == "Updated Name"

    @pytest.mark.asyncio
    async def test_admin_update_organization_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test updating non-existent organization."""
        response = await client.put(
            f"/api/v1/admin/organizations/{uuid4()}",
            json={"name": "Updated"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminDeleteOrganization:
    """Tests for DELETE /admin/organizations/{org_id} endpoint."""

    @pytest.mark.asyncio
    async def test_admin_delete_organization_success(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test successfully deleting an organization."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create test organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Delete Test", slug="delete-test")
            session.add(org)
            await session.commit()
            org_id = org.id

        response = await client.delete(
            f"/api/v1/admin/organizations/{org_id}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["success"] is True

    @pytest.mark.asyncio
    async def test_admin_delete_organization_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test deleting non-existent organization."""
        response = await client.delete(
            f"/api/v1/admin/organizations/{uuid4()}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminListOrganizationMembers:
    """Tests for GET /admin/organizations/{org_id}/members endpoint."""

    @pytest.mark.asyncio
    async def test_admin_list_organization_members_success(
        self,
        client,
        async_test_superuser,
        async_test_db,
        async_test_user,
        superuser_token,
    ):
        """Test successfully listing organization members."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create test organization with member
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Members Test", slug="members-test")
            session.add(org)
            await session.commit()

            user_org = UserOrganization(
                user_id=async_test_user.id,
                organization_id=org.id,
                role=OrganizationRole.MEMBER,
                is_active=True,
            )
            session.add(user_org)
            await session.commit()
            org_id = org.id

        response = await client.get(
            f"/api/v1/admin/organizations/{org_id}/members",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert "data" in data
        assert len(data["data"]) >= 1

    @pytest.mark.asyncio
    async def test_admin_list_organization_members_not_found(
        self, client, async_test_superuser, superuser_token
    ):
        """Test listing members of non-existent organization."""
        response = await client.get(
            f"/api/v1/admin/organizations/{uuid4()}/members",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminAddOrganizationMember:
    """Tests for POST /admin/organizations/{org_id}/members endpoint."""

    @pytest.mark.asyncio
    async def test_admin_add_organization_member_success(
        self,
        client,
        async_test_superuser,
        async_test_db,
        async_test_user,
        superuser_token,
    ):
        """Test successfully adding a member to organization."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create test organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Add Member Test", slug="add-member-test")
            session.add(org)
            await session.commit()
            org_id = org.id

        response = await client.post(
            f"/api/v1/admin/organizations/{org_id}/members",
            json={"user_id": str(async_test_user.id), "role": "member"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["success"] is True

    @pytest.mark.asyncio
    async def test_admin_add_organization_member_already_exists(
        self,
        client,
        async_test_superuser,
        async_test_db,
        async_test_user,
        superuser_token,
    ):
        """Test adding member who is already a member."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create organization with existing member
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Existing Member", slug="existing-member")
            session.add(org)
            await session.commit()

            user_org = UserOrganization(
                user_id=async_test_user.id,
                organization_id=org.id,
                role=OrganizationRole.MEMBER,
                is_active=True,
            )
            session.add(user_org)
            await session.commit()
            org_id = org.id

        response = await client.post(
            f"/api/v1/admin/organizations/{org_id}/members",
            json={"user_id": str(async_test_user.id), "role": "member"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_409_CONFLICT

    @pytest.mark.asyncio
    async def test_admin_add_organization_member_org_not_found(
        self, client, async_test_superuser, async_test_user, superuser_token
    ):
        """Test adding member to non-existent organization."""
        response = await client.post(
            f"/api/v1/admin/organizations/{uuid4()}/members",
            json={"user_id": str(async_test_user.id), "role": "member"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_admin_add_organization_member_user_not_found(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test adding non-existent user to organization."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create test organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="User Not Found", slug="user-not-found")
            session.add(org)
            await session.commit()
            org_id = org.id

        response = await client.post(
            f"/api/v1/admin/organizations/{org_id}/members",
            json={"user_id": str(uuid4()), "role": "member"},
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


class TestAdminRemoveOrganizationMember:
    """Tests for DELETE /admin/organizations/{org_id}/members/{user_id} endpoint."""

    @pytest.mark.asyncio
    async def test_admin_remove_organization_member_success(
        self,
        client,
        async_test_superuser,
        async_test_db,
        async_test_user,
        superuser_token,
    ):
        """Test successfully removing a member from organization."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create organization with member
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="Remove Member", slug="remove-member")
            session.add(org)
            await session.commit()

            user_org = UserOrganization(
                user_id=async_test_user.id,
                organization_id=org.id,
                role=OrganizationRole.MEMBER,
                is_active=True,
            )
            session.add(user_org)
            await session.commit()
            org_id = org.id

        response = await client.delete(
            f"/api/v1/admin/organizations/{org_id}/members/{async_test_user.id}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert data["success"] is True

    @pytest.mark.asyncio
    async def test_admin_remove_organization_member_not_member(
        self,
        client,
        async_test_superuser,
        async_test_db,
        async_test_user,
        superuser_token,
    ):
        """Test removing user who is not a member."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create organization without member
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="No Member", slug="no-member")
            session.add(org)
            await session.commit()
            org_id = org.id

        response = await client.delete(
            f"/api/v1/admin/organizations/{org_id}/members/{async_test_user.id}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_admin_remove_organization_member_org_not_found(
        self, client, async_test_superuser, async_test_user, superuser_token
    ):
        """Test removing member from non-existent organization."""
        response = await client.delete(
            f"/api/v1/admin/organizations/{uuid4()}/members/{async_test_user.id}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND

    @pytest.mark.asyncio
    async def test_admin_remove_organization_member_user_not_found(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test removing non-existent user from organization."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create organization
        async with AsyncTestingSessionLocal() as session:
            org = Organization(name="User Not Found Org", slug="user-not-found-org")
            session.add(org)
            await session.commit()
            org_id = org.id

        response = await client.delete(
            f"/api/v1/admin/organizations/{org_id}/members/{uuid4()}",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_404_NOT_FOUND


# ===== SESSION MANAGEMENT TESTS =====


class TestAdminListSessions:
    """Tests for admin sessions list endpoint."""

    @pytest.mark.asyncio
    async def test_admin_list_sessions_success(
        self,
        client,
        async_test_superuser,
        async_test_user,
        async_test_db,
        superuser_token,
    ):
        """Test listing all sessions as admin."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create some test sessions
        async with AsyncTestingSessionLocal() as session:
            now = datetime.now(UTC)
            expires_at = now + timedelta(days=7)

            session1 = UserSession(
                user_id=async_test_user.id,
                refresh_token_jti="jti-test-1",
                device_name="iPhone 14",
                device_id="device-1",
                ip_address="192.168.1.100",
                user_agent="Mozilla/5.0",
                last_used_at=now,
                expires_at=expires_at,
                is_active=True,
                location_city="San Francisco",
                location_country="United States",
            )
            session2 = UserSession(
                user_id=async_test_superuser.id,
                refresh_token_jti="jti-test-2",
                device_name="MacBook Pro",
                device_id="device-2",
                ip_address="192.168.1.101",
                user_agent="Mozilla/5.0",
                last_used_at=now,
                expires_at=expires_at,
                is_active=True,
            )
            session.add_all([session1, session2])
            await session.commit()

        response = await client.get(
            "/api/v1/admin/sessions?page=1&limit=10",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert "data" in data
        assert "pagination" in data
        assert len(data["data"]) >= 2  # At least our 2 test sessions
        assert data["pagination"]["total"] >= 2

        # Verify session structure includes user info
        first_session = data["data"][0]
        assert "id" in first_session
        assert "user_id" in first_session
        assert "user_email" in first_session
        assert "device_name" in first_session
        assert "ip_address" in first_session
        assert "is_active" in first_session

    @pytest.mark.asyncio
    async def test_admin_list_sessions_filter_active(
        self,
        client,
        async_test_superuser,
        async_test_user,
        async_test_db,
        superuser_token,
    ):
        """Test filtering sessions by active status."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create active and inactive sessions
        async with AsyncTestingSessionLocal() as session:
            now = datetime.now(UTC)
            expires_at = now + timedelta(days=7)

            active_session = UserSession(
                user_id=async_test_user.id,
                refresh_token_jti="jti-active",
                device_name="Active Device",
                ip_address="192.168.1.100",
                last_used_at=now,
                expires_at=expires_at,
                is_active=True,
            )
            inactive_session = UserSession(
                user_id=async_test_user.id,
                refresh_token_jti="jti-inactive",
                device_name="Inactive Device",
                ip_address="192.168.1.101",
                last_used_at=now,
                expires_at=expires_at,
                is_active=False,
            )
            session.add_all([active_session, inactive_session])
            await session.commit()

        # Get only active sessions (default)
        response = await client.get(
            "/api/v1/admin/sessions?page=1&limit=100",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()

        # All returned sessions should be active
        for sess in data["data"]:
            assert sess["is_active"] is True

    @pytest.mark.asyncio
    async def test_admin_list_sessions_pagination(
        self, client, async_test_superuser, async_test_db, superuser_token
    ):
        """Test pagination of sessions list."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create multiple sessions
        async with AsyncTestingSessionLocal() as session:
            now = datetime.now(UTC)
            expires_at = now + timedelta(days=7)

            sessions = []
            for i in range(5):
                sess = UserSession(
                    user_id=async_test_superuser.id,
                    refresh_token_jti=f"jti-pagination-{i}",
                    device_name=f"Device {i}",
                    ip_address=f"192.168.1.{100 + i}",
                    last_used_at=now,
                    expires_at=expires_at,
                    is_active=True,
                )
                sessions.append(sess)
            session.add_all(sessions)
            await session.commit()

        # Get first page with limit 2
        response = await client.get(
            "/api/v1/admin/sessions?page=1&limit=2",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()
        assert len(data["data"]) == 2
        assert data["pagination"]["page"] == 1
        assert data["pagination"]["page_size"] == 2
        assert data["pagination"]["total"] >= 5

    @pytest.mark.asyncio
    async def test_admin_list_sessions_unauthorized(
        self, client, async_test_user, user_token
    ):
        """Test that non-admin users cannot access admin sessions endpoint."""
        response = await client.get(
            "/api/v1/admin/sessions?page=1&limit=10",
            headers={"Authorization": f"Bearer {user_token}"},
        )

        assert response.status_code == status.HTTP_403_FORBIDDEN


# ===== ADMIN STATS TESTS =====


class TestAdminStats:
    """Tests for GET /admin/stats endpoint."""

    @pytest.mark.asyncio
    async def test_admin_get_stats_with_data(
        self,
        client,
        async_test_superuser,
        async_test_user,
        async_test_db,
        superuser_token,
    ):
        """Test getting admin stats with real data in database."""
        _test_engine, AsyncTestingSessionLocal = async_test_db

        # Create multiple users and organizations with members
        async with AsyncTestingSessionLocal() as session:
            from app.core.auth import get_password_hash
            from app.models.user import User

            # Create several users
            for i in range(5):
                user = User(
                    email=f"statsuser{i}@example.com",
                    password_hash=get_password_hash("TestPassword123!"),
                    first_name=f"Stats{i}",
                    last_name="User",
                    is_active=i % 2 == 0,  # Mix of active/inactive
                )
                session.add(user)
            await session.commit()

        # Create organizations with members
        async with AsyncTestingSessionLocal() as session:
            orgs = []
            for i in range(3):
                org = Organization(name=f"Stats Org {i}", slug=f"stats-org-{i}")
                session.add(org)
                orgs.append(org)
            await session.flush()

            # Add some members to organizations
            user_org = UserOrganization(
                user_id=async_test_user.id,
                organization_id=orgs[0].id,
                role=OrganizationRole.MEMBER,
                is_active=True,
            )
            session.add(user_org)
            await session.commit()

        response = await client.get(
            "/api/v1/admin/stats",
            headers={"Authorization": f"Bearer {superuser_token}"},
        )

        assert response.status_code == status.HTTP_200_OK
        data = response.json()

        # Verify response structure
        assert "user_growth" in data
        assert "organization_distribution" in data
        assert "registration_activity" in data
        assert "user_status" in data

        # Verify user_growth has 30 days of data
        assert len(data["user_growth"]) == 30
        for item in data["user_growth"]:
            assert "date" in item
            assert "total_users" in item
            assert "active_users" in item

        # Verify registration_activity has 14 days of data
        assert len(data["registration_activity"]) == 14
        for item in data["registration_activity"]:
            assert "date" in item
            assert "registrations" in item

        # Verify user_status has active/inactive counts
        assert len(data["user_status"]) == 2
        status_names = {item["name"] for item in data["user_status"]}
        assert status_names == {"Active", "Inactive"}

    @pytest.mark.asyncio
    async def test_admin_get_stats_unauthorized(
        self, client, async_test_user, user_token
    ):
        """Test that non-admin users cannot access stats endpoint."""
        response = await client.get(
            "/api/v1/admin/stats",
            headers={"Authorization": f"Bearer {user_token}"},
        )

        assert response.status_code == status.HTTP_403_FORBIDDEN
