"""Tests for share link and public access endpoints."""

from unittest.mock import AsyncMock, patch

_DB = "api.routers.shared.db"
_STORAGE = "api.routers.shared.storage"


class TestCreateShareLink:
    """Tests for POST /v1/runs/{run_id}/share."""

    def test_create_share_link_success(self, client, auth_header, sample_run):
        run_id = sample_run["id"]
        with patch(f"{_DB}.get_run", new_callable=AsyncMock) as mock_run, \
             patch(f"{_DB}.create_share_link", new_callable=AsyncMock) as mock_create:
            mock_run.return_value = sample_run
            mock_create.return_value = None
            response = client.post(
                f"/v1/runs/{run_id}/share",
                json={"expires_in_days": 7},
                headers=auth_header,
            )
            assert response.status_code == 200
            data = response.json()
            assert "share_url" in data
            assert "share_token" in data
            assert data["expires_at"] is not None

    def test_create_share_link_no_expiry(self, client, auth_header, sample_run):
        run_id = sample_run["id"]
        with patch(f"{_DB}.get_run", new_callable=AsyncMock) as mock_run, \
             patch(f"{_DB}.create_share_link", new_callable=AsyncMock) as mock_create:
            mock_run.return_value = sample_run
            mock_create.return_value = None
            response = client.post(
                f"/v1/runs/{run_id}/share",
                json={"expires_in_days": None},
                headers=auth_header,
            )
            assert response.status_code == 200
            data = response.json()
            assert data["expires_at"] is None

    def test_create_share_link_unauthorized(self, client):
        response = client.post(
            "/v1/runs/550e8400-e29b-41d4-a716-446655440000/share",
            json={"expires_in_days": 7},
        )
        assert response.status_code == 401


class TestListRunShares:
    """Tests for GET /v1/runs/{run_id}/shares."""

    def test_list_shares_success(self, client, auth_header, sample_run):
        run_id = sample_run["id"]
        with patch(f"{_DB}.get_run", new_callable=AsyncMock) as mock_run, \
             patch(f"{_DB}.get_run_share_links", new_callable=AsyncMock) as mock_links:
            mock_run.return_value = sample_run
            mock_links.return_value = [
                {"share_token": "tok_1", "expires_at": "2025-02-01T00:00:00Z"},
            ]
            response = client.get(
                f"/v1/runs/{run_id}/shares", headers=auth_header,
            )
            assert response.status_code == 200
            data = response.json()
            assert len(data) == 1

    def test_list_shares_unauthorized(self, client):
        response = client.get(
            "/v1/runs/550e8400-e29b-41d4-a716-446655440000/shares",
        )
        assert response.status_code == 401


class TestGetSharedRun:
    """Tests for GET /v1/shared/{share_token} (public endpoint)."""

    def test_get_shared_run_success(self, client):
        with patch(f"{_DB}.get_share_link", new_callable=AsyncMock) as mock_link, \
             patch(f"{_DB}.get_shared_run_data", new_callable=AsyncMock) as mock_run, \
             patch(f"{_DB}.increment_share_view_count", new_callable=AsyncMock) as mock_inc, \
             patch(f"{_STORAGE}.sign_download_url"):
            mock_link.return_value = {
                "run_id": "550e8400-e29b-41d4-a716-446655440000",
                "expires_at": None,
            }
            mock_run.return_value = {
                "id": "550e8400-e29b-41d4-a716-446655440000",
                "status": "FAILED",
                "created_at": "2025-01-15T10:00:00Z",
            }
            mock_inc.return_value = None
            response = client.get("/v1/shared/test_token_abc")
            assert response.status_code == 200
            data = response.json()
            assert data["id"] == "550e8400-e29b-41d4-a716-446655440000"
            assert data["status"] == "FAILED"

    def test_get_shared_run_not_found(self, client):
        with patch(f"{_DB}.get_share_link", new_callable=AsyncMock) as mock_link:
            mock_link.return_value = None
            response = client.get("/v1/shared/nonexistent_token")
            assert response.status_code == 404


class TestDeleteShareLink:
    """Tests for DELETE /v1/shares/{share_token}."""

    def test_delete_share_link_success(self, client, auth_header, sample_run):
        with patch(f"{_DB}.get_share_link", new_callable=AsyncMock) as mock_link, \
             patch(f"{_DB}.get_run", new_callable=AsyncMock) as mock_run, \
             patch(f"{_DB}.delete_share_link", new_callable=AsyncMock) as mock_delete:
            mock_link.return_value = {
                "run_id": sample_run["id"],
                "share_token": "tok_1",
            }
            mock_run.return_value = sample_run
            mock_delete.return_value = None
            response = client.delete("/v1/shares/tok_1", headers=auth_header)
            assert response.status_code == 200
            assert response.json()["status"] == "deleted"

    def test_delete_share_link_not_found(self, client, auth_header):
        with patch(f"{_DB}.get_share_link", new_callable=AsyncMock) as mock_link:
            mock_link.return_value = None
            response = client.delete("/v1/shares/nonexistent", headers=auth_header)
            assert response.status_code == 404

    def test_delete_share_link_unauthorized(self, client):
        response = client.delete("/v1/shares/tok_1")
        assert response.status_code == 401


class TestSessionShareLink:
    """Tests for session share link endpoints."""

    def test_create_session_share(self, client, auth_header, sample_session):
        session_id = sample_session["id"]
        with patch(f"{_DB}.get_session", new_callable=AsyncMock) as mock_session, \
             patch(f"{_DB}.create_session_share_link", new_callable=AsyncMock) as mock_create:
            mock_session.return_value = sample_session
            mock_create.return_value = None
            response = client.post(
                f"/v1/sessions/{session_id}/share",
                json={"expires_in_days": 7},
                headers=auth_header,
            )
            assert response.status_code == 200
            data = response.json()
            assert "share_url" in data
            assert "share_token" in data

    def test_delete_session_share(self, client, auth_header, sample_session):
        with patch(f"{_DB}.get_session_share_link", new_callable=AsyncMock) as mock_link, \
             patch(f"{_DB}.get_session", new_callable=AsyncMock) as mock_session, \
             patch(f"{_DB}.delete_session_share_link", new_callable=AsyncMock) as mock_delete:
            mock_link.return_value = {"session_id": sample_session["id"]}
            mock_session.return_value = sample_session
            mock_delete.return_value = None
            response = client.delete(
                "/v1/session-shares/tok_session_1", headers=auth_header,
            )
            assert response.status_code == 200
            assert response.json()["status"] == "deleted"
