"""Index management protocol."""

from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable

from pinecone.__interface__.indexes.info import IndexInfoProtocol
from pinecone.__interface__.indexes.types import IndexSpec, MetricType


if TYPE_CHECKING:
    from pinecone.models.pagination import Paginator


@runtime_checkable
class IndexNamespaceProtocol(Protocol):
    """Protocol for index management operations.

    This protocol defines CRUD operations for managing Pinecone indexes.

    Example:
        >>> from pinecone import Pinecone
        >>> pc = Pinecone(api_key="...")
        >>>
        >>> # List indexes
        >>> indexes = pc.indexes.list()
        >>>
        >>> # Create index
        >>> pc.indexes.create(
        ...     name="my-index",
        ...     dimension=128,
        ...     metric="cosine",
        ...     spec={"serverless": {"cloud": "aws", "region": "us-east-1"}},
        ... )
        >>>
        >>> # Describe index
        >>> info = pc.indexes.describe("my-index")
        >>> print(f"Status: {info.status}")
    """

    def list(
        self,
        *,
        limit: int | None = None,
        pagination_token: str | None = None,
    ) -> "Paginator[IndexInfoProtocol]":
        """List all indexes.

        Args:
            limit: Maximum number of items to return (across all pages).
            pagination_token: Token to resume pagination from a previous call.

        Returns:
            Paginator over IndexInfo objects. Iterate directly for items,
            or use .pages() for page-level access.

        Raises:
            APIError: If the API returns an error response
            APIConnectionError: If the request fails to connect
            TimeoutError: If the request times out

        Example:
            >>> # Iterate all indexes
            >>> for index in pc.indexes.list():
            ...     print(f"{index.name}: {index.config.dimension}D")

        Example with pagination control:
            >>> # Get first 10 indexes
            >>> for index in pc.indexes.list(limit=10):
            ...     print(index.name)

        Example with page-level access:
            >>> for page in pc.indexes.list().pages():
            ...     print(f"Got {len(page.items)} indexes")
        """
        ...

    def create(
        self,
        name: str,
        *,
        dimension: int | None = None,
        metric: MetricType = "cosine",
        spec: IndexSpec,
        deletion_protection: str = "disabled",
        tags: dict[str, str] | None = None,
        vector_type: str = "dense",
        timeout: int | None = None,
    ) -> IndexInfoProtocol:
        """Create a new index.

        Args:
            name: Unique index name (1-45 chars, lowercase alphanumeric or '-')
            dimension: Vector dimensionality (1-20000). Required for dense indexes,
                omit for sparse indexes
            metric: Distance metric ('cosine', 'euclidean', or 'dotproduct').
                Defaults to 'cosine'. Must be 'dotproduct' for sparse indexes
            spec: Index specification. Can be a ServerlessSpec or PodSpec dict:
                - Serverless: {"cloud": "aws"|"gcp"|"azure", "region": "..."}
                - Pod: {"environment": "...", "pod_type": "...", ...}
            deletion_protection: Whether deletion protection is enabled
                ('disabled' or 'enabled'). Defaults to 'disabled'
            tags: Optional key-value tags for the index. Keys must be 80 chars or less,
                values 120 chars or less
            vector_type: Type of vectors ('dense' or 'sparse'). Defaults to 'dense'
            timeout: Optional timeout in seconds

        Returns:
            IndexInfo with the created index metadata and configuration

        Raises:
            BadRequestError: If parameters are invalid
            ConflictError: If index with the name already exists
            NotFoundError: If cloud/region combination is invalid for serverless
            APIError: If the API returns an error response
            APIConnectionError: If the request fails to connect
            TimeoutError: If the request times out

        Example with serverless (on-demand):
            >>> info = pc.indexes.create(
            ...     name="my-serverless-index",
            ...     dimension=1536,
            ...     metric="cosine",
            ...     spec={"cloud": "aws", "region": "us-east-1"},
            ... )
            >>> print(f"Created {info.name} at {info.host}")

        Example with serverless (dedicated capacity):
            >>> info = pc.indexes.create(
            ...     name="my-dedicated-index",
            ...     dimension=1536,
            ...     metric="cosine",
            ...     spec={
            ...         "cloud": "aws",
            ...         "region": "us-east-1",
            ...         "read_capacity": {
            ...             "mode": "Dedicated",
            ...             "dedicated": {
            ...                 "node_type": "b1",
            ...                 "scaling": "Manual",
            ...                 "manual": {"shards": 2, "replicas": 1},
            ...             },
            ...         },
            ...     },
            ... )

        Example with sparse vectors:
            >>> info = pc.indexes.create(
            ...     name="sparse-index",
            ...     metric="dotproduct",
            ...     vector_type="sparse",
            ...     spec={"cloud": "gcp", "region": "us-east1"},
            ... )

        Example with tags and deletion protection:
            >>> info = pc.indexes.create(
            ...     name="production-index",
            ...     dimension=768,
            ...     spec={"cloud": "aws", "region": "us-west-2"},
            ...     deletion_protection="enabled",
            ...     tags={"environment": "prod", "team": "ml"},
            ... )
        """
        ...

    def delete(self, name: str, *, timeout: int | None = None) -> None:
        """Delete an index.

        Warning: This operation is irreversible. The index and all its data
            will be permanently deleted.

        Args:
            name: Index name (string) to delete.
            timeout: Optional timeout in seconds. If None (default), uses the
                client's configured timeout. Increase for indexes that may take
                longer to delete (e.g., large pod-based indexes).

        Returns:
            None

        Raises:
            ValueError: If name is empty or timeout is not positive
            NotFoundError: If index does not exist
            APIError: If the API returns an error response (including 412 if there
                is a pending collection from this index)
            APIConnectionError: If the request fails to connect
            TimeoutError: If the request times out

        Example:
            >>> pc.indexes.delete("my-index")

        Example with timeout:
            >>> # Give extra time for large index deletion
            >>> pc.indexes.delete("large-index", timeout=120)

        Example with confirmation:
            >>> # Check before deleting
            >>> try:
            ...     info = pc.indexes.describe("my-index")
            ...     if info.config.dimension == 1536:
            ...         pc.indexes.delete("my-index")
            ... except NotFoundError:
            ...     print("Index doesn't exist")
        """
        ...

    def describe(self, name: str) -> IndexInfoProtocol:
        """Describe a specific index.

        Args:
            name: Index name (string) to describe.

        Returns:
            IndexInfo with metadata and configuration.

        Raises:
            ValueError: If name is empty
            NotFoundError: If index does not exist
            APIError: If the API returns an error response
            APIConnectionError: If the request fails to connect
            TimeoutError: If the request times out

        Example:
            >>> info = pc.indexes.describe("my-index")
            >>> print(f"Dimension: {info.config.dimension}")
            >>> print(f"Metric: {info.config.metric}")
            >>> print(f"Host: {info.host}")
            >>> print(f"Status: {info.status}")

        Example checking status:
            >>> info = pc.indexes.describe("my-index")
            >>> if info.status.ready:
            ...     print("Index is ready for queries")
            ... else:
            ...     print(f"Index state: {info.status.state}")

        Example waiting for index to be ready:
            >>> import time
            >>> # Poll until index is ready
            >>> while True:
            ...     info = pc.indexes.describe("my-index")
            ...     if info.status.ready:
            ...         print("Index is ready!")
            ...         break
            ...     print(f"Index state: {info.status.state}")
            ...     time.sleep(5)
        """
        ...

    def configure(
        self,
        name: str,
        *,
        deletion_protection: str | None = None,
        tags: dict[str, str] | None = None,
        spec: dict[str, Any] | None = None,
        embed: dict[str, Any] | None = None,
    ) -> IndexInfoProtocol:
        """Update index configuration.

        Args:
            name: Index name to configure
            deletion_protection: Set to "enabled" or "disabled"
            tags: Update tags (set value to "" to delete a tag)
            spec: Update spec configuration:
                - Pod: {"pod": {"replicas": 4, "pod_type": "p1.x2"}}
                - Serverless: {"serverless": {"read_capacity": {...}}}
            embed: Update embedding configuration:
                - {"field_map": {...}, "read_parameters": {...}, "write_parameters": {...}}
                - Note: model cannot be changed once set

        Returns:
            Updated IndexInfo

        Raises:
            NotFoundError: If index does not exist
            BadRequestError: If configuration is invalid
            APIError: If the API returns an error response
            APIConnectionError: If the request fails to connect
            TimeoutError: If the request times out

        Example:
            >>> # Update deletion protection
            >>> pc.indexes.configure("my-index", deletion_protection="enabled")

        Example scaling pod replicas:
            >>> # Scale pod replicas
            >>> pc.indexes.configure("my-index", spec={"pod": {"replicas": 4}})

        Example with dedicated read nodes:
            >>> # Update serverless read capacity (dedicated nodes)
            >>> pc.indexes.configure(
            ...     "my-index",
            ...     spec={
            ...         "serverless": {
            ...             "read_capacity": {
            ...                 "mode": "Dedicated",
            ...                 "dedicated": {
            ...                     "node_type": "b1",
            ...                     "scaling": "Manual",
            ...                     "manual": {"shards": 2, "replicas": 2},
            ...                 },
            ...             }
            ...         }
            ...     },
            ... )
        """
        ...

    def create_for_model(
        self,
        name: str,
        cloud: str,
        region: str,
        *,
        embed: dict[str, Any],
        deletion_protection: str = "disabled",
        tags: dict[str, str] | None = None,
        schema: dict[str, Any] | None = None,
        read_capacity: dict[str, Any] | None = None,
        timeout: int | None = None,
    ) -> IndexInfoProtocol:
        """Create index with integrated embedding model.

        Creates a serverless index with automatic text embedding using a hosted model.
        The model's dimension and metric are automatically configured.

        Args:
            name: Unique index name (1-45 chars, lowercase alphanumeric or '-')
            cloud: Cloud provider ("aws", "gcp", or "azure")
            region: Region for deployment
            embed: Embedding configuration with required fields:
                - model: Model name (e.g., "multilingual-e5-large")
                - field_map: {"text": "your-field-name"} mapping to text field
                - Optional: read_parameters, write_parameters, dimension, metric
            deletion_protection: "disabled" or "enabled" (default: "disabled")
            tags: Optional key-value tags
            schema: Optional metadata schema for indexing specific fields
            read_capacity: Optional read capacity configuration for dedicated nodes
            timeout: Optional timeout in seconds

        Returns:
            IndexInfo with created index metadata

        Raises:
            BadRequestError: If parameters are invalid
            ConflictError: If index name already exists
            NotFoundError: If cloud/region combination is invalid
            APIError: If the API returns an error response
            APIConnectionError: If the request fails to connect
            TimeoutError: If the request times out

        Example:
            >>> # Create with multilingual embedding model
            >>> info = pc.indexes.create_for_model(
            ...     name="semantic-search",
            ...     cloud="aws",
            ...     region="us-east-1",
            ...     embed={"model": "multilingual-e5-large", "field_map": {"text": "content"}},
            ... )

        Example with custom parameters:
            >>> # Create with custom parameters
            >>> info = pc.indexes.create_for_model(
            ...     name="custom-search",
            ...     cloud="gcp",
            ...     region="us-east1",
            ...     embed={
            ...         "model": "llama-text-embed-v2",
            ...         "field_map": {"text": "text_content"},
            ...         "read_parameters": {"input_type": "query", "truncate": "NONE"},
            ...         "write_parameters": {"input_type": "passage"},
            ...     },
            ...     deletion_protection="enabled",
            ...     tags={"environment": "production"},
            ... )
        """
        ...
