"""Async collections namespace implementation for managing collection resources.

This module provides async 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._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 AsyncPaginator, Page


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


logger = get_logger(__name__)


class AsyncCollections(InteractiveIntrospectionMixin):
    """Async implementation of collection management operations.

    This class provides async CRUD operations for managing Pinecone collections,
    enabling concurrent collection management operations. 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: Async HTTP client for making API requests
        base_url: Base URL for the Collections API

    Example:
        >>> from pinecone import AsyncPinecone
        >>> import asyncio
        >>>
        >>> async def main():
        ...     async with AsyncPinecone(api_key="...") as pc:
        ...         # List collections
        ...         async for coll in pc.collections.list():
        ...             print(f"{coll.name}: {coll.status}")
        ...
        ...         # Create collection from index
        ...         await pc.collections.create(name="my-backup", source="my-index")
        ...
        ...         # Poll until ready
        ...         while True:
        ...             info = await pc.collections.describe("my-backup")
        ...             if info.status == "Ready":
        ...                 break
        ...             await asyncio.sleep(10)
        ...
        ...         # Delete collection
        ...         await pc.collections.delete("my-backup")
        >>>
        >>> asyncio.run(main())

    Example with concurrent operations:
        >>> async def manage_collections(pc, collection_names):
        ...     tasks = [pc.collections.describe(name) for name in collection_names]
        ...     return await asyncio.gather(*tasks)
    """

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

        Args:
            http_client: Async 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,
    ) -> AsyncPaginator[CollectionInfoProtocol]:
        """List all collections asynchronously.

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

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

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

        Example with page-level access:
            >>> async 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,
        )

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

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

                response = await 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 AsyncPaginator(
            fetch_page=fetch_page,
            initial_token=pagination_token,
            limit=limit,
            item_type="CollectionModel",
        )

    async 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:
            >>> await pc.collections.create(name="my-backup", source="my-index")
            >>>
            >>> # Poll until ready
            >>> import asyncio
            >>> while True:
            ...     info = await pc.collections.describe("my-backup")
            ...     if info.status == "Ready":
            ...         break
            ...     await asyncio.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
            await 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

    async 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:
            >>> await 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
            await 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

    async 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 = await 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 = await 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"AsyncCollections(base_url='{self._base_url}')"
