#!/usr/bin/env python3
"""
Self-hosted Discord presence service for rotten.network.

Tracks ONE user and writes public/assets/discord.json (Lanyard-compatible, plus
banner / accent / badges) which the site's discord.js renders as a profile card.

Setup:
  1. discord.com/developers -> your app -> Bot
       - enable PRESENCE INTENT and SERVER MEMBERS INTENT
  2. Invite the bot to a server you are in.
  3. pip install -U discord.py
  4. Token in scripts/token.txt (or env DISCORD_TOKEN)
  5. python scripts/discord_bot.py   (see deploy/discord-bot.service for 24/7)
"""

import os
import json
from pathlib import Path

import discord

HERE = Path(__file__).resolve().parent
OUT = HERE.parent / "public" / "assets" / "discord.json"
USER_ID = 253922160832282645  # the account to track

TOKEN = os.environ.get("DISCORD_TOKEN")
if not TOKEN:
    tf = HERE / "token.txt"
    if tf.exists():
        TOKEN = tf.read_text(encoding="utf-8").strip()
if not TOKEN:
    raise SystemExit("No token: set DISCORD_TOKEN or put it in scripts/token.txt")

intents = discord.Intents.default()
intents.presences = True
intents.members = True
client = discord.Client(intents=intents)

# cached profile bits (banner/badges change rarely; fetched via the REST user)
PROFILE = {}


def emoji_json(e):
    if not e:
        return None
    out = {"name": getattr(e, "name", None)}
    if getattr(e, "id", None):
        out["id"] = str(e.id)
        out["animated"] = bool(getattr(e, "animated", False))
    return out


def activity_json(a):
    if isinstance(a, discord.CustomActivity):
        return {"type": 4, "name": "Custom Status", "state": a.name,
                "emoji": emoji_json(a.emoji)}
    t = int(getattr(a.type, "value", 0)) if getattr(a, "type", None) is not None else 0
    out = {"type": t, "name": getattr(a, "name", None)}
    for k in ("details", "state"):
        if getattr(a, k, None):
            out[k] = getattr(a, k)
    if getattr(a, "application_id", None):
        out["application_id"] = str(a.application_id)
    # elapsed timer for games ("for 12 minutes")
    start = getattr(a, "start", None)
    if start:
        out["timestamps"] = {"start": int(start.timestamp() * 1000)}
    large = getattr(a, "large_image_url", None)
    if large:
        out["assets"] = {"large_image": str(large)}
    return out


def spotify_json(member):
    for a in member.activities:
        if isinstance(a, discord.Spotify):
            return {"song": a.title, "artist": a.artist, "album": a.album,
                    "album_art_url": a.album_cover_url,
                    "timestamps": {"start": int(a.start.timestamp() * 1000),
                                   "end": int(a.end.timestamp() * 1000)}}
    return None


async def fetch_profile():
    """Pull banner / accent / badges / created date from the REST user object."""
    try:
        u = await client.fetch_user(USER_ID)
    except Exception as e:
        print("fetch_user failed:", e)
        return
    PROFILE.update({
        "username": u.name,
        "global_name": getattr(u, "global_name", None),
        "discriminator": u.discriminator,
        "avatar": u.avatar.key if u.avatar else None,
        "banner": u.banner.key if getattr(u, "banner", None) else None,
        "banner_color": (f"#{u.accent_colour.value:06x}" if getattr(u, "accent_colour", None) else None),
        "public_flags": int(u.public_flags.value) if getattr(u, "public_flags", None) else 0,
        "created_at": int(u.created_at.timestamp() * 1000),
        "avatar_decoration": None,
    })
    deco = getattr(u, "avatar_decoration", None)
    if deco:
        PROFILE["avatar_decoration"] = deco.key if hasattr(deco, "key") else None


def build(member):
    data = {
        "discord_user": {
            "id": str(USER_ID),
            "username": PROFILE.get("username"),
            "global_name": PROFILE.get("global_name"),
            "discriminator": PROFILE.get("discriminator"),
            "avatar": PROFILE.get("avatar"),
            "banner": PROFILE.get("banner"),
            "banner_color": PROFILE.get("banner_color"),
            "public_flags": PROFILE.get("public_flags", 0),
            "created_at": PROFILE.get("created_at"),
            "avatar_decoration": PROFILE.get("avatar_decoration"),
        },
        "discord_status": str(member.status) if member else "offline",
        "activities": [activity_json(a) for a in member.activities] if member else [],
        "listening_to_spotify": False,
        "spotify": None,
    }
    if member:
        sp = spotify_json(member)
        if sp:
            data["listening_to_spotify"] = True
            data["spotify"] = sp
    return data


def find_member():
    for g in client.guilds:
        m = g.get_member(USER_ID)
        if m:
            return m
    return None


def write(data):
    OUT.parent.mkdir(parents=True, exist_ok=True)
    OUT.write_text(json.dumps(data), encoding="utf-8")


@client.event
async def on_ready():
    print(f"logged in as {client.user}")
    await fetch_profile()
    m = find_member()
    write(build(m))
    print(f"updated: status={m.status if m else 'offline (user not in a shared server?)'}")


@client.event
async def on_presence_update(before, after):
    if after.id == USER_ID:
        write(build(after))
        print(f"presence: {after.status}, activities={[a.name for a in after.activities]}")


client.run(TOKEN)
