import uuid from datetime import datetime, timezone from sqlalchemy import func, select, cast, Integer from sqlalchemy.ext.asyncio import AsyncSession from app.models.chat import Chat from app.models.message import Message from app.models.user import User from app.services.setting_service import get_setting_value def _today_start() -> datetime: return datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0) async def get_user_message_count_today(db: AsyncSession, user_id: uuid.UUID) -> int: result = await db.scalar( select(func.count()) .select_from(Message) .join(Chat, Message.chat_id == Chat.id) .where(Chat.user_id == user_id, Message.role == "user", Message.created_at >= _today_start()) ) return result or 0 async def get_user_token_count_today(db: AsyncSession, user_id: uuid.UUID) -> int: """Sum input_tokens + output_tokens from assistant message metadata today.""" result = await db.execute( select(Message.metadata_) .join(Chat, Message.chat_id == Chat.id) .where( Chat.user_id == user_id, Message.role == "assistant", Message.created_at >= _today_start(), Message.metadata_.isnot(None), ) ) total = 0 for (meta,) in result: if meta and isinstance(meta, dict): total += int(meta.get("input_tokens", 0)) + int(meta.get("output_tokens", 0)) return total async def get_user_daily_limits(db: AsyncSession, user: User) -> dict: msg_limit = user.max_ai_messages_per_day if msg_limit is None: msg_limit = int(await get_setting_value(db, "default_max_ai_messages_per_day", 100)) token_limit = user.max_ai_tokens_per_day if token_limit is None: token_limit = int(await get_setting_value(db, "default_max_ai_tokens_per_day", 500000)) return {"message_limit": msg_limit, "token_limit": token_limit} async def check_user_quota(db: AsyncSession, user: User) -> dict: """Check quota and return status. Raises nothing — caller decides.""" limits = await get_user_daily_limits(db, user) msg_count = await get_user_message_count_today(db, user.id) token_count = await get_user_token_count_today(db, user.id) tomorrow = _today_start().replace(day=_today_start().day + 1) return { "messages_used": msg_count, "message_limit": limits["message_limit"], "tokens_used": token_count, "token_limit": limits["token_limit"], "messages_exceeded": msg_count >= limits["message_limit"], "tokens_exceeded": token_count >= limits["token_limit"], "resets_at": tomorrow.isoformat(), }