"""Audio primitives for voice generation."""

from pathlib import Path
from typing import BinaryIO

import librosa
import numpy as np
import soundfile as sf  # type: ignore[import-untyped]


class Audio:
    """Represents an audio file."""

    def __init__(self, data: np.ndarray, sample_rate: float):
        self.data = data
        self.sample_rate = sample_rate

    @classmethod
    def from_file(cls, file_path: Path) -> "Audio":
        data, sample_rate = librosa.load(file_path, mono=True)
        return Audio(data=data, sample_rate=sample_rate)

    @classmethod
    def create_silence(cls, duration: float, sample_rate: float) -> "Audio":
        data = np.zeros(int(duration * sample_rate))
        return Audio(data=data, sample_rate=sample_rate)

    def get_sample_rate(self) -> float:
        return self.sample_rate

    def set_sample_rate(self, sample_rate: float) -> None:
        resampled_data = librosa.resample(
            y=self.data,
            orig_sr=self.sample_rate,
            target_sr=sample_rate,
        )
        self.data = resampled_data
        self.sample_rate = sample_rate

    def get_length_seconds(self) -> float:
        return librosa.get_duration(y=self.data, sr=self.sample_rate)

    def export(self, output: BinaryIO, audio_format: str) -> None:
        sf.write(
            file=output,
            data=self.data,
            samplerate=self.sample_rate,
            format=audio_format,
        )

    def __add__(self, other: "Audio") -> "Audio":
        if self.get_sample_rate() > other.get_sample_rate():
            other.set_sample_rate(self.get_sample_rate())
        elif self.get_sample_rate() < other.get_sample_rate():
            self.set_sample_rate(other.get_sample_rate())

        combined_data: np.ndarray = np.concatenate((self.data, other.data))
        return Audio(data=combined_data, sample_rate=self.get_sample_rate())
