"""
Message Limit Service
Price-based daily message limits for guests and users.
"""
from __future__ import annotations
import logging
from datetime import datetime, timedelta
from common.cache import redis_cache
from config import RATE_LIMIT_GUEST, RATE_LIMIT_USER

logger = logging.getLogger(__name__)

MESSAGE_COUNT_PREFIX = "msg_limit:"

class MessageLimitService:
    _instance: MessageLimitService | None = None
    _initialized: bool = False

    def __new__(cls) -> MessageLimitService:
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self) -> None:
        if MessageLimitService._initialized:
            return
        self._memory_storage: dict[str, dict[str, int]] = {}
        self._memory_date: str = ""
        self.guest_limit = RATE_LIMIT_GUEST
        self.user_limit = RATE_LIMIT_USER
        MessageLimitService._initialized = True

    def _get_today_key(self) -> str:
        return datetime.now().strftime("%Y-%m-%d")

    def _get_redis_key(self, identity_key: str) -> str:
        today = self._get_today_key()
        return f"{MESSAGE_COUNT_PREFIX}{today}:{identity_key}"

    def _get_seconds_until_midnight(self) -> int:
        now = datetime.now()
        tomorrow = now.date() + timedelta(days=1)
        midnight = datetime.combine(tomorrow, datetime.min.time())
        return int((midnight - now).total_seconds())

    def _reset_memory_if_new_day(self) -> None:
        today = self._get_today_key()
        if self._memory_date != today:
            self._memory_storage.clear()
            self._memory_date = today

    async def _get_counts_from_redis(self, identity_key: str) -> dict[str, int] | None:
        try:
            client = redis_cache.get_client()
            if not client: return None
            redis_key = self._get_redis_key(identity_key)
            data = await client.hgetall(redis_key)
            counts = {"guest": 0, "user": 0}
            if data:
                counts["guest"] = int(data.get("guest") or data.get(b"guest") or 0)
                counts["user"] = int(data.get("user") or data.get(b"user") or 0)
            return counts
        except Exception as e:
            logger.warning(f"Redis get counts error: {e}")
            return None

    async def _increment_in_redis(self, identity_key: str, field: str) -> int | None:
        try:
            client = redis_cache.get_client()
            if not client: return None
            redis_key = self._get_redis_key(identity_key)
            new_val = await client.hincrby(redis_key, field, 1)
            if new_val == 1:
                await client.expire(redis_key, 48 * 3600)
            return new_val
        except Exception as e:
            logger.warning(f"Redis increment error: {e}")
            return None

    async def check_limit(self, identity_key: str, is_authenticated: bool) -> tuple[bool, dict]:
        counts = await self._get_counts_from_redis(identity_key)
        if counts is None:
            self._reset_memory_if_new_day()
            if identity_key not in self._memory_storage:
                self._memory_storage[identity_key] = {"guest": 0, "user": 0}
            counts = self._memory_storage[identity_key]

        guest_used = counts.get("guest", 0)
        user_used = counts.get("user", 0)
        total_used = guest_used + user_used
        
        can_send = True
        limit_display = self.user_limit if is_authenticated else self.guest_limit
        message = ""
        require_login = False
        
        if total_used >= self.user_limit:
            can_send = False
            message = f"Limit reached: {self.user_limit} msgs/day."
        elif not is_authenticated and guest_used >= self.guest_limit:
            can_send = False
            require_login = True
            message = f"Guest limit reached ({self.guest_limit}). Login to continue."

        info = {
            "limit": limit_display,
            "used": user_used if is_authenticated else guest_used,
            "guest_used": guest_used,
            "user_used": user_used,
            "total_used": total_used,
            "remaining": max(0, limit_display - (user_used if is_authenticated else guest_used)),
            "reset_seconds": self._get_seconds_until_midnight(),
            "require_login": require_login,
            "message": message
        }
        return can_send, info

    async def increment(self, identity_key: str, is_authenticated: bool) -> dict:
        field = "user" if is_authenticated else "guest"
        new_val = await self._increment_in_redis(identity_key, field)
        if new_val is None:
            self._reset_memory_if_new_day()
            if identity_key not in self._memory_storage:
                self._memory_storage[identity_key] = {"guest": 0, "user": 0}
            self._memory_storage[identity_key][field] += 1
        
        _, info = await self.check_limit(identity_key, is_authenticated)
        return info

message_limit_service = MessageLimitService()
