from uuid import uuid4

import pytest
from app.authorization import authorize, permissions_for
from app.errors import AppError
from app.models import (
    Base,
    MembershipRole,
    Permission,
    Role,
    RolePermission,
    User,
    Workspace,
    WorkspaceMembership,
)
from sqlalchemy import text
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine


@pytest.mark.asyncio
async def test_permissions_are_resolved_only_through_matching_workspace_membership():
    engine = create_async_engine("sqlite+aiosqlite:///:memory:")
    factory = async_sessionmaker(engine, expire_on_commit=False)
    async with engine.begin() as connection:
        await connection.execute(text("PRAGMA foreign_keys=ON"))
        await connection.run_sync(Base.metadata.create_all)

    workspace, other_workspace, user = Workspace(id=uuid4(), name="One"), Workspace(
        id=uuid4(), name="Two"), User(id=uuid4(), email="owner@example.com",
                                      password_hash="not-used", display_name="Owner")
    membership = WorkspaceMembership(id=uuid4(), workspace_id=workspace.id,
                                     user_id=user.id, status="active")
    role = Role(id=uuid4(), workspace_id=workspace.id, name="Owner", is_system=True)
    async with factory() as session:
        session.add_all([
            workspace, other_workspace, user,
        ])
        await session.commit()
        session.add_all([
            membership, role, Permission(key="audit.read", description="Read audit"),
        ])
        await session.commit()
        session.add_all([
            RolePermission(workspace_id=workspace.id, role_id=role.id,
                           permission_key="audit.read"),
            MembershipRole(workspace_id=workspace.id, membership_id=membership.id,
                           role_id=role.id),
        ])
        await session.commit()

        assert await permissions_for(session, workspace_id=workspace.id,
                                     user_id=user.id) == {"audit.read"}
        assert await permissions_for(session, workspace_id=other_workspace.id,
                                     user_id=user.id) == set()
        with pytest.raises(AppError) as denied:
            await authorize(session, workspace_id=other_workspace.id,
                            user_id=user.id, permission="audit.read")
        assert denied.value.status_code == 403

        session.add(MembershipRole(workspace_id=other_workspace.id,
                                   membership_id=membership.id, role_id=role.id))
        with pytest.raises(IntegrityError):
            await session.commit()
        await session.rollback()
    await engine.dispose()
