#!/usr/bin/env python3
#
# Copyright (c) 2026 Stormux
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.

"""Utilities for selecting GStreamer audio sinks."""

from __future__ import annotations

from typing import Any, Optional, Tuple

import gi
from . import gstreamer_support
from . import settings

try:
    gi.require_version('Gst', '1.0')
    from gi.repository import Gst
except Exception:
    _gstreamerAvailable: bool = False
else:
    _gstreamerAvailable = gstreamer_support.gst_init_check_available(Gst)

_SINK_ELEMENT_BY_SETTING = {
    settings.SOUND_SINK_PIPEWIRE: "pipewiresink",
    settings.SOUND_SINK_PULSE: "pulsesink",
    settings.SOUND_SINK_ALSA: "alsasink",
}

_AUTO_SINK_ELEMENTS = [
    "autoaudiosink",
    "pipewiresink",
    "pulsesink",
    "alsasink",
]


def normalize_sound_sink_choice(soundSink: Optional[str]) -> str:
    """Return a valid sound sink setting value."""

    if soundSink is None:
        return settings.SOUND_SINK_AUTO

    choice = str(soundSink).strip().lower()
    if choice in settings.SOUND_SINK_VALUES:
        return choice

    return settings.SOUND_SINK_AUTO


def get_configured_sound_sink() -> str:
    """Return the configured sound sink, falling back to defaults."""

    try:
        from . import cthulhu
        if cthulhu.cthulhuApp is not None:
            configured = cthulhu.cthulhuApp.settingsManager.getSetting("soundSink")
            return normalize_sound_sink_choice(configured)
    except Exception:
        pass

    return normalize_sound_sink_choice(getattr(settings, "soundSink", settings.SOUND_SINK_AUTO))


def _get_sink_element_candidates(soundSink: str) -> list[str]:
    if soundSink == settings.SOUND_SINK_AUTO:
        return list(_AUTO_SINK_ELEMENTS)

    elementName = _SINK_ELEMENT_BY_SETTING.get(soundSink)
    if elementName:
        return [elementName]

    return list(_AUTO_SINK_ELEMENTS)


def create_audio_sink(
    sinkId: str,
    soundSink: Optional[str] = None,
) -> Tuple[Optional[Any], Optional[str], Optional[str]]:
    """Create a configured GStreamer audio sink."""

    if not _gstreamerAvailable:
        return None, None, "GStreamer is not available"

    configuredSink = normalize_sound_sink_choice(soundSink or get_configured_sound_sink())
    triedCandidates = []

    for elementName in _get_sink_element_candidates(configuredSink):
        factory = Gst.ElementFactory.find(elementName)
        if factory is None:
            triedCandidates.append(f"{elementName} (unavailable)")
            continue

        sink = Gst.ElementFactory.make(elementName, sinkId)
        if sink is None:
            triedCandidates.append(f"{elementName} (failed to create)")
            continue

        return sink, elementName, None

    triedText = ", ".join(triedCandidates) if triedCandidates else "none"
    reason = (
        f"No usable audio sink for soundSink={configuredSink}. "
        f"Tried: {triedText}"
    )
    return None, None, reason
