from collections import deque
from dataclasses import dataclass


@dataclass
class EffectTiming:
    name: str
    duration_ms: float
    buffer_chars_in: int
    buffer_chars_out: int


@dataclass
class FrameTiming:
    frame_number: int
    total_ms: float
    effects: list[EffectTiming]


class PerformanceMonitor:
    """Collects and stores performance metrics for effect pipeline."""

    def __init__(self, max_frames: int = 60):
        self._max_frames = max_frames
        self._frames: deque[FrameTiming] = deque(maxlen=max_frames)
        self._current_frame: list[EffectTiming] = []

    def start_frame(self, frame_number: int) -> None:
        self._current_frame = []

    def record_effect(
        self, name: str, duration_ms: float, chars_in: int, chars_out: int
    ) -> None:
        self._current_frame.append(
            EffectTiming(
                name=name,
                duration_ms=duration_ms,
                buffer_chars_in=chars_in,
                buffer_chars_out=chars_out,
            )
        )

    def end_frame(self, frame_number: int, total_ms: float) -> None:
        self._frames.append(
            FrameTiming(
                frame_number=frame_number,
                total_ms=total_ms,
                effects=self._current_frame,
            )
        )

    def get_stats(self) -> dict:
        if not self._frames:
            return {"error": "No timing data available"}

        total_times = [f.total_ms for f in self._frames]
        avg_total = sum(total_times) / len(total_times)
        min_total = min(total_times)
        max_total = max(total_times)

        effect_stats: dict[str, dict] = {}
        for frame in self._frames:
            for effect in frame.effects:
                if effect.name not in effect_stats:
                    effect_stats[effect.name] = {"times": [], "total_chars": 0}
                effect_stats[effect.name]["times"].append(effect.duration_ms)
                effect_stats[effect.name]["total_chars"] += effect.buffer_chars_out

        for name, stats in effect_stats.items():
            times = stats["times"]
            stats["avg_ms"] = sum(times) / len(times)
            stats["min_ms"] = min(times)
            stats["max_ms"] = max(times)
            del stats["times"]

        return {
            "frame_count": len(self._frames),
            "pipeline": {
                "avg_ms": avg_total,
                "min_ms": min_total,
                "max_ms": max_total,
            },
            "effects": effect_stats,
        }

    def reset(self) -> None:
        self._frames.clear()
        self._current_frame = []


_monitor: PerformanceMonitor | None = None


def get_monitor() -> PerformanceMonitor:
    global _monitor
    if _monitor is None:
        _monitor = PerformanceMonitor()
    return _monitor


def set_monitor(monitor: PerformanceMonitor) -> None:
    global _monitor
    _monitor = monitor
