"""Spotify access for the glasses Spotify module.

Auth (client id/secret + tokens) lives in ~/.spotify_glasses/ (chmod 600, NOT
in this repo and NOT under /browse). Access token auto-refreshes via the
stored refresh token. No secrets in this file.

    from spotify_lib import get_current_track, search_and_play
"""
import base64
import json
import os
import time

import requests

D = "/home/cameronsmith/.spotify_glasses"
API = "https://api.spotify.com/v1"


def _creds():
    return json.load(open(D + "/creds.json"))


def _tokens():
    return json.load(open(D + "/tokens.json"))


def _access_token():
    t = _tokens()
    if time.time() < t.get("expires_at", 0) - 60:
        return t["access_token"]
    c = _creds()
    auth = base64.b64encode(f"{c['client_id']}:{c['client_secret']}".encode()).decode()
    r = requests.post(
        "https://accounts.spotify.com/api/token",
        headers={"Authorization": f"Basic {auth}"},
        data={"grant_type": "refresh_token", "refresh_token": t["refresh_token"]},
    )
    r.raise_for_status()
    nt = r.json()
    t["access_token"] = nt["access_token"]
    t["expires_at"] = int(time.time()) + nt.get("expires_in", 3600)
    if nt.get("refresh_token"):
        t["refresh_token"] = nt["refresh_token"]
    json.dump(t, open(D + "/tokens.json", "w"))
    os.chmod(D + "/tokens.json", 0o600)
    return t["access_token"]


def _h():
    return {"Authorization": f"Bearer {_access_token()}"}


def get_current_track():
    """Now playing, or None if nothing's playing."""
    r = requests.get(API + "/me/player/currently-playing", headers=_h(), timeout=8)
    if r.status_code == 204 or not r.text.strip():
        return None
    r.raise_for_status()
    j = r.json()
    item = j.get("item") or {}
    if not item:
        return None
    imgs = (item.get("album") or {}).get("images", [])
    return {
        "name": item.get("name"),
        "artist": ", ".join(a["name"] for a in item.get("artists", [])),
        "album": (item.get("album") or {}).get("name"),
        "art_url": imgs[-1]["url"] if imgs else None,  # smallest (widest-first)
        "progress_ms": j.get("progress_ms") or 0,
        "duration_ms": item.get("duration_ms") or 0,
        "is_playing": j.get("is_playing", False),
    }


def search(query, limit=1):
    r = requests.get(API + "/search", headers=_h(), params={"q": query, "type": "track", "limit": limit}, timeout=8)
    r.raise_for_status()
    items = r.json().get("tracks", {}).get("items", [])
    if not items:
        return None
    t = items[0]
    return {"uri": t["uri"], "name": t["name"], "artist": ", ".join(a["name"] for a in t["artists"])}


def search_tracks(query, limit=5):
    """Top track matches from Spotify's live catalog."""
    r = requests.get(API + "/search", headers=_h(), params={"q": query, "type": "track", "limit": limit}, timeout=8)
    r.raise_for_status()
    return [{"name": t["name"], "artist": ", ".join(a["name"] for a in t["artists"]),
             "album": t["album"]["name"], "uri": t["uri"]}
            for t in r.json().get("tracks", {}).get("items", [])]


def artist_albums(name, limit=12):
    """An artist's albums/singles NEWEST FIRST, from Spotify's live catalog —
    so 'newest album' is always current, not guessed. Uses album SEARCH because
    the /artists/{id}/albums endpoint is restricted for newer dev apps."""
    r = requests.get(API + "/search", headers=_h(), params={"q": name, "type": "album", "limit": 10}, timeout=8)
    r.raise_for_status()
    nl = name.strip().lower()
    rows = [a for a in r.json().get("albums", {}).get("items", [])
            if any(nl == ar["name"].strip().lower() or nl in ar["name"].strip().lower() for ar in a.get("artists", []))]
    rows.sort(key=lambda a: a.get("release_date") or "", reverse=True)  # sort first, keep newest of each name
    seen, out = set(), []
    for a in rows:
        k = a["name"].strip().lower()
        if k in seen:
            continue
        seen.add(k)
        out.append({"name": a["name"], "id": a["id"], "release_date": a.get("release_date"),
                    "type": a.get("album_type"), "artist": a["artists"][0]["name"]})
    return out[:limit]


def album_tracks(album_id):
    r = requests.get(f"{API}/albums/{album_id}/tracks", headers=_h(),
                     params={"limit": 50, "market": "US"}, timeout=8)
    r.raise_for_status()
    return [{"name": t["name"], "uri": t["uri"]} for t in r.json().get("items", [])]


def play(uri=None):
    r = requests.put(API + "/me/player/play", headers=_h(), json=({"uris": [uri]} if uri else None), timeout=8)
    return r.status_code


def search_and_play(query):
    t = search(query)
    if not t:
        return {"ok": False, "error": "no results for " + query}
    code = play(t["uri"])
    return {"ok": code in (200, 202, 204), "track": t, "code": code}


def play_uri(uri):
    return play(uri)


def pause():
    return requests.put(API + "/me/player/pause", headers=_h(), timeout=8).status_code


def resume():
    return requests.put(API + "/me/player/play", headers=_h(), timeout=8).status_code


def toggle_playback():
    tr = get_current_track()
    return pause() if (tr and tr.get("is_playing")) else resume()


def next_track():
    return requests.post(API + "/me/player/next", headers=_h(), timeout=8).status_code


if __name__ == "__main__":
    print("current:", get_current_track())
    print("search 'bohemian rhapsody':", search("bohemian rhapsody"))
