#
# Copyright (c) 2024-2026, Daily
#
# SPDX-License-Identifier: BSD 2-Clause License
#

"""Observer for measuring user-to-bot response latency.

.. deprecated:: 0.0.102
    This module is deprecated. Use :class:`UserBotLatencyObserver` directly
    with its ``on_latency_measured`` event handler instead.
"""

import time
import warnings
from statistics import mean

from loguru import logger

from pipecat.frames.frames import (
    BotStartedSpeakingFrame,
    CancelFrame,
    EndFrame,
    VADUserStartedSpeakingFrame,
    VADUserStoppedSpeakingFrame,
)
from pipecat.observers.base_observer import BaseObserver, FramePushed
from pipecat.processors.frame_processor import FrameDirection


class UserBotLatencyLogObserver(BaseObserver):
    """Observer that measures time between user stopping speech and bot starting speech.

    This helps measure how quickly the AI services respond by tracking
    conversation turn timing and logging latency metrics.

    .. deprecated:: 0.0.102
        This class is deprecated. Use :class:`UserBotLatencyObserver` directly
        with its ``on_latency_measured`` event handler for custom logging.
    """

    def __init__(self):
        """Initialize the latency observer.

        Sets up tracking for processed frames and user speech timing
        to calculate response latencies.

        .. deprecated:: 0.0.102
            This class is deprecated. Use :class:`UserBotLatencyObserver`
            directly with its ``on_latency_measured`` event handler.
        """
        warnings.warn(
            "UserBotLatencyLogObserver is deprecated and will be removed in a future version. "
            "Use UserBotLatencyObserver directly with its on_latency_measured event handler instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        super().__init__()
        self._user_bot_latency_processed_frames = set()
        self._user_stopped_time = 0
        self._latencies = []

    async def on_push_frame(self, data: FramePushed):
        """Process frames to track speech timing and calculate latency.

        Args:
            data: Frame push event containing the frame and direction information.
        """
        # Only process downstream frames
        if data.direction != FrameDirection.DOWNSTREAM:
            return

        # Skip already processed frames
        if data.frame.id in self._user_bot_latency_processed_frames:
            return

        self._user_bot_latency_processed_frames.add(data.frame.id)

        if isinstance(data.frame, VADUserStartedSpeakingFrame):
            self._user_stopped_time = 0
        elif isinstance(data.frame, VADUserStoppedSpeakingFrame):
            self._user_stopped_time = data.frame.timestamp - data.frame.stop_secs
        elif isinstance(data.frame, (EndFrame, CancelFrame)):
            self._log_summary()
        elif isinstance(data.frame, BotStartedSpeakingFrame) and self._user_stopped_time:
            latency = time.time() - self._user_stopped_time
            self._user_stopped_time = 0
            self._latencies.append(latency)
            self._log_latency(latency)

    def _log_summary(self):
        if not self._latencies:
            return
        avg_latency = mean(self._latencies)
        min_latency = min(self._latencies)
        max_latency = max(self._latencies)
        logger.info(
            f"⏱️ LATENCY FROM USER STOPPED SPEAKING TO BOT STARTED SPEAKING - Avg: {avg_latency:.3f}s, Min: {min_latency:.3f}s, Max: {max_latency:.3f}s"
        )

    def _log_latency(self, latency: float):
        """Log the latency.

        Args:
            latency: The latency to log.
        """
        logger.debug(
            f"⏱️ LATENCY FROM USER STOPPED SPEAKING TO BOT STARTED SPEAKING: {latency:.3f}s"
        )
