"""Integration tests for app.api.v1.teams — team invitations and member management."""

import uuid

import pytest
import pytest_asyncio
from sqlalchemy import select

from app.models.user import Tenant, UserTenant, User
from app.models.team import Invitation, SharedFolder
from app.models.interaction import UserFolder


@pytest_asyncio.fixture
async def auth_ctx(client):
    """Register a user and return (headers, user_id)."""
    email = f"t{uuid.uuid4().hex[:6]}@test.cn"
    resp = await client.post("/api/v1/auth/register", json={
        "email": email, "password": "Test1234", "display_name": "TestUser",
    })
    assert resp.status_code == 200
    data = resp.json()
    headers = {"Authorization": f"Bearer {data['token']['access_token']}"}
    user_id = uuid.UUID(data["user"]["id"])
    return headers, user_id


@pytest_asyncio.fixture
async def seed_invitation(auth_ctx, db):
    """Create a pending invitation for a test user."""
    headers, uid = auth_ctx
    # Get tenant ID
    r = await db.execute(select(Tenant.id).join(UserTenant).where(UserTenant.user_id == uid))
    tenant_id = r.scalar()

    # Clean up and create
    from sqlalchemy import delete as sql_delete
    await db.execute(sql_delete(Invitation).where(Invitation.tenant_id == tenant_id))
    token = uuid.uuid4().hex
    from datetime import datetime, timedelta
    from app.compat import UTC
    inv = Invitation(
        id=uuid.uuid4(), tenant_id=tenant_id, inviter_id=uid,
        email="invited@test.cn", role="viewer", token=token,
        status="pending",
        expires_at=datetime.now(UTC) + timedelta(days=7),
    )
    db.add(inv)
    await db.commit()
    return headers, uid, inv


# ── Unauthorized ──


@pytest.mark.asyncio
async def test_teams_unauthorized(client):
    """Access without auth returns 401."""
    r = await client.get("/api/v1/teams/members")
    assert r.status_code == 401


# ── List Members ──


@pytest.mark.asyncio
async def test_list_members(client, auth_ctx):
    """List members returns the current user as a member."""
    headers, uid = auth_ctx
    r = await client.get("/api/v1/teams/members", headers=headers)
    assert r.status_code == 200
    data = r.json()
    assert data["total"] >= 1
    uids = {m["id"] for m in data["members"]}
    assert str(uid) in uids


# ── Create Invitation ──


@pytest.mark.asyncio
async def test_create_invitation(client, auth_ctx):
    """Create an invitation for a colleague."""
    headers, _ = auth_ctx
    r = await client.post("/api/v1/teams/invitations", headers=headers, json={
        "email": "colleague@hospital.cn", "role": "editor",
    })
    assert r.status_code == 200
    data = r.json()
    assert data["status"] == "sent"
    assert "token" in data
    assert "invite_link" in data


@pytest.mark.asyncio
async def test_create_invitation_default_role(client, auth_ctx):
    """Create invitation with default role (viewer)."""
    headers, _ = auth_ctx
    r = await client.post("/api/v1/teams/invitations", headers=headers, json={
        "email": "viewer@hospital.cn",
    })
    assert r.status_code == 200
    assert r.json()["status"] == "sent"


# ── List Invitations ──


@pytest.mark.asyncio
async def test_list_invitations_empty(client, auth_ctx):
    """List invitations returns empty when none created."""
    headers, _ = auth_ctx
    r = await client.get("/api/v1/teams/invitations", headers=headers)
    assert r.status_code == 200
    assert r.json()["items"] == []


@pytest.mark.asyncio
async def test_list_invitations_with_data(client, seed_invitation):
    """List invitations returns seeded invitations."""
    headers, _, inv = seed_invitation
    r = await client.get("/api/v1/teams/invitations", headers=headers)
    assert r.status_code == 200
    data = r.json()
    assert len(data["items"]) >= 1
    assert data["items"][0]["email"] == "invited@test.cn"
    assert data["items"][0]["status"] == "pending"


# ── Accept Invitation ──


@pytest.mark.asyncio
async def test_accept_invitation(client, seed_invitation, db):
    """Accept a valid invitation as a different user."""
    email2 = f"t{uuid.uuid4().hex[:6]}@test.cn"
    resp = await client.post("/api/v1/auth/register", json={
        "email": email2, "password": "Test1234", "display_name": "NewMember",
    })
    headers2 = {"Authorization": f"Bearer {resp.json()['token']['access_token']}"}

    _, _, inv = seed_invitation
    r = await client.post("/api/v1/teams/invitations/accept", headers=headers2, json={
        "token": inv.token,
    })
    assert r.status_code == 200
    data = r.json()
    assert data["status"] == "joined"

    # Verify invitation is accepted via direct DB read
    from app.models.team import Invitation
    await db.refresh(inv)
    assert inv.status == "accepted"


@pytest.mark.asyncio
async def test_accept_invitation_already_member(client, seed_invitation, db):
    """Accept invitation when already a member."""
    _, _, inv = seed_invitation

    email2 = f"t{uuid.uuid4().hex[:6]}@test.cn"
    resp = await client.post("/api/v1/auth/register", json={
        "email": email2, "password": "Test1234", "display_name": "NewMember",
    })
    headers2 = {"Authorization": f"Bearer {resp.json()['token']['access_token']}"}

    # First accept
    r = await client.post("/api/v1/teams/invitations/accept", headers=headers2, json={
        "token": inv.token,
    })
    assert r.status_code == 200
    assert r.json()["status"] == "joined"

    # Reset invitation status via direct SQL
    from app.models.team import Invitation
    from sqlalchemy import update
    await db.execute(update(Invitation).where(Invitation.token == inv.token).values(status="pending"))
    await db.commit()

    # Accept again — user is already a member
    r = await client.post("/api/v1/teams/invitations/accept", headers=headers2, json={
        "token": inv.token,
    })
    assert r.status_code == 200
    assert r.json()["status"] == "already_member"


@pytest.mark.asyncio
async def test_accept_invitation_not_found(client, auth_ctx):
    """Accept non-existent token returns 404."""
    headers, _ = auth_ctx
    r = await client.post("/api/v1/teams/invitations/accept", headers=headers, json={
        "token": "nonexistent-token",
    })
    assert r.status_code == 404


# ── Revoke Invitation ──


@pytest.mark.asyncio
async def test_revoke_invitation(client, seed_invitation):
    """Revoke a pending invitation."""
    headers, _, inv = seed_invitation
    r = await client.delete(f"/api/v1/teams/invitations/{inv.id}", headers=headers)
    assert r.status_code == 200
    assert r.json()["status"] == "revoked"


@pytest.mark.asyncio
async def test_revoke_invitation_not_found(client, auth_ctx):
    """Revoke non-existent invitation returns 404."""
    headers, _ = auth_ctx
    r = await client.delete(f"/api/v1/teams/invitations/{uuid.uuid4()}", headers=headers)
    assert r.status_code == 404


# ── Update Member Role ──


@pytest.mark.asyncio
async def test_update_member_role(client, auth_ctx):
    """Update a member's role."""
    headers, uid = auth_ctx
    r = await client.put(f"/api/v1/teams/members/{uid}/role?role=editor", headers=headers)
    assert r.status_code == 200
    assert r.json()["role"] == "editor"


@pytest.mark.asyncio
async def test_update_member_role_not_found(client, auth_ctx):
    """Update role for non-existent member returns 404."""
    headers, _ = auth_ctx
    r = await client.put(f"/api/v1/teams/members/{uuid.uuid4()}/role?role=admin", headers=headers)
    assert r.status_code == 404


# ── Remove Member ──


@pytest.mark.asyncio
async def test_remove_member(client, auth_ctx, db):
    """Remove a member from the tenant."""
    # Create a second user and add them to the same tenant
    headers1, uid1 = auth_ctx
    r = await db.execute(select(Tenant.id).join(UserTenant).where(UserTenant.user_id == uid1))
    tenant_id = r.scalar()

    # Register second user
    email2 = f"t{uuid.uuid4().hex[:6]}@test.cn"
    resp = await client.post("/api/v1/auth/register", json={
        "email": email2, "password": "Test1234", "display_name": "Member2",
    })
    uid2 = uuid.UUID(resp.json()["user"]["id"])

    # Add uid2 to the same tenant
    ut2 = UserTenant(user_id=uid2, tenant_id=tenant_id, role="viewer")
    db.add(ut2)
    await db.commit()

    # Remove uid2
    r = await client.delete(f"/api/v1/teams/members/{uid2}", headers=headers1)
    assert r.status_code == 200
    assert r.json()["status"] == "removed"


@pytest.mark.asyncio
async def test_remove_member_not_found(client, auth_ctx):
    """Remove non-existent member returns 404."""
    headers, _ = auth_ctx
    r = await client.delete(f"/api/v1/teams/members/{uuid.uuid4()}", headers=headers)
    assert r.status_code == 404


# ── Shared Folders ──


@pytest.mark.asyncio
async def test_list_shared_folders_empty(client, auth_ctx):
    """List shared folders returns empty list when none exist."""
    headers, _ = auth_ctx
    r = await client.get("/api/v1/teams/shared-folders", headers=headers)
    assert r.status_code == 200
    data = r.json()
    assert data["folders"] == []


@pytest.mark.asyncio
async def test_create_shared_folder(client, auth_ctx):
    """Create a shared folder returns folder id."""
    headers, _ = auth_ctx
    r = await client.post("/api/v1/teams/shared-folders", headers=headers, json={
        "name": "Team Literature",
        "description": "Curated oncology papers",
        "color": "#1a5276",
    })
    assert r.status_code == 200
    data = r.json()
    assert data["status"] == "created"
    assert "id" in data
    uuid.UUID(data["id"])


@pytest.mark.asyncio
async def test_delete_shared_folder(client, auth_ctx, db):
    """Delete a shared folder returns success."""
    headers, uid = auth_ctx
    r = await db.execute(select(Tenant.id).join(UserTenant).where(UserTenant.user_id == uid))
    tenant_id = r.scalar()

    # Create a folder and its shared record directly
    folder = UserFolder(
        id=uuid.uuid4(), user_id=uid, tenant_id=tenant_id,
        name="To Delete", is_shared=True,
    )
    db.add(folder)
    await db.flush()

    shared = SharedFolder(
        folder_id=folder.id, target_type="tenant",
        target_id=tenant_id, permission="view", created_by=uid,
    )
    db.add(shared)
    await db.commit()

    r = await client.delete(f"/api/v1/teams/shared-folders/{folder.id}", headers=headers)
    assert r.status_code == 200
    assert r.json()["status"] == "deleted"


# ── Team Activity ──


@pytest.mark.asyncio
async def test_team_activity(client, auth_ctx):
    """Team activity returns a list."""
    headers, _ = auth_ctx
    r = await client.get("/api/v1/teams/activity", headers=headers)
    assert r.status_code == 200
    data = r.json()
    assert "activities" in data
    assert isinstance(data["activities"], list)


# ── Reading Rankings ──


@pytest.mark.asyncio
async def test_reading_rankings(client, auth_ctx):
    """Reading rankings returns a list."""
    headers, _ = auth_ctx
    r = await client.get("/api/v1/teams/reading-rankings", headers=headers)
    assert r.status_code == 200
    data = r.json()
    assert "rankings" in data
    assert isinstance(data["rankings"], list)


# ── Shared Folders Unauthorized ──


@pytest.mark.asyncio
async def test_create_shared_folder_unauthorized(client):
    """Create shared folder without auth returns 401."""
    r = await client.post("/api/v1/teams/shared-folders", json={
        "name": "Should Not Work",
    })
    assert r.status_code == 403
