"""
Spotify OAuth2 一次性授权脚本。
运行后会在本机 8888 端口等待 Spotify 回调，自动打印 refresh_token。

使用方法:
  1. 在 https://developer.spotify.com/dashboard 创建 App
  2. 在 App 的 Redirect URIs 里添加:  https://javrs.mir.gold/callback
  3. 通过环境变量传入凭据后运行:
       SPOTIFY_CLIENT_ID=xxx SPOTIFY_CLIENT_SECRET=yyy python3 spotify_auth.py
  4. 用浏览器打开打印出来的 URL，授权后会自动打印 refresh_token
  5. 把 refresh_token 填入 .env 文件
"""

import os
import json
import urllib.request
import urllib.parse
import base64
import http.server
import threading

CLIENT_ID     = os.getenv("SPOTIFY_CLIENT_ID", "")
CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET", "")
REDIRECT_URI  = os.getenv("SPOTIFY_REDIRECT_URI", "https://javrs.mir.gold/callback")
PORT          = 8812

SCOPES = " ".join([
    "user-read-playback-state",
    "user-modify-playback-state",
    "user-read-currently-playing",
    "playlist-read-private",
    "playlist-read-collaborative",
    "user-library-read",
    "user-top-read",
])

_code_holder = {"code": None}
_server_done = threading.Event()


class _CallbackHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        parsed = urllib.parse.urlparse(self.path)
        params = urllib.parse.parse_qs(parsed.query)
        if "code" in params:
            _code_holder["code"] = params["code"][0]
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b"<h2>Authorization successful! You can close this tab.</h2>")
            _server_done.set()
        elif "error" in params:
            error = params["error"][0]
            self.send_response(400)
            self.end_headers()
            self.wfile.write(f"<h2>Authorization failed: {error}</h2>".encode())
            _server_done.set()
        else:
            self.send_response(200)
            self.end_headers()
            self.wfile.write(b"ok")

    def log_message(self, *args):
        pass


def _exchange_code(code):
    credentials = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
    data = urllib.parse.urlencode({
        "grant_type": "authorization_code",
        "code": code,
        "redirect_uri": REDIRECT_URI,
    }).encode()
    req = urllib.request.Request(
        "https://accounts.spotify.com/api/token",
        data=data,
        headers={
            "Authorization": f"Basic {credentials}",
            "Content-Type": "application/x-www-form-urlencoded",
        },
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=15) as resp:
        return json.loads(resp.read().decode())


def main():
    if not CLIENT_ID or not CLIENT_SECRET:
        print("错误: 请先设置环境变量 SPOTIFY_CLIENT_ID 和 SPOTIFY_CLIENT_SECRET")
        return

    auth_url = "https://accounts.spotify.com/authorize?" + urllib.parse.urlencode({
        "client_id": CLIENT_ID,
        "response_type": "code",
        "redirect_uri": REDIRECT_URI,
        "scope": SCOPES,
    })

    server = http.server.HTTPServer(("0.0.0.0", PORT), _CallbackHandler)
    t = threading.Thread(target=server.serve_forever, daemon=True)
    t.start()

    print("\n" + "=" * 60)
    print("请用浏览器打开以下链接进行授权：")
    print()
    print(auth_url)
    print()
    print(f"（等待 Spotify 回调到 {REDIRECT_URI} ...）")
    print("=" * 60 + "\n")

    _server_done.wait(timeout=300)
    server.shutdown()

    code = _code_holder.get("code")
    if not code:
        print("超时或未收到授权码，请重试。")
        return

    print("收到授权码，正在换取 token...")
    try:
        tokens = _exchange_code(code)
    except Exception as e:
        print(f"换取 token 失败: {e}")
        return

    refresh_token = tokens.get("refresh_token", "")
    if not refresh_token:
        print("未收到 refresh_token，完整响应:")
        print(json.dumps(tokens, indent=2))
        return

    print("\n" + "=" * 60)
    print("授权成功！请把以下内容添加到 .env 文件：")
    print()
    print(f"SPOTIFY_CLIENT_ID={CLIENT_ID}")
    print(f"SPOTIFY_CLIENT_SECRET={CLIENT_SECRET}")
    print(f"SPOTIFY_REFRESH_TOKEN={refresh_token}")
    print("=" * 60 + "\n")


if __name__ == "__main__":
    main()
