"""Unit tests for Backups namespace."""

from __future__ import annotations

import pytest
import respx

from httpx import Response

from pinecone.__interface__.backups import BackupNamespaceProtocol
from pinecone._internal.http import AsyncHTTPClient, HTTPClient
from pinecone.async_client.backups import AsyncBackups
from pinecone.client.backups import Backups


pytestmark = pytest.mark.unit


class TestBackupsProtocolCompliance:
    """Tests for protocol compliance."""

    def test_backups_satisfies_protocol(self):
        """Test that Backups class satisfies BackupNamespaceProtocol."""
        http_client = HTTPClient(api_key="test-key")
        backups = Backups(http_client=http_client)

        assert isinstance(backups, BackupNamespaceProtocol)

    def test_async_backups_has_required_methods(self):
        """Test that AsyncBackups has all required methods.

        Note: AsyncBackups does not inherit from BackupNamespaceProtocol
        because async methods have incompatible return types (Coroutine vs direct types).
        We verify methods exist and are callable instead.
        """
        http_client = AsyncHTTPClient(api_key="test-key")
        backups = AsyncBackups(http_client=http_client)

        # Verify all required methods exist and are callable
        assert hasattr(backups, "create")
        assert callable(backups.create)
        assert hasattr(backups, "list")
        assert callable(backups.list)
        assert hasattr(backups, "describe")
        assert callable(backups.describe)
        assert hasattr(backups, "delete")
        assert callable(backups.delete)


class TestBackupsCreate:
    """Tests for create() method."""

    @respx.mock
    def test_create_backup_success(self):
        """Test successful create backup operation."""
        mock_response = {
            "backup_id": "8c85e612-ed1c-4f97-9f8c-8194e07bcf71",
            "source_index_name": "my-index",
            "source_index_id": "index-123",
            "status": "Initializing",
            "cloud": "aws",
            "region": "us-east-1",
            "created_at": "2024-01-15T10:30:00Z",
            "name": "test-backup",
            "description": "Test backup description",
        }

        route = respx.post("https://api.pinecone.io/indexes/my-index/backups").mock(
            return_value=Response(201, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        result = backups.create(
            index_name="my-index", name="test-backup", description="Test backup description"
        )

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"
        assert "Api-Key" in request.headers

        # Verify response
        assert result.backup_id == "8c85e612-ed1c-4f97-9f8c-8194e07bcf71"
        assert result.source_index_name == "my-index"
        assert result.status == "Initializing"
        assert result.name == "test-backup"

    @respx.mock
    def test_create_backup_minimal(self):
        """Test create backup with minimal parameters."""
        mock_response = {
            "backup_id": "8c85e612-ed1c-4f97-9f8c-8194e07bcf71",
            "source_index_name": "my-index",
            "source_index_id": "index-123",
            "status": "Initializing",
            "cloud": "aws",
            "region": "us-east-1",
            "created_at": "2024-01-15T10:30:00Z",
        }

        route = respx.post("https://api.pinecone.io/indexes/my-index/backups").mock(
            return_value=Response(201, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        result = backups.create(index_name="my-index")

        assert route.called
        assert result.backup_id == "8c85e612-ed1c-4f97-9f8c-8194e07bcf71"

    def test_create_backup_empty_index_name(self):
        """Test create backup with empty index name raises ValueError."""
        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        with pytest.raises(ValueError, match="index_name"):
            backups.create(index_name="")


class TestBackupsList:
    """Tests for list() method."""

    @respx.mock
    def test_list_all_backups(self):
        """Test list all backups in project."""
        mock_response = {
            "data": [
                {
                    "backup_id": "backup-1",
                    "source_index_name": "index-1",
                    "source_index_id": "id-1",
                    "status": "Ready",
                    "cloud": "aws",
                    "region": "us-east-1",
                    "created_at": "2024-01-15T10:30:00Z",
                },
                {
                    "backup_id": "backup-2",
                    "source_index_name": "index-2",
                    "source_index_id": "id-2",
                    "status": "Initializing",
                    "cloud": "aws",
                    "region": "us-west-2",
                    "created_at": "2024-01-16T10:30:00Z",
                },
            ],
            "pagination": {"next": None},
        }

        route = respx.get("https://api.pinecone.io/backups").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        paginator = backups.list()
        items = list(paginator)

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"

        # Verify response
        assert len(items) == 2
        assert items[0].backup_id == "backup-1"
        assert items[1].backup_id == "backup-2"

    @respx.mock
    def test_list_backups_by_index(self):
        """Test list backups filtered by index."""
        mock_response = {
            "data": [
                {
                    "backup_id": "backup-1",
                    "source_index_name": "my-index",
                    "source_index_id": "id-1",
                    "status": "Ready",
                    "cloud": "aws",
                    "region": "us-east-1",
                    "created_at": "2024-01-15T10:30:00Z",
                }
            ],
            "pagination": {"next": None},
        }

        route = respx.get("https://api.pinecone.io/indexes/my-index/backups").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        paginator = backups.list(index_name="my-index")
        items = list(paginator)

        assert route.called
        assert len(items) == 1
        assert items[0].source_index_name == "my-index"

    @respx.mock
    def test_list_backups_empty(self):
        """Test list with no backups."""
        mock_response = {"data": [], "pagination": {"next": None}}

        route = respx.get("https://api.pinecone.io/backups").mock(
            return_value=Response(200, json=mock_response)
        )

        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        paginator = backups.list()
        items = list(paginator)

        assert route.called
        assert len(items) == 0

    def test_list_backups_invalid_limit(self):
        """Test list with invalid limit raises ValueError."""
        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        with pytest.raises(ValueError, match="limit must be a positive integer"):
            list(backups.list(limit=0))

    def test_list_backups_empty_index_name(self):
        """Test list with empty index_name raises ValueError."""
        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        with pytest.raises(ValueError, match="index_name must be a non-empty string"):
            list(backups.list(index_name=""))


class TestBackupsDescribe:
    """Tests for describe() method."""

    @respx.mock
    def test_describe_backup_success(self):
        """Test successful describe backup operation."""
        mock_response = {
            "backup_id": "8c85e612-ed1c-4f97-9f8c-8194e07bcf71",
            "source_index_name": "my-index",
            "source_index_id": "index-123",
            "status": "Ready",
            "cloud": "aws",
            "region": "us-east-1",
            "created_at": "2024-01-15T10:30:00Z",
            "dimension": 128,
            "metric": "cosine",
            "record_count": 1000,
            "namespace_count": 5,
            "size_bytes": 10485760,
        }

        route = respx.get(
            "https://api.pinecone.io/backups/8c85e612-ed1c-4f97-9f8c-8194e07bcf71"
        ).mock(return_value=Response(200, json=mock_response))

        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        result = backups.describe("8c85e612-ed1c-4f97-9f8c-8194e07bcf71")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"

        # Verify response
        assert result.backup_id == "8c85e612-ed1c-4f97-9f8c-8194e07bcf71"
        assert result.status == "Ready"
        assert result.record_count == 1000
        assert result.size_bytes == 10485760

    def test_describe_backup_empty_id(self):
        """Test describe backup with empty id raises ValueError."""
        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        with pytest.raises(ValueError, match="backup_id"):
            backups.describe("")


class TestBackupsDelete:
    """Tests for delete() method."""

    @respx.mock
    def test_delete_backup_success(self):
        """Test successful delete backup operation."""
        route = respx.delete(
            "https://api.pinecone.io/backups/8c85e612-ed1c-4f97-9f8c-8194e07bcf71"
        ).mock(return_value=Response(202))

        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        # Should not raise
        backups.delete("8c85e612-ed1c-4f97-9f8c-8194e07bcf71")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"

    def test_delete_backup_empty_id(self):
        """Test delete backup with empty id raises ValueError."""
        client = HTTPClient(api_key="test-key")
        backups = Backups(client)

        with pytest.raises(ValueError, match="backup_id"):
            backups.delete("")


class TestBackupsInteractiveDiscovery:
    """Tests for interactive discovery features."""

    def test_repr(self):
        """Test __repr__ returns clean representation."""
        client = HTTPClient(api_key="test-key")
        backups = Backups(http_client=client, base_url="https://api.pinecone.io")

        repr_str = repr(backups)
        assert "Backups" in repr_str
        assert "base_url" in repr_str
        assert "https://api.pinecone.io" in repr_str

    def test_dir_shows_only_public_methods(self):
        """Test __dir__ returns only public API methods."""
        client = HTTPClient(api_key="test-key")
        backups = Backups(http_client=client)

        public_attrs = dir(backups)

        # Should include public methods
        assert "create" in public_attrs
        assert "list" in public_attrs
        assert "describe" in public_attrs
        assert "delete" in public_attrs

        # Should NOT include private attributes
        assert "_http" not in public_attrs
        assert "_base_url" not in public_attrs
        assert "_adapter" not in public_attrs


class TestAsyncBackupsCreate:
    """Tests for async create() method."""

    @pytest.mark.asyncio()
    @respx.mock
    async def test_create_backup_success(self):
        """Test successful async create backup operation."""
        mock_response = {
            "backup_id": "8c85e612-ed1c-4f97-9f8c-8194e07bcf71",
            "source_index_name": "my-index",
            "source_index_id": "index-123",
            "status": "Initializing",
            "cloud": "aws",
            "region": "us-east-1",
            "created_at": "2024-01-15T10:30:00Z",
            "name": "test-backup",
        }

        route = respx.post("https://api.pinecone.io/indexes/my-index/backups").mock(
            return_value=Response(201, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        backups = AsyncBackups(client)

        result = await backups.create(index_name="my-index", name="test-backup")

        # Verify request
        assert route.called
        request = route.calls.last.request
        assert request.headers["X-Pinecone-Api-Version"] == "2025-10"

        # Verify response
        assert result.backup_id == "8c85e612-ed1c-4f97-9f8c-8194e07bcf71"
        assert result.status == "Initializing"


class TestAsyncBackupsList:
    """Tests for async list() method."""

    @pytest.mark.asyncio()
    @respx.mock
    async def test_list_all_backups(self):
        """Test async list all backups in project."""
        mock_response = {
            "data": [
                {
                    "backup_id": "backup-1",
                    "source_index_name": "index-1",
                    "source_index_id": "id-1",
                    "status": "Ready",
                    "cloud": "aws",
                    "region": "us-east-1",
                    "created_at": "2024-01-15T10:30:00Z",
                }
            ],
            "pagination": {"next": None},
        }

        route = respx.get("https://api.pinecone.io/backups").mock(
            return_value=Response(200, json=mock_response)
        )

        client = AsyncHTTPClient(api_key="test-key")
        backups = AsyncBackups(client)

        paginator = backups.list()
        items = []
        async for item in paginator:
            items.append(item)

        assert route.called
        assert len(items) == 1
        assert items[0].backup_id == "backup-1"

    @pytest.mark.asyncio()
    async def test_list_backups_empty_index_name(self):
        """Test async list with empty index_name raises ValueError."""
        client = AsyncHTTPClient(api_key="test-key")
        backups = AsyncBackups(client)

        with pytest.raises(ValueError, match="index_name must be a non-empty string"):
            # Force evaluation of the async generator
            async for _ in backups.list(index_name=""):
                pass


class TestAsyncBackupsDescribe:
    """Tests for async describe() method."""

    @pytest.mark.asyncio()
    @respx.mock
    async def test_describe_backup_success(self):
        """Test successful async describe backup operation."""
        mock_response = {
            "backup_id": "8c85e612-ed1c-4f97-9f8c-8194e07bcf71",
            "source_index_name": "my-index",
            "source_index_id": "index-123",
            "status": "Ready",
            "cloud": "aws",
            "region": "us-east-1",
            "created_at": "2024-01-15T10:30:00Z",
        }

        route = respx.get(
            "https://api.pinecone.io/backups/8c85e612-ed1c-4f97-9f8c-8194e07bcf71"
        ).mock(return_value=Response(200, json=mock_response))

        client = AsyncHTTPClient(api_key="test-key")
        backups = AsyncBackups(client)

        result = await backups.describe("8c85e612-ed1c-4f97-9f8c-8194e07bcf71")

        assert route.called
        assert result.backup_id == "8c85e612-ed1c-4f97-9f8c-8194e07bcf71"
        assert result.status == "Ready"


class TestAsyncBackupsDelete:
    """Tests for async delete() method."""

    @pytest.mark.asyncio()
    @respx.mock
    async def test_delete_backup_success(self):
        """Test successful async delete backup operation."""
        route = respx.delete(
            "https://api.pinecone.io/backups/8c85e612-ed1c-4f97-9f8c-8194e07bcf71"
        ).mock(return_value=Response(202))

        client = AsyncHTTPClient(api_key="test-key")
        backups = AsyncBackups(client)

        # Should not raise
        await backups.delete("8c85e612-ed1c-4f97-9f8c-8194e07bcf71")

        assert route.called
