"""
Session idle timer management for Claude Code Telegram bot.

This module provides the SessionIdleTimer class that manages per-session idle
timeouts using asyncio. Each session has its own timer that fires a callback
after a configurable timeout period. Timers reset on activity and can be
cancelled on session shutdown/archive.

Example:
    async def handle_timeout(session_name: str):
        print(f"Session {session_name} timed out")

    timer = SessionIdleTimer("my-session", timeout_seconds=600, on_timeout=handle_timeout)
    timer.reset()  # Start the timer
    # ... activity occurs ...
    timer.reset()  # Reset timer on activity
    # ... no activity for 600 seconds ...
    # handle_timeout("my-session") called automatically
"""

import asyncio
import logging
from datetime import datetime, timezone
from typing import Awaitable, Callable, Optional

logger = logging.getLogger(__name__)


class SessionIdleTimer:
    """
    Manages idle timeout for a single session.

    Provides per-session timeout detection with automatic callback firing
    after inactivity. Timer can be reset on activity and cancelled on shutdown.

    Attributes:
        session_name: Name of the session this timer tracks
        timeout_seconds: Idle timeout in seconds
        on_timeout: Async callback to invoke when timeout fires
        last_activity: UTC timestamp of last activity
        seconds_since_activity: Float seconds since last activity
    """

    def __init__(
        self,
        session_name: str,
        timeout_seconds: int,
        on_timeout: Callable[[str], Awaitable[None]]
    ):
        """
        Initialize SessionIdleTimer.

        Args:
            session_name: Name of the session to track
            timeout_seconds: Idle timeout in seconds
            on_timeout: Async callback(session_name) to invoke when timeout fires
        """
        self.session_name = session_name
        self.timeout_seconds = timeout_seconds
        self.on_timeout = on_timeout

        self._timer_task: Optional[asyncio.Task] = None
        self._last_activity = datetime.now(timezone.utc)

        logger.debug(
            f"SessionIdleTimer initialized: session={session_name}, "
            f"timeout={timeout_seconds}s"
        )

    def reset(self) -> None:
        """
        Reset the idle timer.

        Updates last activity timestamp to now, cancels any existing timer task,
        and creates a new background task that will fire the timeout callback
        after timeout_seconds of inactivity.

        Call this whenever activity occurs on the session.
        """
        # Update last activity timestamp
        self._last_activity = datetime.now(timezone.utc)

        # Cancel existing timer if running
        if self._timer_task and not self._timer_task.done():
            self._timer_task.cancel()
            logger.debug(f"Cancelled existing timer for session '{self.session_name}'")

        # Create new timer task
        self._timer_task = asyncio.create_task(self._wait_for_timeout())
        logger.debug(
            f"Started idle timer for session '{self.session_name}': "
            f"{self.timeout_seconds}s"
        )

    async def _wait_for_timeout(self) -> None:
        """
        Background task that waits for timeout then fires callback.

        Sleeps for timeout_seconds, then invokes on_timeout callback with
        session name. Catches asyncio.CancelledError silently (timer was reset).
        """
        try:
            await asyncio.sleep(self.timeout_seconds)

            # Timeout fired - call callback
            logger.info(
                f"Session '{self.session_name}' idle timeout fired "
                f"({self.timeout_seconds}s)"
            )
            await self.on_timeout(self.session_name)

        except asyncio.CancelledError:
            # Timer was reset or cancelled - this is normal
            logger.debug(f"Timer cancelled for session '{self.session_name}'")

    def cancel(self) -> None:
        """
        Cancel the idle timer.

        Stops the background timer task if running. Used on session shutdown
        or archive to prevent timeout callback from firing.
        """
        if self._timer_task and not self._timer_task.done():
            self._timer_task.cancel()
            logger.debug(f"Cancelled timer for session '{self.session_name}'")

    @property
    def seconds_since_activity(self) -> float:
        """
        Get seconds since last activity.

        Returns:
            Float seconds elapsed since last reset() call
        """
        now = datetime.now(timezone.utc)
        delta = now - self._last_activity
        return delta.total_seconds()

    @property
    def last_activity(self) -> datetime:
        """
        Get timestamp of last activity.

        Returns:
            UTC datetime of last reset() call
        """
        return self._last_activity
