"""Audio level computation implementation.

Provide RMS and dB level calculation for VU meter display.
"""

import math
from typing import Final

import numpy as np
from numpy.typing import NDArray


def compute_rms(frames: NDArray[np.float32]) -> float:
    """Calculate Root Mean Square of audio samples.

    Args:
        frames: Audio samples as float32 array (normalized -1.0 to 1.0).

    Returns:
        RMS level as float (0.0 for empty array).
    """
    return 0.0 if len(frames) == 0 else float(np.sqrt(np.mean(np.square(frames))))


class RmsLevelProvider:
    """RMS-based audio level provider.

    Compute RMS (Root Mean Square) level from audio frames for VU meter display.
    """

    # Minimum dB value to report (silence threshold)
    MIN_DB: Final[float] = -60.0

    def get_rms(self, frames: NDArray[np.float32]) -> float:
        """Calculate RMS level from audio frames.

        Args:
            frames: Audio samples as float32 array (normalized -1.0 to 1.0).

        Returns:
            RMS level normalized to 0.0-1.0 range.
        """
        rms = compute_rms(frames)
        # Clamp to 0.0-1.0 range for VU meter display
        return min(1.0, max(0.0, rms))

    def get_db(self, frames: NDArray[np.float32]) -> float:
        """Calculate dB level from audio frames.

        Args:
            frames: Audio samples as float32 array (normalized -1.0 to 1.0).

        Returns:
            Level in dB (MIN_DB to 0 range).
        """
        rms = self.get_rms(frames)

        if rms <= 0:
            return self.MIN_DB

        # Convert to dB: 20 * log10(rms)
        db = 20.0 * math.log10(rms)

        # Clamp to MIN_DB to 0 range
        return max(self.MIN_DB, min(0.0, db))

    def rms_to_db(self, rms: float) -> float:
        """Convert RMS value to dB.

        Args:
            rms: RMS level (0.0-1.0).

        Returns:
            Level in dB (MIN_DB to 0 range).
        """
        if rms <= 0:
            return self.MIN_DB

        db = 20.0 * math.log10(rms)
        return max(self.MIN_DB, min(0.0, db))

    def db_to_rms(self, db: float) -> float:
        """Convert dB value to RMS.

        Args:
            db: Level in dB.

        Returns:
            RMS level (0.0-1.0).
        """
        return 0.0 if db <= self.MIN_DB else 10.0 ** (db / 20.0)
