"""Index management operations (2026-01.alpha API).

This module provides the schema-driven index management API. Indexes are
defined with field-level schemas instead of top-level dimension/metric parameters.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from pinecone._internal.constants import INDEXES_API_VERSION_ALPHA


if TYPE_CHECKING:
    from pinecone.models.indexes import IndexList, IndexModel
    from pinecone.models.pagination import Page, Paginator


class Indexes:
    """Index management operations.

    This class provides CRUD operations for managing Pinecone indexes using
    the 2026-01.alpha schema-driven API. Indexes are defined with field-level
    schemas that support multiple vector fields, full-text search, and metadata filtering.

    Access via the ``indexes`` property on a Pinecone instance::

        pc = Pinecone(api_key="...")
        pc.indexes.create(schema={...})

    Args:
        http_client: HTTP client for making requests
        base_url: Base URL for the control plane API

    Example - Create index (defaults to managed if deployment omitted):
        >>> from pinecone import Pinecone
        >>> pc = Pinecone(api_key="...")
        >>>
        >>> index = pc.indexes.create(
        ...     name="my-index",
        ...     schema={
        ...         "fields": {
        ...             "embedding": {"type": "dense_vector", "dimension": 1536, "metric": "cosine"}
        ...         }
        ...     },
        ...     deployment={
        ...         "deployment_type": "managed",
        ...         "environment": "aped-4627-b74a",
        ...         "cloud": "aws",
        ...         "region": "us-east-1",
        ...     },
        ... )

    Example - List indexes:
        >>> indexes = pc.indexes.list()
        >>> for idx in indexes.indexes:
        ...     print(f"{idx.name}: {idx.status.state}")

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

    Note:
        This is the 2026-01.alpha API. For the legacy 2025-10 API, use
        ``pc.indexes_old``.
    """

    def __init__(self, *, http_client: Any, base_url: str) -> None:
        """Initialize Indexes namespace.

        Args:
            http_client: HTTP client for making requests
            base_url: Base URL for the control plane API
        """
        self._http = http_client
        self._base_url = base_url

    def _make_indexes_request(
        self,
        method: str,
        path: str,
        **kwargs: Any,
    ) -> Any:
        """Make HTTP request to indexes API with 2026-01.alpha version header.

        Args:
            method: HTTP method ("get", "post", "patch", "delete")
            path: API path (e.g., "/indexes", "/indexes/my-index")
            **kwargs: Additional arguments to pass to HTTP client

        Returns:
            HTTP response object

        Example:
            >>> response = self._make_indexes_request(
            ...     "post", "/indexes", headers={"Content-Type": "application/json"}, content=json_bytes
            ... )
        """
        # Get or create headers dict
        headers = kwargs.pop("headers", {})

        # Add API version header
        headers["X-Pinecone-Api-Version"] = INDEXES_API_VERSION_ALPHA

        # Make request
        return getattr(self._http, method)(
            f"{self._base_url}{path}",
            headers=headers,
            **kwargs,
        )

    def create(
        self,
        *,
        schema: dict[str, Any],
        name: str | None = None,
        deployment: dict[str, Any] | None = None,
        read_capacity: dict[str, Any] | None = None,
        deletion_protection: str | None = None,
        tags: dict[str, str] | None = None,
        source_collection: str | None = None,
    ) -> IndexModel:
        """Create a new index with schema-driven configuration.

        The 2026-01.alpha API uses field-level schema definitions instead of
        top-level dimension/metric/vector_type parameters. This enables indexes
        with multiple vector fields, full-text search, and metadata filtering.

        Args:
            schema: Index schema with field definitions. Required.
                Must contain a "fields" dict mapping field names to field definitions.
                Each field must have a "type" (dense_vector, sparse_vector, semantic_text, string, integer).
            name: Index name (auto-generated if not provided)
            deployment: Deployment configuration dict with "deployment_type" key.
                Defaults to managed if not provided.
                - Managed: {"deployment_type": "managed", "environment": "...", "cloud": "aws", "region": "us-east-1"}
                - Pod: {"deployment_type": "pod", "environment": "us-east1-gcp", "pod_type": "p1.x1", ...}
                - BYOC: {"deployment_type": "byoc", "environment": "us-east1-gcp-byoc"}
            read_capacity: Read capacity specification (managed indexes only)
                - On-demand: {"mode": "OnDemand"}
                - Dedicated: {"mode": "Dedicated", "node_type": "t1", "scaling": {...}}
            deletion_protection: "enabled" or "disabled" (default: "disabled")
            tags: User-defined key-value tags
            source_collection: Create index from existing collection

        Returns:
            IndexModel: Created index description

        Raises:
            ValueError: If schema is invalid or missing required fields
            APIError: If API request fails

        Example - Minimal vector search:
            >>> index = pc.indexes.create(
            ...     schema={
            ...         "fields": {
            ...             "embedding": {"type": "dense_vector", "dimension": 1536, "metric": "cosine"}
            ...         }
            ...     }
            ... )

        Example - Full-text search + vector search:
            >>> index = pc.indexes.create(
            ...     name="document-search",
            ...     schema={
            ...         "fields": {
            ...             "content_embedding": {
            ...                 "type": "dense_vector",
            ...                 "dimension": 768,
            ...                 "metric": "cosine",
            ...                 "description": "Document content embeddings",
            ...             },
            ...             "title": {
            ...                 "type": "string",
            ...                 "full_text_searchable": True,
            ...                 "description": "Document title",
            ...             },
            ...             "category": {
            ...                 "type": "string",
            ...                 "filterable": True,
            ...                 "description": "Document category",
            ...             },
            ...             "year": {
            ...                 "type": "integer",
            ...                 "filterable": True,
            ...                 "description": "Publication year",
            ...             },
            ...         }
            ...     },
            ...     deployment={
            ...         "deployment_type": "managed",
            ...         "environment": "aped-4627-b74a",
            ...         "cloud": "aws",
            ...         "region": "us-east-1",
            ...     },
            ...     deletion_protection="enabled",
            ...     tags={"environment": "production"},
            ... )

        Example - Semantic text (integrated embedding):
            >>> index = pc.indexes.create(
            ...     schema={
            ...         "fields": {
            ...             "description": {
            ...                 "type": "semantic_text",
            ...                 "model": "multilingual-e5-large",
            ...                 "metric": "cosine",
            ...                 "write_parameters": {"input_type": "passage"},
            ...                 "read_parameters": {"input_type": "query", "truncate": "NONE"},
            ...             }
            ...         }
            ...     }
            ... )

        Example - Pod-based index:
            >>> index = pc.indexes.create(
            ...     name="pod-index",
            ...     schema={
            ...         "fields": {
            ...             "_values": {  # Reserved field name for pod indexes
            ...                 "type": "dense_vector",
            ...                 "dimension": 1536,
            ...                 "metric": "cosine",
            ...             }
            ...         }
            ...     },
            ...     deployment={
            ...         "deployment_type": "pod",
            ...         "environment": "us-east1-gcp",
            ...         "pod_type": "p1.x2",
            ...         "replicas": 2,
            ...         "shards": 2,
            ...     },
            ... )
        """
        from pinecone._internal.adapters.indexes import create_adapter
        from pinecone._internal.validation import (
            convert_with_validation,
            parse_response_with_validation,
        )
        from pinecone.models.indexes import CreateIndexRequest

        # Build request dict then convert to typed model
        # This allows users to pass dicts while ensuring type safety
        # Filter out None values - API expects fields to be omitted, not null
        request_dict = {
            "schema": schema,
            "name": name,
            "deployment": deployment,
            "read_capacity": read_capacity,
            "deletion_protection": deletion_protection,
            "tags": tags,
            "source_collection": source_collection,
        }
        request_dict = {k: v for k, v in request_dict.items() if v is not None}

        # Convert dict to typed model with validation
        request = convert_with_validation(request_dict, CreateIndexRequest, "create index request")

        # Make HTTP request
        response = self._make_indexes_request(
            "post",
            "/indexes",
            headers={"Content-Type": "application/json"},
            content=create_adapter.to_request(request),
        )

        # Parse and return response
        return parse_response_with_validation(response, create_adapter, "create index response")

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

        Returns detailed information about all indexes, including their schema,
        deployment configuration, and operational status.

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

        Returns:
            Paginator[IndexModel]: Iterable paginator over index descriptions.
            Iterate directly for items, or use .pages() for page-level access.

        Raises:
            APIError: If API request fails

        Note:
            The 2026-01.alpha indexes API currently returns all indexes in a single
            response. Pagination parameters are supported by the Paginator for
            consistency with other list methods and future API enhancements.

        Example - Iterate over indexes:
            >>> for index in pc.indexes.list():
            ...     print(f"{index.name}: {index.status.state}")
            ...     print(f"  Fields: {list(index.schema.fields.keys())}")
            ...     print(f"  Deployment: {index.deployment.deployment_type}")

        Example - Convert to list:
            >>> all_indexes = pc.indexes.list().to_list()
            >>> print(f"Found {len(all_indexes)} indexes")

        Example - Check if index exists:
            >>> existing_indexes = [idx.name for idx in pc.indexes.list()]
            >>> if "my-index" in existing_indexes:
            ...     print("Index exists")

        Example - Limit results:
            >>> for index in pc.indexes.list(limit=5):
            ...     print(index.name)

        Example - Page-level access:
            >>> for page in pc.indexes.list().pages():
            ...     print(f"Retrieved {len(page.items)} indexes")
        """
        from pinecone._internal.adapters.indexes import list_adapter
        from pinecone._internal.validation import (
            parse_response_with_validation,
        )
        from pinecone.models.pagination import Page, Paginator

        def fetch_page(token: str | None) -> Page[IndexModel]:
            """Fetch a page of indexes."""
            # Make HTTP request
            response = self._make_indexes_request("get", "/indexes")

            # Parse response
            result: IndexList = parse_response_with_validation(
                response, list_adapter, "list indexes response"
            )

            # API currently doesn't support pagination, return all items in one page
            return Page(items=result.indexes, pagination_token=None)

        return Paginator(
            fetch_page=fetch_page,
            initial_token=pagination_token,
            limit=limit,
            item_type="IndexModel",
        )

    def describe(self, name: str) -> IndexModel:
        """Get detailed information about a specific index.

        Returns complete index configuration including schema, deployment,
        read capacity, and operational status.

        Args:
            name: Index name

        Returns:
            IndexModel: Full index description

        Raises:
            NotFoundError: If index doesn't exist
            APIError: If API request fails

        Example:
            >>> index = pc.indexes.describe("my-index")
            >>> print(f"Name: {index.name}")
            >>> print(f"Host: {index.host}")
            >>> print(f"Status: {index.status.state}")
            >>> print(f"Fields: {list(index.schema.fields.keys())}")
            >>>
            >>> # Check specific field
            >>> if "embedding" in index.schema.fields:
            ...     field = index.schema.fields["embedding"]
            ...     print(f"Vector dimension: {field.dimension}")
        """
        from pinecone._internal.adapters.indexes import describe_adapter
        from pinecone._internal.validation import (
            parse_response_with_validation,
        )

        # Make HTTP request
        response = self._make_indexes_request("get", f"/indexes/{name}")

        # Parse and return response
        return parse_response_with_validation(response, describe_adapter, "describe index response")

    def delete(self, name: str) -> None:
        """Delete an index.

        Permanently deletes an index and all its data. This operation cannot
        be undone. If deletion protection is enabled on the index, this will
        fail.

        Args:
            name: Index name to delete

        Raises:
            NotFoundError: If index doesn't exist
            ForbiddenError: If deletion protection is enabled
            APIError: If API request fails

        Example:
            >>> # Delete an index
            >>> pc.indexes.delete("my-index")
            >>>
            >>> # If deletion protection is enabled, disable it first
            >>> pc.indexes.configure("my-index", deletion_protection="disabled")
            >>> pc.indexes.delete("my-index")
        """
        # Make HTTP request
        self._make_indexes_request("delete", f"/indexes/{name}")

    def configure(
        self,
        name: str,
        *,
        schema: dict[str, Any] | None = None,
        deployment: dict[str, Any] | None = None,
        read_capacity: dict[str, Any] | None = None,
        deletion_protection: str | None = None,
        tags: dict[str, str] | None = None,
    ) -> IndexModel:
        """Update index configuration.

        The 2026-01.alpha API supports partial updates to schema, read capacity,
        deployment, deletion protection, and tags. Only provided fields are updated.

        Schema updates can add new fields but cannot remove or modify existing fields.
        Deployment updates are limited to scaling operations (replicas, shards, pod type).

        Args:
            name: Index name to configure
            schema: Updated schema (can add new fields)
            deployment: Updated deployment configuration (limited operations)
            read_capacity: Updated read capacity specification
            deletion_protection: "enabled" or "disabled"
            tags: Updated tags (replaces all existing tags)

        Returns:
            IndexModel: Updated index description

        Raises:
            NotFoundError: If index doesn't exist
            BadRequestError: If update is invalid
            APIError: If API request fails

        Example - Add field to schema:
            >>> index = pc.indexes.configure(
            ...     "my-index",
            ...     schema={
            ...         "fields": {
            ...             "embedding": {  # Existing field (must include all existing fields)
            ...                 "type": "dense_vector",
            ...                 "dimension": 1536,
            ...                 "metric": "cosine",
            ...             },
            ...             "description": {  # NEW field
            ...                 "type": "string",
            ...                 "full_text_searchable": True,
            ...             },
            ...         }
            ...     },
            ... )

        Example - Update read capacity:
            >>> index = pc.indexes.configure(
            ...     "my-index",
            ...     read_capacity={
            ...         "mode": "Dedicated",
            ...         "node_type": "t1",
            ...         "scaling": {
            ...             "strategy": "Manual",
            ...             "replicas": 4,  # Scale up
            ...             "shards": 2,
            ...         },
            ...     },
            ... )

        Example - Update tags:
            >>> index = pc.indexes.configure(
            ...     "my-index", tags={"environment": "staging", "version": "v2"}
            ... )

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

        Example - Scale pod deployment:
            >>> index = pc.indexes.configure(
            ...     "my-index",
            ...     deployment={
            ...         "deployment_type": "pod",
            ...         "pod_type": "p1.x2",  # Upgrade pod type
            ...         "replicas": 4,  # Increase replicas
            ...         "shards": 2,
            ...     },
            ... )
        """
        from pinecone._internal.adapters.indexes import configure_adapter
        from pinecone._internal.validation import (
            convert_with_validation,
            parse_response_with_validation,
        )
        from pinecone.models.indexes import ConfigureIndexRequest

        # Build request dict then convert to typed model
        # This allows users to pass dicts while ensuring type safety
        request_dict = {
            "schema": schema,
            "deployment": deployment,
            "read_capacity": read_capacity,
            "deletion_protection": deletion_protection,
            "tags": tags,
        }

        # Convert dict to typed model with validation
        request = convert_with_validation(
            request_dict, ConfigureIndexRequest, "configure index request"
        )

        # Make HTTP request
        response = self._make_indexes_request(
            "patch",
            f"/indexes/{name}",
            headers={"Content-Type": "application/json"},
            content=configure_adapter.to_request(request),
        )

        # Parse and return response
        return parse_response_with_validation(
            response, configure_adapter, "configure index response"
        )

    def __repr__(self) -> str:
        """Return string representation."""
        return f"Indexes(base_url={self._base_url!r})"


__all__ = ["Indexes"]
