from __future__ import annotations

from contextlib import suppress
from typing import Any, Generic, Optional, TypeVar

from aiocache import Cache
from aiocache.serializers import BaseSerializer
from pydantic.type_adapter import TypeAdapter
from yarl import URL

T = TypeVar("T")

with suppress(ImportError):
	import ormsgpack


class OrmsgpackSerializer(BaseSerializer, Generic[T]):
	DEFAULT_ENCODING = None

	def __init__(self, schema: TypeAdapter[T]):
		super().__init__()
		self.schema = schema

	def dumps(self, value: T) -> bytes:
		return ormsgpack.packb(self.schema.dump_python(value))

	def loads(self, value: Optional[bytes]) -> T | None:
		if value is None:
			return None
		data = ormsgpack.unpackb(value)
		return self.schema.validate_python(data)


def make_cache(schema: TypeAdapter[T], url: URL, namespace: str = "") -> Cache[T]:
	backend = Cache.get_scheme_class(url.scheme)
	if backend == Cache.MEMORY:
		return Cache(backend)

	kwargs: dict[str, Any] = dict(url.query)

	if url.path:
		kwargs.update(backend.parse_uri_path(url.path))

	if url.host:
		kwargs["endpoint"] = url.host

	if url.port:
		kwargs["port"] = url.port

	if url.password:
		kwargs["password"] = url.password

	namespace = ":".join(s for s in [kwargs.pop("namespace", ""), namespace] if s)

	serializer = OrmsgpackSerializer(schema)

	return Cache(backend, serializer=serializer, namespace=namespace, **kwargs)