"""
Tiny Arms Tavern — Now Playing companion for Windows.

Reads the currently-playing track from Apple Music (via the Windows Media
Session API — the same channel the volume overlay uses, so it works with the
new Apple Music app AND old iTunes) and pushes track changes to the bot's
/overlay/nowplaying/update endpoint.

Setup (one time, on the stream PC):
    pip install winsdk requests
    set NOW_PLAYING_URL=https://tat.neondestiny.com/overlay/nowplaying/update
    set NOW_PLAYING_TOKEN=<the token from the bot .env>
    python nowplaying_windows.py

Run it however you like — a terminal, a .bat file, or Task Scheduler at logon.
Artwork is resolved from Apple's own iTunes Search API, so the widget gets the
real album art without any extra permissions.
"""

import asyncio
import os
import sys
import time

import requests

try:
    from winsdk.windows.media.control import (
        GlobalSystemMediaTransportControlsSessionManager as MediaManager,
    )
except ImportError:
    sys.exit("winsdk is required: pip install winsdk")

UPDATE_URL = os.environ.get(
    "NOW_PLAYING_URL",
    "https://tat.neondestiny.com/overlay/nowplaying/update",
)
TOKEN = os.environ.get("NOW_PLAYING_TOKEN", "")
POLL_SECONDS = float(os.environ.get("NOW_PLAYING_POLL_SECONDS", "1"))

# When True, any playing app feeds the widget (Spotify, browsers, ...).
# Default False: only Apple Music / iTunes sessions are used.
ANY_APP = os.environ.get("NOW_PLAYING_ANY_APP", "0") == "1"

APPLE_APP_HINTS = ("apple", "itunes", "music")
PLAYING = 4  # GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing

HEADERS = {"x-now-playing-token": TOKEN} if TOKEN else {}


def log(msg):
    print(time.strftime("[%H:%M:%S] ") + msg, flush=True)


def post(payload):
    try:
        r = requests.post(UPDATE_URL, json=payload, headers=HEADERS, timeout=10)
        if r.status_code == 401:
            log("!! Bot rejected the token (401). Check NOW_PLAYING_TOKEN / bot .env.")
        elif r.status_code == 503:
            log("!! Bot has no NOW_PLAYING_TOKEN configured yet.")
        elif not r.ok:
            log(f"!! Bot returned {r.status_code}: {r.text[:200]}")
        return r.ok
    except requests.RequestException as e:
        log(f"!! Could not reach the bot: {e}")
        return False


def artwork_url(title, artist):
    """Best-effort album art from Apple's public iTunes Search API."""
    try:
        r = requests.get(
            "https://itunes.apple.com/search",
            params={"term": f"{title} {artist}".strip(), "entity": "song", "limit": 1},
            timeout=10,
        )
        results = r.json().get("results") or []
        art = results and results[0].get("artworkUrl100")
        # Ask for 600x600 instead of the 100x100 thumbnail.
        return art.replace("100x100", "600x600") if art else None
    except (requests.RequestException, ValueError):
        return None


def is_apple_session(session):
    try:
        app_id = (session.source_app_user_model_id or "").lower()
    except AttributeError:
        app_id = ""
    return any(hint in app_id for hint in APPLE_APP_HINTS)


async def get_current_track():
    manager = await MediaManager.request_async()
    sessions = manager.get_sessions()

    chosen = None
    for s in sessions:
        status = s.get_playback_info().playback_status
        if status != PLAYING:
            continue
        if ANY_APP or is_apple_session(s):
            chosen = s
            break  # sessions come back most-recent-first-ish; first match wins

    if not chosen:
        return None

    props = await chosen.try_get_media_properties_async()
    return {
        "title": props.title or "",
        "artist": props.artist or "",
        "album": props.album_title or "",
    }


async def main():
    if not TOKEN:
        log("WARNING: NOW_PLAYING_TOKEN is not set — the bot will reject updates.")

    log(f"Watching Windows media sessions (poll {POLL_SECONDS}s)...")
    log(f"Posting to {UPDATE_URL}")
    log("Scope: " + ("ANY media app" if ANY_APP else "Apple Music / iTunes only"))

    last = None  # (title, artist) of the last pushed track
    last_artwork = {}  # cache artwork lookups: (title, artist) -> url

    while True:
        try:
            track = await get_current_track()
            key = (track["title"], track["artist"]) if track else None

            if key != last:
                if not track:
                    log("Playback stopped — clearing the widget.")
                    post({"title": ""})  # empty update = clear
                else:
                    log(f"Now playing: {track['title']} — {track['artist']}")
                    art = last_artwork.get(key)
                    if art is None:
                        art = artwork_url(track["title"], track["artist"])
                        last_artwork[key] = art  # cache misses too (None)
                    track["artUrl"] = art
                    post(track)

                last = key

        except Exception as e:  # never die on a bad poll
            log(f"!! Poll failed: {e}")

        await asyncio.sleep(POLL_SECONDS)


if __name__ == "__main__":
    try:
        asyncio.run(main())
    except KeyboardInterrupt:
        log("Stopped.")
