"""Reminder business logic service.""" from datetime import datetime, time from typing import List, Optional from sqlalchemy.ext.asyncio import AsyncSession from bot.db.models import Reminder from bot.db.operations import ( create_reminder, get_reminder_by_id, get_user_reminders, update_reminder, delete_reminder, mark_reminder_done, snooze_reminder, toggle_reminder_active, ) from bot.services.time_service import get_time_service from bot.logging_config import get_logger logger = get_logger(__name__) class RemindersService: """Service for reminder-related operations.""" def __init__(self): """Initialize RemindersService.""" self.time_service = get_time_service() async def create_new_reminder( self, session: AsyncSession, user_id: int, text: str, days_interval: int, time_of_day: time, ) -> Reminder: """ Create a new reminder. Args: session: Database session user_id: User's database ID text: Reminder text days_interval: Days between reminders time_of_day: Time of day for reminder Returns: Created Reminder instance """ # Calculate next run time next_run_at = self.time_service.calculate_next_run_time( time_of_day=time_of_day, days_interval=days_interval, ) # Create reminder reminder = await create_reminder( session=session, user_id=user_id, text=text, days_interval=days_interval, time_of_day=time_of_day, next_run_at=next_run_at, ) logger.info( f"Created reminder {reminder.id} for user {user_id}, " f"next run at {next_run_at}" ) return reminder async def get_reminder( self, session: AsyncSession, reminder_id: int, ) -> Optional[Reminder]: """ Get reminder by ID. Args: session: Database session reminder_id: Reminder ID Returns: Reminder instance or None """ return await get_reminder_by_id(session, reminder_id) async def get_user_all_reminders( self, session: AsyncSession, user_id: int, active_only: bool = False, ) -> List[Reminder]: """ Get all reminders for a user. Args: session: Database session user_id: User's database ID active_only: Return only active reminders Returns: List of Reminder instances """ return await get_user_reminders(session, user_id, active_only) async def update_reminder_text( self, session: AsyncSession, reminder_id: int, new_text: str, ) -> Optional[Reminder]: """ Update reminder text. Args: session: Database session reminder_id: Reminder ID new_text: New reminder text Returns: Updated Reminder instance or None """ return await update_reminder(session, reminder_id, text=new_text) async def update_reminder_interval( self, session: AsyncSession, reminder_id: int, new_days_interval: int, ) -> Optional[Reminder]: """ Update reminder interval. Args: session: Database session reminder_id: Reminder ID new_days_interval: New days interval Returns: Updated Reminder instance or None """ reminder = await get_reminder_by_id(session, reminder_id) if not reminder: return None # Recalculate next_run_at with new interval next_run_at = self.time_service.calculate_next_run_time( time_of_day=reminder.time_of_day, days_interval=new_days_interval, ) return await update_reminder( session, reminder_id, days_interval=new_days_interval, next_run_at=next_run_at, ) async def update_reminder_time( self, session: AsyncSession, reminder_id: int, new_time_of_day: time, ) -> Optional[Reminder]: """ Update reminder time. Args: session: Database session reminder_id: Reminder ID new_time_of_day: New time of day Returns: Updated Reminder instance or None """ reminder = await get_reminder_by_id(session, reminder_id) if not reminder: return None # Recalculate next_run_at with new time next_run_at = self.time_service.calculate_next_run_time( time_of_day=new_time_of_day, days_interval=reminder.days_interval, ) return await update_reminder( session, reminder_id, time_of_day=new_time_of_day, next_run_at=next_run_at, ) async def delete_reminder_by_id( self, session: AsyncSession, reminder_id: int, ) -> bool: """ Delete a reminder. Args: session: Database session reminder_id: Reminder ID Returns: True if deleted, False if not found """ return await delete_reminder(session, reminder_id) async def mark_as_done( self, session: AsyncSession, reminder_id: int, ) -> Optional[Reminder]: """ Mark reminder as done and schedule next occurrence. Args: session: Database session reminder_id: Reminder ID Returns: Updated Reminder instance or None """ reminder = await get_reminder_by_id(session, reminder_id) if not reminder: return None # Calculate next occurrence next_run_at = self.time_service.calculate_next_occurrence( current_run=reminder.next_run_at, days_interval=reminder.days_interval, ) return await mark_reminder_done(session, reminder_id, next_run_at) async def snooze( self, session: AsyncSession, reminder_id: int, hours: int, ) -> Optional[Reminder]: """ Snooze reminder for specified hours. Args: session: Database session reminder_id: Reminder ID hours: Hours to snooze Returns: Updated Reminder instance or None """ now = self.time_service.get_now() next_run_at = self.time_service.add_hours(now, hours) return await snooze_reminder(session, reminder_id, next_run_at) async def pause_reminder( self, session: AsyncSession, reminder_id: int, ) -> Optional[Reminder]: """ Pause (deactivate) a reminder. Args: session: Database session reminder_id: Reminder ID Returns: Updated Reminder instance or None """ return await toggle_reminder_active(session, reminder_id, is_active=False) async def resume_reminder( self, session: AsyncSession, reminder_id: int, ) -> Optional[Reminder]: """ Resume (activate) a reminder. Args: session: Database session reminder_id: Reminder ID Returns: Updated Reminder instance or None """ reminder = await get_reminder_by_id(session, reminder_id) if not reminder: return None # Recalculate next_run_at from now next_run_at = self.time_service.calculate_next_run_time( time_of_day=reminder.time_of_day, days_interval=reminder.days_interval, ) # Update and activate await update_reminder(session, reminder_id, next_run_at=next_run_at) return await toggle_reminder_active(session, reminder_id, is_active=True)