"""Batch and transaction operations."""

from __future__ import annotations

from typing import Any

from pydynox._internal._logging import _log_debug


class BatchOperations:
    """Batch and transaction operations."""

    # ========== BATCH WRITE (ASYNC - default, no prefix) ==========

    async def batch_write(
        self,
        table: str,
        put_items: list[dict[str, Any]] | None = None,
        delete_keys: list[dict[str, Any]] | None = None,
    ) -> None:
        """Async batch write items to a DynamoDB table.

        Writes multiple items in a single request. Handles:
        - Splitting requests to respect the 25-item limit per batch
        - Retrying unprocessed items with exponential backoff
        """
        put_count = len(put_items) if put_items else 0
        delete_count = len(delete_keys) if delete_keys else 0
        _log_debug(
            "batch_write", f'Batch writing to "{table}" ({put_count} puts, {delete_count} deletes)'
        )
        self._acquire_wcu(float(put_count + delete_count))  # type: ignore[attr-defined]
        await self._client.batch_write(  # type: ignore[attr-defined]
            table,
            put_items or [],
            delete_keys or [],
        )

    # ========== BATCH GET (ASYNC - default, no prefix) ==========

    async def batch_get(
        self,
        table: str,
        keys: list[dict[str, Any]],
    ) -> list[dict[str, Any]]:
        """Async batch get items from a DynamoDB table.

        Gets multiple items in a single request. Handles:
        - Splitting requests to respect the 100-item limit per batch
        - Retrying unprocessed keys with exponential backoff
        - Combining results from multiple requests
        """
        _log_debug("batch_get", f'Batch getting from "{table}" ({len(keys)} keys)')
        self._acquire_rcu(float(len(keys)))  # type: ignore[attr-defined]
        return await self._client.batch_get(table, keys)  # type: ignore[attr-defined, no-any-return]

    # ========== BATCH WRITE (SYNC - with sync_ prefix) ==========

    def sync_batch_write(
        self,
        table: str,
        put_items: list[dict[str, Any]] | None = None,
        delete_keys: list[dict[str, Any]] | None = None,
    ) -> None:
        """Sync batch write items to a DynamoDB table.

        Writes multiple items in a single request. Handles:
        - Splitting requests to respect the 25-item limit per batch
        - Retrying unprocessed items with exponential backoff
        """
        put_count = len(put_items) if put_items else 0
        delete_count = len(delete_keys) if delete_keys else 0
        _log_debug(
            "sync_batch_write",
            f'Batch writing to "{table}" ({put_count} puts, {delete_count} deletes)',
        )
        self._acquire_wcu(float(put_count + delete_count))  # type: ignore[attr-defined]
        self._client.sync_batch_write(  # type: ignore[attr-defined]
            table,
            put_items or [],
            delete_keys or [],
        )

    # ========== BATCH GET (SYNC - with sync_ prefix) ==========

    def sync_batch_get(
        self,
        table: str,
        keys: list[dict[str, Any]],
    ) -> list[dict[str, Any]]:
        """Sync batch get items from a DynamoDB table.

        Gets multiple items in a single request. Handles:
        - Splitting requests to respect the 100-item limit per batch
        - Retrying unprocessed keys with exponential backoff
        - Combining results from multiple requests
        """
        _log_debug("sync_batch_get", f'Batch getting from "{table}" ({len(keys)} keys)')
        self._acquire_rcu(float(len(keys)))  # type: ignore[attr-defined]
        return self._client.sync_batch_get(table, keys)  # type: ignore[attr-defined, no-any-return]

    # ========== TRANSACT WRITE (SYNC) ==========

    def sync_transact_write(self, operations: list[dict[str, Any]]) -> None:
        """Sync version of transact_write. Blocks until complete.

        All operations run atomically. Either all succeed or all fail.
        """
        _log_debug("sync_transact_write", f"Running transaction ({len(operations)} operations)")
        self._client.sync_transact_write(operations)  # type: ignore[attr-defined]

    # ========== TRANSACT GET (SYNC) ==========

    def sync_transact_get(self, gets: list[dict[str, Any]]) -> list[dict[str, Any] | None]:
        """Sync version of transact_get. Blocks until complete.

        Reads multiple items atomically. Either all reads succeed or all fail.
        Use this when you need a consistent snapshot of multiple items.

        Args:
            gets: List of get dicts, each with:
                - table: Table name
                - key: Key dict (pk and optional sk)
                - projection_expression: Optional projection (saves RCU)
                - expression_attribute_names: Optional name placeholders

        Returns:
            List of items (or None for items that don't exist).

        Example:
            items = client.sync_transact_get([
                {"table": "users", "key": {"pk": "USER#1"}},
                {"table": "orders", "key": {"pk": "ORDER#1", "sk": "ITEM#1"}},
            ])
        """
        _log_debug("sync_transact_get", f"Transact getting ({len(gets)} keys)")
        return self._client.sync_transact_get(gets)  # type: ignore[attr-defined, no-any-return]

    # ========== TRANSACT WRITE (ASYNC - default) ==========

    async def transact_write(self, operations: list[dict[str, Any]]) -> None:
        """Execute a transactional write operation.

        All operations run atomically. Either all succeed or all fail.
        """
        _log_debug("transact_write", f"Running transaction ({len(operations)} operations)")
        await self._client.transact_write(operations)  # type: ignore[attr-defined]

    # ========== TRANSACT GET (ASYNC - default) ==========

    async def transact_get(self, gets: list[dict[str, Any]]) -> list[dict[str, Any] | None]:
        """Execute a transactional get operation.

        Reads multiple items atomically. Either all reads succeed or all fail.

        Args:
            gets: List of get dicts (same format as sync_transact_get).

        Returns:
            List of items (or None for items that don't exist).
        """
        _log_debug("transact_get", f"Transact getting ({len(gets)} keys)")
        return await self._client.transact_get(gets)  # type: ignore[attr-defined, no-any-return]
