from __future__ import annotations

import asyncio
import os
from typing import Dict

try:
    import redis.asyncio as aioredis  # type: ignore
except Exception:  # pragma: no cover - optional dependency
    aioredis = None  # type: ignore


class _InMemoryCounters:
    def __init__(self):
        self._lock = asyncio.Lock()
        self._counters: Dict[str, int] = {}

    async def incr(self, key: str, by: int = 1) -> int:
        async with self._lock:
            self._counters[key] = self._counters.get(key, 0) + by
            return self._counters[key]

    async def dump(self) -> Dict[str, int]:
        async with self._lock:
            return dict(self._counters)


class Metrics:
    def __init__(self):
        self.redis_url = os.getenv("REDIS_URL")
        self._mem = _InMemoryCounters()
        self._redis = None

    async def _get_redis(self):
        if self._redis is not None:
            return self._redis
        if not self.redis_url or aioredis is None:
            return None
        try:
            self._redis = aioredis.from_url(self.redis_url, decode_responses=True)
        except Exception:
            self._redis = None
        return self._redis

    async def increment(self, key: str, by: int = 1) -> int:
        r = await self._get_redis()
        if r is not None:
            try:
                return int(await r.hincrby("metrics:counters", key, by))
            except Exception:
                pass
        return await self._mem.incr(key, by)

    async def snapshot(self) -> Dict[str, int]:
        r = await self._get_redis()
        if r is not None:
            try:
                data = await r.hgetall("metrics:counters")
                return {k: int(v) for k, v in (data or {}).items()}
            except Exception:
                pass
        return await self._mem.dump()


metrics = Metrics()
