"""
Memcached cache backend using aiomcache.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any, Dict, Optional

from cachestack.base import BaseCache, BackendUnavailableError, CacheError
from cachestack.serializers import BaseSerializer, PickleSerializer


@dataclass
class MemcachedConfig:
    """Configuration for the Memcached backend.

    Attributes:
        host: Memcached server host.
        port: Memcached server port.
        serializer: Serializer instance.
        ttl: Default TTL in seconds.
        namespace: Key prefix.
        silent: Suppress errors if True.
    """
    host: str = "localhost"
    port: int = 11211
    serializer: BaseSerializer = field(default_factory=PickleSerializer)
    ttl: Optional[int] = None
    namespace: str = "cachestack"
    silent: bool = False


class MemcachedBackend(BaseCache):
    """
    Async Memcached cache backend using aiomcache.

    Args:
        config: MemcachedConfig instance.
    """

    def __init__(self, config: Optional[MemcachedConfig] = None) -> None:
        self._config = config or MemcachedConfig()
        self._client: Any = None
        self._hits = 0
        self._misses = 0
        self._errors = 0

    def _prefixed(self, key: str) -> bytes:
        return f"{self._config.namespace}:{key}".encode()

    async def _get_client(self) -> Any:
        if self._client is None:
            try:
                import aiomcache
                self._client = aiomcache.Client(
                    self._config.host,
                    self._config.port,
                )
            except ImportError as e:
                raise ImportError(
                    "aiomcache is required for MemcachedBackend. "
                    "Install it with: pip install cachestack[memcached]"
                ) from e
            except Exception as e:
                raise BackendUnavailableError(
                    f"Failed to connect to Memcached: {e}"
                ) from e
        return self._client

    async def get(self, key: str) -> Optional[Any]:
        """Retrieve a value by key."""
        try:
            client = await self._get_client()
            raw = await client.get(self._prefixed(key))
            if raw is None:
                self._misses += 1
                return None
            self._hits += 1
            return self._config.serializer.loads(raw)
        except (BackendUnavailableError, ImportError):
            raise
        except Exception as e:
            self._errors += 1
            if self._config.silent:
                return None
            raise CacheError(f"MemcachedBackend get failed: {e}") from e

    async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> None:
        """Store a value by key."""
        try:
            client = await self._get_client()
            raw = self._config.serializer.dumps(value)
            effective_ttl = ttl or self._config.ttl or 0
            await client.set(self._prefixed(key), raw, exptime=effective_ttl)
        except (BackendUnavailableError, ImportError):
            raise
        except Exception as e:
            self._errors += 1
            if self._config.silent:
                return
            raise CacheError(f"MemcachedBackend set failed: {e}") from e

    async def delete(self, key: str) -> None:
        """Delete a key."""
        try:
            client = await self._get_client()
            await client.delete(self._prefixed(key))
        except (BackendUnavailableError, ImportError):
            raise
        except Exception as e:
            self._errors += 1
            if self._config.silent:
                return
            raise CacheError(f"MemcachedBackend delete failed: {e}") from e

    async def exists(self, key: str) -> bool:
        """Check if a key exists."""
        return (await self.get(key)) is not None

    async def clear(self) -> None:
        """Flush all Memcached entries (note: flushes entire server)."""
        try:
            client = await self._get_client()
            await client.flush_all()
        except (BackendUnavailableError, ImportError):
            raise
        except Exception as e:
            self._errors += 1
            if self._config.silent:
                return
            raise CacheError(f"MemcachedBackend clear failed: {e}") from e

    async def close(self) -> None:
        """Close the Memcached connection."""
        if self._client:
            await self._client.close()
            self._client = None

    async def stats(self) -> Dict[str, Any]:
        """Return cache statistics."""
        return {
            "backend": "memcached",
            "host": self._config.host,
            "port": self._config.port,
            "namespace": self._config.namespace,
            "hits": self._hits,
            "misses": self._misses,
            "errors": self._errors,
        }
