#!/usr/bin/env python3
"""
Erstellt die Spotify-Playlist "Reisebericht Hintergrund" mit 50 definierten Tracks.
Auth-Muster identisch mit _spotify_client() aus song_erkennung.py.
"""
from __future__ import annotations

import json
import sys
from pathlib import Path

DISPATCHER = Path(__file__).parent.parent
SPOTIFY_CONFIG_FILE = DISPATCHER / "hue" / "spotify_config.json"
SPOTIFY_CACHE_FILE  = DISPATCHER / "hue" / ".spotify_song_cache"
SPOTIFY_REDIRECT_URI = "http://127.0.0.1:8089/spotify/callback"
SPOTIFY_SCOPE = (
    "user-library-modify user-library-read "
    "playlist-modify-private playlist-modify-public "
    "playlist-read-private playlist-read-collaborative"
)

PLAYLIST_NAME = "Reisebericht Hintergrund"
PLAYLIST_DESCRIPTION = (
    "Hintergrundmusik für Reiseberichte — 50 Tracks aus Victors Spotify-History"
)

TRACKS = [
    ("Rival Consoles",               "Untravel"),
    ("Brian Eno",                    "An Ending (Ascent)"),
    ("Stars Of The Lid",             "Don't Bother They're Here"),
    ("Nils Frahm",                   "Says"),
    ("Stars Of The Lid",             "Articulate Silences, Pt. 1"),
    ("Stars Of The Lid",             "Dungtitled (In a Major)"),
    ("Apparat",                      "Ash / Black Veil"),
    ("Jóhann Jóhannsson",            "Kangaru"),
    ("Jon Hopkins",                  "Immunity - Asleep Version"),
    ("Keith Jarrett",                "Encore From Tokyo"),
    ("Sigur Rós",                    "Sé Lest"),
    ("Trent Reznor and Atticus Ross","Epiphany"),
    ("Air",                          "La femme d'argent"),
    ("Jóhann Jóhannsson",            "Flight from the City"),
    ("Stars Of The Lid",             "A Meaningful Moment Through a Meaning(less) Process"),
    ("Moderat",                      "A New Error"),
    ("Keith Jarrett",                "Köln, January 24, 1975, Pt. I"),
    ("William Basinski",             "Melancholia II"),
    ("Röyksopp",                     "Shores of Easy (Lost Tapes)"),
    ("Hans Zimmer",                  "Mesa"),
    ("Stars Of The Lid",             "Even If You're Never Awake"),
    ("Apparat",                      "Candil De La Calle - Apparat Dub Mix"),
    ("Philip Glass",                 "Facades"),
    ("Nightmares On Wax",            "Typical"),
    ("The Album Leaf",               "Twentytwofourteen"),
    ("Philip Glass",                 "Glassworks: VI. Closing"),
    ("Ulrich Schnauss",              "On My Own"),
    ("Max Richter",                  "Dream 3"),
    ("Brian Eno",                    "1/1"),
    ("Aphex Twin",                   "#3"),
    ("Moderat",                      "Milk"),
    ("Stars Of The Lid",             "The Evil That Never Arrived"),
    ("Brian Eno",                    "Stars"),
    ("Stars Of The Lid",             "December Hunting for Vegetarian Fuckface"),
    ("Brian Eno",                    "2/1"),
    ("Brian Eno",                    "2/2"),
    ("Jon Hopkins",                  "Abandon Window - Moderat Remix"),
    ("Apparat",                      "Arcadia"),
    ("Röyksopp",                     "Rescue (Lost Tapes)"),
    ("Röyksopp",                     "Rising Urge (Lost Tapes)"),
    ("Röyksopp",                     "The Ladder"),
    ("Moderat",                      "Rusty Nails"),
    ("Solar Fields",                 "Until We Meet the Sky"),
    ("Stars Of The Lid",             "Articulate Silences, Pt. 2"),
    ("Stars Of The Lid",             "The Daughters of Quiet Minds"),
    ("Stars Of The Lid",             "Piano Aquieu"),
    ("Stars Of The Lid",             "Tippy's Demise"),
    ("Brian Eno",                    "1/2"),
    ("Nightmares On Wax",            "I Am You"),
    ("Brian Eno",                    "77 Million Paintings"),
]


# ─────────────────────────────────────────
# Auth — identisch mit song_erkennung.py
# ─────────────────────────────────────────

def _spotify_cfg() -> dict:
    try:
        return json.loads(SPOTIFY_CONFIG_FILE.read_text())
    except Exception:
        return {}


def _spotify_client():
    """Gibt ein authentifiziertes spotipy.Spotify zurück oder None."""
    try:
        import spotipy
        from spotipy.oauth2 import SpotifyOAuth
        cfg = _spotify_cfg()
        client_id = cfg.get("client_id", "")
        client_secret = cfg.get("client_secret", "")
        if not client_id:
            return None
        auth = SpotifyOAuth(
            client_id=client_id,
            client_secret=client_secret,
            redirect_uri=SPOTIFY_REDIRECT_URI,
            scope=SPOTIFY_SCOPE,
            cache_handler=spotipy.CacheFileHandler(cache_path=str(SPOTIFY_CACHE_FILE)),
            open_browser=False,
        )
        token = auth.get_cached_token()
        if not token:
            return None
        if auth.is_token_expired(token):
            token = auth.refresh_access_token(token["refresh_token"])
        return spotipy.Spotify(auth=token["access_token"])
    except Exception:
        return None


# ─────────────────────────────────────────
# Playlist-Logik
# ─────────────────────────────────────────

def find_existing_playlist(sp, user_id: str, name: str) -> str | None:
    """Durchsucht alle Playlists des Users nach 'name'. Gibt playlist_id zurück oder None."""
    offset = 0
    limit = 50
    while True:
        result = sp.current_user_playlists(limit=limit, offset=offset)
        items = result.get("items", [])
        for pl in items:
            if pl and pl.get("name") == name:
                return pl["id"]
        if result.get("next"):
            offset += limit
        else:
            break
    return None


def search_track(sp, artist: str, track: str) -> str | None:
    """
    Sucht einen Track auf Spotify.
    Zuerst mit artist:"X" track:"Y", dann als einfacher Fallback.
    Gibt spotify:track:URI zurück oder None.
    """
    try:
        q = f'artist:"{artist}" track:"{track}"'
        result = sp.search(q=q, type="track", limit=1)
        items = result.get("tracks", {}).get("items", [])
        if items:
            return items[0]["uri"]
    except Exception as e:
        print(f"  [Suche-Fehler A] {artist} — {track}: {e}", file=sys.stderr)

    # Fallback: einfache Suche
    try:
        q_simple = f"{artist} {track}"
        result = sp.search(q=q_simple, type="track", limit=1)
        items = result.get("tracks", {}).get("items", [])
        if items:
            return items[0]["uri"]
    except Exception as e:
        print(f"  [Suche-Fehler B] {artist} — {track}: {e}", file=sys.stderr)

    return None


def add_tracks_in_batches(sp, playlist_id: str, uris: list[str], batch_size: int = 100):
    """Fügt URIs in Batches à 100 zur Playlist hinzu."""
    for i in range(0, len(uris), batch_size):
        batch = uris[i : i + batch_size]
        sp.playlist_add_items(playlist_id, batch)


# ─────────────────────────────────────────
# Main
# ─────────────────────────────────────────

def main():
    sp = _spotify_client()
    if sp is None:
        print("NICHT VERBUNDEN — zuerst Spotify autorisieren")
        sys.exit(1)

    user_id = sp.me()["id"]
    print(f"Eingeloggt als: {user_id}")

    # Playlist suchen oder anlegen
    playlist_id = find_existing_playlist(sp, user_id, PLAYLIST_NAME)
    if playlist_id:
        print(f"Playlist existiert bereits: {playlist_id}")
    else:
        pl = sp._post("me/playlists", payload={
            "name": PLAYLIST_NAME,
            "public": True,
            "description": PLAYLIST_DESCRIPTION,
        })
        playlist_id = pl["id"]
        print(f"Playlist erstellt: {playlist_id}")

    # Tracks suchen
    found_uris: list[str] = []
    not_found: list[tuple[str, str]] = []

    print(f"\nSuche {len(TRACKS)} Tracks …")
    for i, (artist, track) in enumerate(TRACKS, start=1):
        uri = search_track(sp, artist, track)
        if uri:
            found_uris.append(uri)
            print(f"  [{i:02d}/50] ✓  {artist} — {track}")
        else:
            not_found.append((artist, track))
            print(f"  [{i:02d}/50] ✗  {artist} — {track}", file=sys.stderr)

    # Tracks hinzufügen
    if found_uris:
        print(f"\nFüge {len(found_uris)} Tracks zur Playlist hinzu …")
        add_tracks_in_batches(sp, playlist_id, found_uris)

    # Zusammenfassung
    print(f"\n{'─' * 40}")
    print(f"Fertig.")
    print(f"  Gefunden und hinzugefügt : {len(found_uris)} / {len(TRACKS)}")
    if not_found:
        print(f"  Nicht gefunden ({len(not_found)}):")
        for artist, track in not_found:
            print(f"    – {artist} — {track}")
    print(f"  Playlist-ID : {playlist_id}")
    print(f"  Playlist    : https://open.spotify.com/playlist/{playlist_id}")


if __name__ == "__main__":
    main()
