import datetime
import functools
import itertools
import re
import subprocess
from urllib.parse import urlsplit

import jwt
import redis
import requests
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, ValidationError
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
from spotipy import Spotify

from main import oauth_manager

from .client import AppleMusicClient

SPOTIFY_PLAYLIST_URL_PAT = re.compile(
    r"http(s)?:\/\/open.spotify.com/(user\/.+\/)?playlist/(?P<playlist_id>[^\s?]+)"
)
APPLE_MUSIC_PLAYLIST_URL_PAT = re.compile(
    r"https:\/\/(embed.)?music\.apple\.com\/(?P<storefront>.{2})\/playlist(\/.+)?\/(?P<playlist_id>[^\s?]+)"
)


def requests_retry_session(
    retries=3,
    backoff_factor=1,
    status_forcelist=(429, 500, 502, 503, 504),
    session=None,
):
    session = session or requests.Session()
    retry = Retry(
        total=retries,
        read=retries,
        connect=retries,
        backoff_factor=backoff_factor,
        status_forcelist=status_forcelist,
        allowed_methods=["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"],
    )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount("http://", adapter)
    session.mount("https://", adapter)
    return session


def get_spotify_client():
    return Spotify(oauth_manager=oauth_manager)


def get_applemusic_client():
    return AppleMusicClient(
        settings.APPLE_TEAM_ID, settings.APPLE_KEY_ID, settings.APPLE_PRIVATE_KEY
    )


def grouper(n, iterable):
    iterator = iter(iterable)
    while True:
        chunk = list(itertools.islice(iterator, n))
        if not chunk:
            break
        yield chunk


def get_redis_client():
    return redis.Redis.from_url(settings.REDIS_URL)


def generate_auth_token() -> str:
    # see https://developer.apple.com/documentation/applemusicapi/getting_keys_and_creating_tokens
    time_now = datetime.datetime.now()
    time_expired = time_now + datetime.timedelta(hours=12)
    headers = {"alg": "ES256", "kid": settings.APPLE_KEY_ID}
    payload = {
        "iss": settings.APPLE_TEAM_ID,
        "exp": int(time_expired.strftime("%s")),
        "iat": int(time_now.strftime("%s")),
    }
    token = jwt.encode(
        payload, settings.APPLE_PRIVATE_KEY, algorithm="ES256", headers=headers
    )
    return token


def strip_qs(url):
    # Strips query string from url
    return urlsplit(url)._replace(query=None, fragment=None).geturl()


def check_config():
    if not settings.APPLE_KEY_ID:
        raise ImproperlyConfigured(
            "APPLE_KEY_ID setting has not been properly defined."
        )
    if not settings.APPLE_TEAM_ID:
        raise ImproperlyConfigured(
            "APPLE_TEAM_ID setting has not been properly defined."
        )
    if not settings.APPLE_PRIVATE_KEY:
        raise ImproperlyConfigured(
            "APPLE_PRIVATE_KEY setting has not been properly defined."
        )
    if not settings.SPOTIFY_CLIENT_ID:
        raise ImproperlyConfigured(
            "SPOTIFY_CLIENT_ID setting has not been properly defined."
        )
    if not settings.SPOTIFY_CLIENT_SECRET:
        raise ImproperlyConfigured(
            "SPOTIFY_CLIENT_SECRET has not been properly defined."
        )
    if not settings.SPOTIFY_REDIRECT_URI:
        raise ImproperlyConfigured(
            "SPOTIFY_REDIRECT_URI has not been properly defined."
        )
    if not settings.REDIS_URL:
        raise ImproperlyConfigured("REDIS_URL has not been properly defined.")


def parse_track_name(track_name):
    """
    Parse a track title into main title and featured artists.

    Returns:
        dict: Contains 'title' and 'featured_artists' keys
    """
    match = re.search(
        r"[\(\[](?:feat\.?|featuring|ft\.?|with)\s+([^\)\]]+)[\)\]]",
        track_name,
        re.IGNORECASE,
    )

    featured_artists = []
    if match:
        artists_string = match.group(1)
        featured_artists = [
            artist.strip()
            for artist in re.split(r"\s*[,&,+]\s*", artists_string)
            if artist.strip()
        ]

    # Extract clean title
    clean_name = re.sub(
        r"\s*[\(\[](?:feat\.?|featuring|ft\.?|with)\s+[^\)\]]*[\)\]]",
        "",
        track_name,
        flags=re.IGNORECASE,
    ).strip()

    return {"track_name": clean_name, "featured_artists": featured_artists}


@functools.lru_cache(maxsize=128)
def get_version():
    version = subprocess.check_output(
        ["git", "rev-parse", "--short", "HEAD"], universal_newlines=True
    ).strip()
    return version


def validate_playlist_url(pattern, url, platform):
    mo = pattern.match(url)
    if not mo:
        raise ValidationError(f"Invalid {platform} playlist url")
    return url


def validate_apple_music_playlist_url(url):
    validate_playlist_url(APPLE_MUSIC_PLAYLIST_URL_PAT, url, "Apple Music")


def validate_spotify_playlist_url(url):
    validate_playlist_url(SPOTIFY_PLAYLIST_URL_PAT, url, "Spotify")
