import time
from typing import Dict, Optional
import requests
import json

from ytmusicapi.constants import OAUTH_CLIENT_ID, OAUTH_CLIENT_SECRET, OAUTH_SCOPE, OAUTH_CODE_URL, OAUTH_TOKEN_URL, OAUTH_USER_AGENT
from ytmusicapi.helpers import initialize_headers


class YTMusicOAuth:
    """OAuth implementation for YouTube Music based on YouTube TV"""

    def __init__(self, session: requests.Session, proxies: Dict = None):
        self._session = session
        if proxies:
            self._session.proxies.update(proxies)

    def _send_request(self, url, data) -> requests.Response:
        data.update({"client_id": OAUTH_CLIENT_ID})
        headers = {"User-Agent": OAUTH_USER_AGENT}
        return self._session.post(url, data, headers=headers)

    def get_code(self) -> Dict:
        code_response = self._send_request(OAUTH_CODE_URL, data={"scope": OAUTH_SCOPE})
        response_json = code_response.json()
        url = f"{response_json['verification_url']}?user_code={response_json['user_code']}"
        input(f"Go to {url}, finish the login flow and press Enter when done, Ctrl-C to abort")
        return response_json

    def get_token_from_code(self, device_code: str) -> Dict:
        token_response = self._send_request(OAUTH_TOKEN_URL,
                                            data={
                                                "client_secret": OAUTH_CLIENT_SECRET,
                                                "grant_type":
                                                "http://oauth.net/grant_type/device/1.0",
                                                "code": device_code
                                            })
        return token_response.json()

    def refresh_token(self, refresh_token: str) -> Dict:
        response = self._send_request(OAUTH_TOKEN_URL,
                                      data={
                                          "client_secret": OAUTH_CLIENT_SECRET,
                                          "grant_type": "refresh_token",
                                          "refresh_token": refresh_token
                                      })
        return response.json()

    @staticmethod
    def dump_token(token, filepath):
        token["expires_at"] = int(time.time()) + int(token["expires_in"])
        with open(filepath, encoding="utf8", mode="w") as file:
            json.dump(token, file, indent=True)

    def setup(self, filepath: Optional[str] = None) -> Dict:
        code = self.get_code()
        token = self.get_token_from_code(code["device_code"])
        if filepath:
            self.dump_token(token, filepath)

        return token

    def load_headers(self, token: Dict, filepath: Optional[str] = None):
        headers = initialize_headers()
        if time.time() > token["expires_at"] - 3600:
            token.update(self.refresh_token(token["refresh_token"]))
            self.dump_token(token, filepath)
        headers["Authorization"] = f"{token['token_type']} {token['access_token']}"
        headers["Content-Type"] = "application/json"
        headers["X-Goog-Request-Time"] = str(int(time.time()))
        return headers
