# SPDX-License-Identifier: Apache-2.0
"""Exchange between recorders, and standing preferences (SPEC §11).

Lowerings only ever lower, so recorders sharing a room can merge them by taking the minimum,
in any order, any number of times. Raises are never exchanged. Only labels the recorders
genuinely share (roster or account ids) may be exchanged — never diarisation labels (§4.2).
A standing preference is a voice's own request made in advance: its lowering applies at once;
its raise applies only once the binding has authenticated who the voice is.
"""
from __future__ import annotations

from .grammar import Intent
from .session import TOP, Session


def _level(x):
    return x if isinstance(x, int) and not isinstance(x, bool) and 0 <= x <= 5 else None


def _secs(x):
    return float(x) if isinstance(x, (int, float)) and not isinstance(x, bool) and x >= 0 else None


def export_lowerings(s: Session, shared_labels=()) -> dict:
    """The session's lowerings. Voices are included only for labels in `shared_labels`."""
    voices = {}
    for lab, v in s.voices.items():
        if lab not in shared_labels:
            continue
        lv = min(v.requested if v.requested is not None else TOP, v.cap)
        o = {k: x for k, x in (("level", lv if lv < s.default else None), ("retention", v.retention)) if x is not None}
        voices[lab] = o
    return {"rp": "0.2", "type": "lowerings", "ceiling": None if s.ceiling == TOP else s.ceiling, "retention": s.retention, "voices": voices}


def merge_lowerings(s: Session, msg: dict, now: float) -> int:
    """Apply another recorder's lowerings here: minimum wins, nothing is raised, invalid members are ignored.
    Returns how many lowerings were applied."""
    if not isinstance(msg, dict) or msg.get("type") != "lowerings" or not str(msg.get("rp", "")).startswith("0."):
        return 0
    n = 0
    if (c := _level(msg.get("ceiling"))) is not None:
        s.apply(Intent("lower", "room", c, source="exchange"), None, 1.0, now); n += 1
    if (r := _secs(msg.get("retention"))) is not None:
        s.apply(Intent("shorten", "room", seconds=r, source="exchange"), None, 1.0, now); n += 1
    for lab, p in (msg.get("voices") or {}).items():
        if not isinstance(p, dict) or s._voice(str(lab), now) is None:
            continue
        if (lv := _level(p.get("level"))) is not None:
            s.apply(Intent("lower", "self", lv, source="exchange"), str(lab), 1.0, now); n += 1
        if (r := _secs(p.get("retention"))) is not None:
            s.apply(Intent("shorten", "self", seconds=r, source="exchange"), str(lab), 1.0, now); n += 1
    return n


def apply_preference(s: Session, label: str, pref: dict, now: float, authenticated: bool) -> list[str]:
    """A standing preference for `label`: {"level": n, "retention": seconds}. A raise needs authentication."""
    out = []
    v = s._voice(label, now)
    if v is None:
        return out
    if (lvl := _level(pref.get("level"))) is not None:
        cur = v.requested if v.requested is not None else s.default
        if lvl < cur:
            out.append(s.apply(Intent("lower", "self", lvl, source="preference"), label, 1.0, now))
        elif lvl > cur and authenticated:
            out.append(s.apply(Intent("raise", "self", lvl, source="preference"), label, 1.0, now))
    if (r := _secs(pref.get("retention"))) is not None:
        out.append(s.apply(Intent("shorten", "self", seconds=r, source="preference"), label, 1.0, now))
    return out
