"""What is this box doing right now — gaming, media, or idle — and switching between.

WHY THIS EXISTS
---------------
Home Assistant needs to be able to answer "is somebody playing on the TV box?" without
guessing from power draw. That one sensor is worth a lot in automations: don't dim the
living room during a game, don't announce the doorbell over a raid, count the room as
occupied even though nobody has moved for forty minutes.

WHAT IT READS
-------------
Two cheap facts, both local:
  - is the Steam client running (pgrep);
  - which sway workspace is focused (swaymsg -t get_workspaces).

Deliberately NOT the window title. Steam's window titles and the existence of a
separate Big Picture window have both changed across client rewrites, and a sensor that
silently goes wrong after a Steam update is worse than one that is slightly coarse. The
workspace name is a contract this repo owns — see the header in
configs/session/session-watcher, which makes the same call for the same reason.

MODES
-----
  gaming  — Steam is running and 1:steam is focused. The screen is showing a game or
            Big Picture.
  steam   — Steam is running but the user has moved elsewhere (this is "exited Big
            Picture but left Steam up", the case session-watcher reacts to).
  media   — Steam is not running; the media session is what is on screen.
  idle    — nothing is up. Only really seen in the seconds after boot, or after
            somebody quit Steam and the media session has not been started yet.

SECURITY POSTURE, UNCHANGED
---------------------------
The two commands below take no arguments derived from any MQTT payload. The mode
*switch* is an enumerated action — see mqtt_discovery.py's module docstring.
"""

from __future__ import annotations

import json
import logging

from .sway_control import WS_STEAM

log = logging.getLogger(__name__)

MODE_GAMING = "gaming"
MODE_STEAM = "steam"
MODE_MEDIA = "media"
MODE_IDLE = "idle"

MODES = (MODE_GAMING, MODE_MEDIA)

STEAM_PROCESS = "steam"
MEDIA_SESSION = "/usr/local/bin/media-session"
STEAM_BIG_PICTURE = "/usr/local/bin/steam-big-picture"

# What media-session itself guards on. Kept in sync with that script by hand; there is
# no shared file to read, and inventing one to hold three strings would be worse.
MEDIA_PATTERNS = (
    "com.spotify.Client",
    "media-player-idle",
    "firefox.*--profile.*/firefox/web",
)


class SessionMode:
    def __init__(self, sway):
        self.sway = sway
        # What current() last computed, versus what was last put on the broker. Two
        # fields because current() updates the first every time it is called, so it
        # cannot also serve as "have we told HA about this yet".
        self.last_mode = MODE_IDLE
        self.last_published = ""

    # --- reading ------------------------------------------------------------
    def focused_workspace(self) -> str:
        output = self.sway.swaymsg("-t", "get_workspaces")
        if not output:
            return ""
        try:
            workspaces = json.loads(output)
        except ValueError:
            log.warning("could not parse get_workspaces output")
            return ""
        for workspace in workspaces:
            if isinstance(workspace, dict) and workspace.get("focused"):
                return str(workspace.get("name") or "")
        return ""

    def steam_running(self) -> bool:
        # is_process (pgrep -x), not is_running (pgrep -f) — see that method's comment
        # for why Steam specifically needs the exact-name match.
        return self.sway.is_process(STEAM_PROCESS)

    def media_running(self) -> bool:
        return any(self.sway.is_running(pattern) for pattern in MEDIA_PATTERNS)

    def current(self) -> str:
        steam = self.steam_running()
        if steam and self.focused_workspace() == WS_STEAM:
            mode = MODE_GAMING
        elif steam:
            mode = MODE_STEAM
        elif self.media_running():
            mode = MODE_MEDIA
        else:
            mode = MODE_IDLE
        self.last_mode = mode
        return mode

    def attributes(self) -> dict:
        return {
            "steam_running": self.steam_running(),
            "media_running": self.media_running(),
            "workspace": self.focused_workspace(),
        }

    # --- switching ----------------------------------------------------------
    def select(self, mode: str) -> str:
        """Handle the HA select. Enumerated: anything else is ignored, not guessed at.

        Note the asymmetry, which is intentional. Choosing "gaming" launches or focuses
        Steam and leaves the media apps alone — someone may well want music over a
        game, and killing a running Spotify because a game started would lose whatever
        was playing. Choosing "media" starts the media session and likewise does not
        quit Steam, because quitting Steam from a phone while somebody is mid-match is
        not a thing this should be able to do by accident. Both directions are additive;
        the only thing that shuts anything down is the explicit "Stop media apps"
        button.
        """
        mode = (mode or "").strip().lower()
        if mode == MODE_GAMING:
            log.info("session mode -> gaming")
            self.sway.launch_app([STEAM_BIG_PICTURE])
        elif mode == MODE_MEDIA:
            log.info("session mode -> media")
            self.sway.launch_app([MEDIA_SESSION, "start", "--focus"])
        else:
            log.warning("ignoring unknown session mode %r", mode)
        return self.current()

    def stop_media(self) -> str:
        log.info("stopping the media session")
        self.sway.launch_app([MEDIA_SESSION, "stop"])
        return self.current()
