using ytLive.Services;

namespace ytLive.Services.Audio;

/// <summary>
/// Owns the mic + desktop/game capture sources (TASK 4 ship step 4) and drives
/// the live mic meter. Runs only while live: started when go-live succeeds,
/// stopped on end-stream. Mic samples are level-metered and forwarded; loopback
/// samples are currently dropped (consumed by the encoder mix in a later step).
/// </summary>
public sealed class AudioMixer : IDisposable
{
    private readonly IAudioSource _mic;
    private readonly IAudioSource _loopback;
    private readonly AudioLevelMeter _meter;
    private readonly Action<string>? _log;
    private bool _started;

    public AudioMixer(IAudioSource mic, IAudioSource loopback, Action<string>? log = null)
    {
        _mic = mic;
        _loopback = loopback;
        _meter = new AudioLevelMeter();
        _log = log;

        _mic.SampleReady += OnMicSample;
        _loopback.SampleReady += OnLoopbackSample;
        _mic.Failed += OnMicFailed;
        _loopback.Failed += OnLoopbackFailed;
    }

    /// <summary>Current smoothed mic level (0..1).</summary>
    public float MicLevel => _meter.Level;

    /// <summary>Raised whenever the smoothed mic level changes.</summary>
    public event Action<float>? MicLevelChanged;

    public void Start()
    {
        if (_started)
            return;

        _started = true;
        _meter.Reset();
        _loopback.Start();
        _mic.Start();
    }

    public void Stop()
    {
        if (!_started)
            return;

        _started = false;
        _mic.Stop();
        _loopback.Stop();
        _meter.Reset();
        MicLevelChanged?.Invoke(0);
    }

    public void Dispose()
    {
        Stop();
        _mic.SampleReady -= OnMicSample;
        _loopback.SampleReady -= OnLoopbackSample;
        _mic.Failed -= OnMicFailed;
        _loopback.Failed -= OnLoopbackFailed;
        _mic.Dispose();
        _loopback.Dispose();
    }

    private void OnMicSample(AudioSample sample)
    {
        MicLevelChanged?.Invoke(_meter.Push(sample));
    }

    private void OnLoopbackSample(AudioSample sample)
    {
        // Desktop/game audio: captured for the future encoder mix; no UI yet.
    }

    private void OnMicFailed(Exception ex)
    {
        _log?.Invoke($"Mic capture failed: {ex.Message}");
        MicLevelChanged?.Invoke(0);
    }

    private void OnLoopbackFailed(Exception ex)
    {
        _log?.Invoke($"Desktop audio capture failed: {ex.Message}");
    }
}
