"""Tests for session sharing (create, access, revoke)."""

import pytest
from httpx import AsyncClient


pytestmark = pytest.mark.asyncio


class TestSessionSharing:
    """Test session share creation, access control, and revocation."""

    async def _create_session(self, client, auth_headers, tree_id):
        """Helper: start a session for a tree."""
        response = await client.post(
            "/api/v1/sessions",
            json={"tree_id": tree_id},
            headers=auth_headers
        )
        assert response.status_code == 201
        return response.json()

    async def test_create_public_share(self, client: AsyncClient, auth_headers, test_tree):
        """Create a public share link."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        response = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "public", "share_name": "Customer link"},
            headers=auth_headers
        )
        assert response.status_code == 201

        share = response.json()
        assert share["visibility"] == "public"
        assert share["share_name"] == "Customer link"
        assert share["is_active"] is True
        assert share["view_count"] == 0
        assert len(share["share_token"]) > 0

    async def test_create_account_share(self, client: AsyncClient, auth_headers, test_tree):
        """Create an account-only share link."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        response = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "account"},
            headers=auth_headers
        )
        assert response.status_code == 201
        assert response.json()["visibility"] == "account"

    async def test_access_public_share(self, client: AsyncClient, auth_headers, test_tree):
        """Access a public share without authentication."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        # Create share
        share_resp = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "public"},
            headers=auth_headers
        )
        share_token = share_resp.json()["share_token"]

        # Access without auth
        response = await client.get(f"/api/v1/share/{share_token}")
        assert response.status_code == 200

        data = response.json()
        assert data["session_id"] == session["id"]
        assert data["visibility"] == "public"
        assert data["path_taken"] is not None

    async def test_access_revoked_share_returns_404(self, client: AsyncClient, auth_headers, test_tree):
        """Accessing a revoked share returns 404."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        # Create and revoke share
        share_resp = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "public"},
            headers=auth_headers
        )
        share = share_resp.json()

        await client.delete(
            f"/api/v1/shares/{share['id']}",
            headers=auth_headers
        )

        # Try to access revoked share
        response = await client.get(f"/api/v1/share/{share['share_token']}")
        assert response.status_code == 404

    async def test_access_nonexistent_share_returns_404(self, client: AsyncClient):
        """Accessing a nonexistent share token returns 404."""
        response = await client.get("/api/v1/share/nonexistent-token-12345")
        assert response.status_code == 404

    async def test_list_my_shares(self, client: AsyncClient, auth_headers, test_tree):
        """List shares created by current user."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        # Create two shares
        await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "public", "share_name": "Link 1"},
            headers=auth_headers
        )
        await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "account", "share_name": "Link 2"},
            headers=auth_headers
        )

        response = await client.get(
            "/api/v1/shares/my-shares",
            headers=auth_headers
        )
        assert response.status_code == 200
        shares = response.json()
        assert len(shares) == 2

    async def test_revoke_share(self, client: AsyncClient, auth_headers, test_tree):
        """Revoke a share link (soft delete)."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        share_resp = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "public"},
            headers=auth_headers
        )
        share = share_resp.json()

        # Revoke
        response = await client.delete(
            f"/api/v1/shares/{share['id']}",
            headers=auth_headers
        )
        assert response.status_code == 204

        # Verify it's gone from my-shares
        list_resp = await client.get(
            "/api/v1/shares/my-shares",
            headers=auth_headers
        )
        shares = list_resp.json()
        assert len(shares) == 0

    async def test_multiple_shares_per_session(self, client: AsyncClient, auth_headers, test_tree):
        """Multiple shares for the same session work independently."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        # Create public + account shares
        resp1 = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "public", "share_name": "For customer"},
            headers=auth_headers
        )
        resp2 = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "account", "share_name": "For team"},
            headers=auth_headers
        )

        assert resp1.status_code == 201
        assert resp2.status_code == 201

        # Both tokens are different
        assert resp1.json()["share_token"] != resp2.json()["share_token"]

        # Both accessible
        access1 = await client.get(f"/api/v1/share/{resp1.json()['share_token']}")
        assert access1.status_code == 200

    async def test_share_view_count_increments(self, client: AsyncClient, auth_headers, test_tree):
        """View count increments on each access."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        share_resp = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "public"},
            headers=auth_headers
        )
        token = share_resp.json()["share_token"]

        # Access three times
        await client.get(f"/api/v1/share/{token}")
        await client.get(f"/api/v1/share/{token}")
        await client.get(f"/api/v1/share/{token}")

        # Check view count via my-shares
        list_resp = await client.get(
            "/api/v1/shares/my-shares",
            headers=auth_headers
        )
        shares = list_resp.json()
        assert shares[0]["view_count"] == 3

    async def test_share_requires_session_ownership(self, client: AsyncClient, auth_headers, test_tree, test_db):
        """Non-owner cannot create a share for someone else's session."""
        session = await self._create_session(client, auth_headers, test_tree["id"])

        # Register a different user
        await client.post("/api/v1/auth/register", json={
            "email": "other@example.com",
            "password": "OtherPassword123!",
            "name": "Other User"
        })
        login_resp = await client.post("/api/v1/auth/login/json", json={
            "email": "other@example.com",
            "password": "OtherPassword123!"
        })
        other_headers = {"Authorization": f"Bearer {login_resp.json()['access_token']}"}

        # Try to share other user's session
        response = await client.post(
            f"/api/v1/sessions/{session['id']}/shares",
            json={"visibility": "public"},
            headers=other_headers
        )
        assert response.status_code == 403

    async def test_share_nonexistent_session(self, client: AsyncClient, auth_headers):
        """Creating a share for nonexistent session returns 404."""
        response = await client.post(
            "/api/v1/sessions/00000000-0000-0000-0000-000000000000/shares",
            json={"visibility": "public"},
            headers=auth_headers
        )
        assert response.status_code == 404

    async def test_create_share_requires_auth(self, client: AsyncClient, test_tree):
        """Creating a share without auth returns 401."""
        response = await client.post(
            "/api/v1/sessions/00000000-0000-0000-0000-000000000000/shares",
            json={"visibility": "public"}
        )
        assert response.status_code == 401
