import obspython as obs

MONITOR_INTERVAL_MS = 100
RESCAN_INTERVAL_MS = 1000

_settings_ref = None
_selected_sources = set()
_known_sources = set()


def script_description():
    return """
Sincroniza automaticamente o Mute das fontes de áudio com o monitoramento.

Fonte marcada:
- Desmutada: Monitoramento e saída
- Mutada: Monitoramento desligado

Esta versão detecta novamente as fontes após o OBS terminar de carregar.
Não utiliza OBS WebSocket.
"""


def _safe_key(name):
    return "audio_" + "".join(f"{b:02X}" for b in name.encode("utf-8"))


def _get_audio_source_names():
    names = []
    seen = set()

    sources = obs.obs_enum_sources()

    if sources is not None:
        for source in sources:
            flags = obs.obs_source_get_output_flags(source)

            if (flags & obs.OBS_SOURCE_AUDIO) != 0:
                name = obs.obs_source_get_name(source)

                if name and name not in seen:
                    seen.add(name)
                    names.append(name)

        obs.source_list_release(sources)

    names.sort(key=str.casefold)
    return names


def _rebuild_selected_sources():
    global _selected_sources, _known_sources

    names = _get_audio_source_names()
    current_sources = set(names)
    selected = set()

    if _settings_ref is not None:
        for name in names:
            if obs.obs_data_get_bool(_settings_ref, _safe_key(name)):
                selected.add(name)

    sources_changed = current_sources != _known_sources
    selection_changed = selected != _selected_sources

    _known_sources = current_sources
    _selected_sources = selected

    if sources_changed or selection_changed:
        obs.script_log(
            obs.LOG_INFO,
            (
                "[Mutar Monitor] Fontes detectadas: "
                f"{len(_known_sources)} | Marcadas: {len(_selected_sources)}"
            ),
        )


def _apply_source(source_name):
    source = obs.obs_get_source_by_name(source_name)

    if source is None:
        return

    try:
        muted = obs.obs_source_muted(source)

        target_mode = (
            obs.OBS_MONITORING_TYPE_NONE
            if muted
            else obs.OBS_MONITORING_TYPE_MONITOR_AND_OUTPUT
        )

        current_mode = obs.obs_source_get_monitoring_type(source)

        if current_mode != target_mode:
            obs.obs_source_set_monitoring_type(source, target_mode)

    finally:
        obs.obs_source_release(source)


def _monitor_sources():
    for source_name in list(_selected_sources):
        _apply_source(source_name)


def _rescan_sources():
    _rebuild_selected_sources()
    _monitor_sources()


def _refresh_sources(props, prop):
    _rescan_sources()
    return True


def script_properties():
    props = obs.obs_properties_create()

    obs.obs_properties_add_text(
        props,
        "info",
        (
            "Marque as fontes desejadas. "
            "Desmutada = Monitoramento e saída | "
            "Mutada = Monitoramento desligado"
        ),
        obs.OBS_TEXT_INFO,
    )

    names = _get_audio_source_names()

    if not names:
        obs.obs_properties_add_text(
            props,
            "none",
            (
                "As fontes ainda não foram carregadas. "
                "O script tentará detectá-las automaticamente."
            ),
            obs.OBS_TEXT_INFO,
        )

    for name in names:
        obs.obs_properties_add_bool(
            props,
            _safe_key(name),
            name,
        )

    obs.obs_properties_add_button(
        props,
        "refresh_sources",
        "Atualizar lista de fontes",
        _refresh_sources,
    )

    return props


def script_defaults(settings):
    pass


def script_update(settings):
    global _settings_ref

    _settings_ref = settings
    _rebuild_selected_sources()
    _monitor_sources()


def script_load(settings):
    global _settings_ref

    _settings_ref = settings

    obs.timer_remove(_monitor_sources)
    obs.timer_remove(_rescan_sources)

    obs.timer_add(_monitor_sources, MONITOR_INTERVAL_MS)
    obs.timer_add(_rescan_sources, RESCAN_INTERVAL_MS)

    _rescan_sources()

    obs.script_log(
        obs.LOG_INFO,
        "[Mutar Monitor] Script iniciado automaticamente.",
    )


def script_unload():
    obs.timer_remove(_monitor_sources)
    obs.timer_remove(_rescan_sources)

    obs.script_log(
        obs.LOG_INFO,
        "[Mutar Monitor] Script descarregado.",
    )
