"""Local OBS websocket client — stamps GSI events with the recording's own
timecode so kill clips land frame-aligned on the VOD clock.

Optional: with no obs-websocket connection events fall back to wall-clock
epoch matching against the VOD's recorded window (works fine as long as the
recording is never paused).
"""
import logging
import time

from clipshelf.config import CFG

try:
    import obsws_python as obs
except ImportError:
    obs = None

# a missing OBS is a normal state — don't let the library's reconnect
# tracebacks wallpaper the console every 15s
for _name in ("obsws_python", "obsws_python.baseclient", "websocket"):
    logging.getLogger(_name).setLevel(logging.CRITICAL)

_req_client = None
_last_err = 0.0


def check_and_reconnect():
    global _req_client, _last_err
    if obs is None:
        return
    if _req_client:
        try:
            _req_client.get_version()
            return
        except Exception:
            _req_client = None
    ws = CFG.get("obs_ws") or {}
    try:
        _req_client = obs.ReqClient(host=ws.get("host", "127.0.0.1"),
                                    port=int(ws.get("port", 4455)),
                                    password=ws.get("password", ""), timeout=3)
        print(f"[{time.strftime('%X')}] [OBS] connected", flush=True)
    except Exception as e:
        _req_client = None
        if time.time() - _last_err > 300:
            _last_err = time.time()
            print(f"[{time.strftime('%X')}] [OBS] not reachable ({type(e).__name__}) — "
                  "events will use clock matching instead of timecodes", flush=True)


def reset():
    """Drop the connection so the next check picks up new credentials."""
    global _req_client, _last_err
    _req_client = None
    _last_err = 0.0


def connected() -> bool:
    return _req_client is not None


def save_replay_buffer(timeout_s: float = 12.0) -> str | None:
    """Trigger OBS's replay-buffer save and return the saved file path, or
    None when the buffer is off/unreachable. SaveReplayBuffer is async in
    OBS — poll GetLastReplayBufferReplay until a NEW path lands on disk."""
    if not _req_client:
        return None
    try:
        if not _req_client.get_replay_buffer_status().output_active:
            return None
        try:
            before = _req_client.get_last_replay_buffer_replay().saved_replay_path
        except Exception:
            before = None
        _req_client.save_replay_buffer()
    except Exception:
        return None
    import os
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        try:
            path = _req_client.get_last_replay_buffer_replay().saved_replay_path
            if path and path != before and os.path.exists(path):
                # let OBS finish flushing the container
                size = -1
                while time.time() < deadline:
                    now = os.path.getsize(path)
                    if now == size:
                        return path
                    size = now
                    time.sleep(0.4)
                return path
        except Exception:
            pass
        time.sleep(0.3)
    return None


def get_obs_timecode() -> int:
    """ms into the current recording (or stream), 0 when idle/unreachable."""
    if not _req_client:
        return 0
    try:
        rec = _req_client.get_record_status()
        if rec.output_active:
            return rec.output_duration
        st = _req_client.get_stream_status()
        if st.output_active:
            return st.output_duration
    except Exception:
        pass
    return 0
