"""Tidal authentication and API client"""

from operator import itemgetter
from typing import cast, Any, Callable, Dict, List, Optional, Tuple, Type, Union
from urllib.parse import urlparse
import base64
import datetime
import json
import locale
import re
import sys
import time

import requests

from .models import (
    Track,
    Album,
    Artist,
    Media,
    MediaType,
    Client,
    ManiaSeriousException,
    UnavailableException,
)

LOCALE = locale.getlocale()[0]

# API_SCHEME_AND_DOMAIN = "https://api.tidalhifi.com"
API_ENDPOINT = "https://api.tidalhifi.com/v1"
AUTH_ENDPOINT = "https://auth.tidal.com/v1"
CLIENT_ID = "aR7gUaTK1ihpXOEP"
CLIENT_SECRET = "eVWBEkuL2FCjxgjOkR3yK0RYZEbcrMXRc2l8fU3ZCdE="
DEVICE_TYPE = "TABLET"
USER_AGENT = "TIDAL_ANDROID/1000 okhttp/3.10.0"
CLIENT_VERSION = "2.26.1"
MAXIMUM_LIMIT = 50

SPECIAL_AUDIO_MODES = frozenset(("DOLBY_ATMOS", "SONY_360RA"))
COVER_ART_SIZE = 1280
MAXIMUM_ATTEMPTS = 4


class TidalAuthError(Exception):
    """Error authenticating to TIDAL"""


class TidalSession:
    """Adapted from
    https://github.com/Dniel97/RedSea/blob/master/redsea/sessions.py. Thanks,
    Daniel!"""

    def __init__(
        self,
        device_code: Optional[str] = None,
        user_code: Optional[str] = None,
        country_code: Optional[str] = None,
        access_token: Optional[str] = None,
        refresh_token: Optional[str] = None,
        user_id: Optional[str] = None,
        expires: Optional[datetime.datetime] = None,
    ):
        self.device_code = device_code
        self.user_code = user_code
        self.country_code = country_code
        self.access_token = access_token
        self.refresh_token = refresh_token
        self.user_id = user_id
        self.expires = expires
        self._session = requests.Session()

    def to_dict(self) -> Dict[str, Any]:
        """Get the session parameters as a dict, for serialization"""
        return {
            "device_code": self.device_code,
            "user_code": self.user_code,
            "country_code": self.country_code,
            "access_token": self.access_token,
            "refresh_token": self.refresh_token,
            "user_id": self.user_id,
            "expires": self.expires,
        }

    def check_valid(self) -> None:
        """Raise a TidalAuthError if session is invalid"""
        if self.access_token is None:
            raise TidalAuthError("Missing access token.")

        if self.expires is not None and datetime.datetime.now() > self.expires:
            self._refresh()
        subscription_response = self.request(
            "GET", f"users/{self.user_id}/subscription"
        )
        try:
            subscription_response.raise_for_status()
            # assert subscription_response.json()["subscription"]["type"] == "HIFI"
        except requests.exceptions.HTTPError as error:
            raise TidalAuthError("Session is invalid.") from error
        except AssertionError as error:
            raise TidalAuthError("You need a HiFi subscription.") from error

    def get_authorization(self):
        authorization_response = self._session.post(
            f"{AUTH_ENDPOINT}/oauth2/device_authorization",
            data={
                "client_id": CLIENT_ID,
                "scope": "r_usr w_usr",
            },
        )
        if authorization_response.status_code == 400:
            raise TidalAuthError(
                "Authorization failed! Is the clientid/token up to date?"
            )
        try:
            authorization_response.raise_for_status()
        except requests.exceptions.HTTPError as error:
            raise TidalAuthError("Authorization failed!") from error

        authorization_json = authorization_response.json()
        self.device_code = authorization_json["deviceCode"]
        self.user_code = authorization_json["userCode"]

    def authenticate(self) -> None:
        print(
            "Go to https://link.tidal.com/{} and log in or sign up to TIDAL.".format(
                self.user_code
            )
        )

        data = {