"""Collections namespace implementation for managing collection resources.

This module provides collection management operations (CRUD) for Pinecone collections.
Collections are static copies of indexes useful for backups and cloning.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any, cast

import msgspec
import orjson

from pinecone.__interface__.collections import CollectionNamespaceProtocol
from pinecone._internal.adapters.collections_adapter import CollectionsAdapter
from pinecone._internal.constants import CONTROL_PLANE_API_VERSION
from pinecone._internal.introspection import InteractiveIntrospectionMixin
from pinecone._internal.logging import get_logger
from pinecone._internal.validation import require_non_empty
from pinecone.models.collections import CollectionModel, CreateCollectionRequest
from pinecone.models.pagination import Page, Paginator


if TYPE_CHECKING:
    from pinecone.__interface__.collections import CollectionInfoProtocol
    from pinecone._internal.http import HTTPClient

logger = get_logger(__name__)


class Collections(InteractiveIntrospectionMixin, CollectionNamespaceProtocol):
    """Implementation of collection management operations.

    This class provides CRUD operations for managing Pinecone collections.
    Collections are static, non-queryable copies of pod-based indexes that only
    consume storage (no compute costs). They are useful for backups, cloning,
    and temporarily shutting down indexes.

    Note:
        - Serverless indexes do not support collections. Only pod-based indexes
          can create collections.
        - Collections cannot be moved between projects once created.
        - Collection creation is asynchronous and may take time to complete
          (typically ~10 minutes for p1/s1 pods, potentially hours for p2 pods
          with millions of vectors).

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

    Example:
        >>> from pinecone import Pinecone
        >>> pc = Pinecone(api_key="...")
        >>> collections = pc.collections
        >>>
        >>> # List collections
        >>> for coll in collections.list():
        ...     print(f"{coll.name}: {coll.status}")
        >>>
        >>> # Create collection from index
        >>> collections.create(name="my-backup", source="my-index")
        >>>
        >>> # Poll until collection is ready
        >>> import time
        >>> while True:
        ...     info = collections.describe("my-backup")
        ...     if info.status == "Ready":
        ...         break
        ...     print(f"Status: {info.status}")
        ...     time.sleep(10)
        >>>
        >>> # Delete collection
        >>> collections.delete("my-backup")
    """

    def __init__(
        self,
        http_client: HTTPClient,
        base_url: str = "https://api.pinecone.io",
    ):
        """Initialize Collections namespace.

        Args:
            http_client: HTTP client for making API requests
            base_url: Base URL for the Collections API. Defaults to production endpoint.
        """
        self._http = http_client
        self._base_url = base_url
        self._adapter = CollectionsAdapter()
        self._headers = {"X-Pinecone-Api-Version": CONTROL_PLANE_API_VERSION}

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

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

        Returns:
            Paginator[CollectionInfoProtocol]: Iterator over CollectionInfo objects.
            Iterate directly for items, or use .pages() for page-level access.

        Example:
            >>> for coll in pc.collections.list():
            ...     print(f"{coll.name}: {coll.vector_count} vectors")

        Example with page-level access:
            >>> for page in pc.collections.list().pages():
            ...     print(f"Got {len(page.items)} collections")
        """
        # Validate inputs
        if limit is not None and limit <= 0:
            raise ValueError("limit must be a positive integer")

        logger.info(
            "Listing collections",
            limit=limit,
            has_pagination_token=pagination_token is not None,
        )

        def fetch_page(token: str | None) -> Page[CollectionInfoProtocol]:
            """Fetch a page of collections."""
            logger.info("Fetching collections page", pagination_token=token)

            try:
                # Build query params including pagination token
                params = {}
                if token:
                    params["pagination_token"] = token

                response = self._http.get(
                    f"{self._base_url}/collections",
                    headers=self._headers,
                    params=params,
                )

                data = orjson.loads(response.content)
                result = self._adapter.to_collection_list(data)

                logger.debug(
                    "Successfully fetched collections",
                    collection_count=len(result.collections),
                )

                # Extract next token from response (API may not support pagination yet)
                pagination = data.get("pagination")
                next_token = pagination.get("next") if pagination else None
                return Page(
                    items=cast("list[Any]", result.collections),
                    pagination_token=next_token,
                )

            except Exception:
                logger.exception("Failed to list collections")
                raise

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

    def create(
        self,
        name: str,
        *,
        source: str,
        timeout: int | None = None,
    ) -> None:
        """Create a collection from a pod-based index.

        This operation is asynchronous. The collection is created in the background
        and may take time to complete (typically ~10 minutes for p1/s1 pods,
        potentially several hours for p2 pods with millions of vectors).

        Use describe() to poll the collection status until it shows "Ready".

        Args:
            name: Collection name (1-45 chars, lowercase alphanumeric or '-').
            source: Source pod-based index name to create collection from.
            timeout: Optional timeout in seconds for the API request (not the
                collection creation itself).

        Raises:
            ValueError: If name or source is empty, or timeout is not positive.
            APIError: If creation fails.
            NotFoundError: If source index not found.

        Example:
            >>> pc.collections.create(name="my-backup", source="my-index")
            >>>
            >>> # Poll until ready
            >>> import time
            >>> while True:
            ...     info = pc.collections.describe("my-backup")
            ...     if info.status == "Ready":
            ...         break
            ...     time.sleep(10)
        """
        # Validate inputs
        require_non_empty("name", name)
        require_non_empty("source", source)
        if timeout is not None and timeout <= 0:
            raise ValueError("timeout must be a positive integer")

        logger.info(
            "Creating collection",
            collection_name=name,
            source_index=source,
            timeout=timeout,
        )

        try:
            # Build request
            request = CreateCollectionRequest(name=name, source=source)

            # Make HTTP request
            self._http.post(
                f"{self._base_url}/collections",
                content=msgspec.json.encode(request),
                headers={**self._headers, "Content-Type": "application/json"},
                timeout=timeout,
            )

            logger.info("Collection created successfully", collection_name=name)
            # Note: API returns 201 with CollectionModel, but we return None per protocol

        except Exception:
            logger.exception(
                "Failed to create collection", collection_name=name, source_index=source
            )
            raise

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

        Args:
            name: Collection name to delete.
            timeout: Optional timeout in seconds for the API request.

        Raises:
            ValueError: If name is empty or timeout is not positive.
            NotFoundError: If collection not found.
            APIError: If deletion fails.

        Example:
            >>> pc.collections.delete("my-backup")
        """
        # Validate inputs
        require_non_empty("name", name)
        if timeout is not None and timeout <= 0:
            raise ValueError("timeout must be a positive integer")

        logger.info(
            "Deleting collection",
            collection_name=name,
            timeout=timeout,
        )

        try:
            # Make HTTP request
            self._http.delete(
                f"{self._base_url}/collections/{name}",
                headers=self._headers,
                timeout=timeout,
            )

            logger.info("Collection deletion initiated", collection_name=name)
            # Note: API returns 202 (async deletion), no response body

        except Exception:
            logger.exception("Failed to delete collection", collection_name=name)
            raise

    def describe(self, name: str) -> CollectionInfoProtocol:
        """Get collection details.

        Args:
            name: Collection name.

        Returns:
            CollectionInfoProtocol with metadata including name, size, status,
            dimension, vector_count, and environment.

        Raises:
            ValueError: If name is empty.
            NotFoundError: If collection not found.
            APIError: If operation fails.

        Example:
            >>> info = pc.collections.describe("my-backup")
            >>> print(f"Status: {info.status}")
            >>> print(f"Vectors: {info.vector_count}")
        """
        # Validate inputs
        require_non_empty("name", name)

        logger.info(
            "Describing collection",
            collection_name=name,
        )

        try:
            # Make HTTP request
            response = self._http.get(
                f"{self._base_url}/collections/{name}",
                headers=self._headers,
            )

            # Transform response
            data = orjson.loads(response.content)
            result: CollectionModel = self._adapter.to_collection_model(data)

            logger.debug(
                "Successfully described collection", collection_name=name, status=result.status
            )

            return result  # noqa: TRY300

        except Exception:
            logger.exception("Failed to describe collection", collection_name=name)
            raise

    def __repr__(self) -> str:
        """Return string representation of collections namespace."""
        return f"Collections(base_url='{self._base_url}')"
