# -*- coding: utf-8 -*-
"""TMDb access of the engine (docs/plan/pro-tebe/010 §4): every answer goes through the generic `http:` cache in
library.db (wcs/metadata/TMDbCache.py), so a rebuild is mostly offline. Errors are never cached.

Tests replace `FETCH` with a function (path, params) -> dict.
"""
import threading

import xbmc

BASE = 'https://api.themoviedb.org/3/'
LANGUAGE = 'cs-CZ'
DETAIL_APPEND = 'credits,keywords,recommendations,similar,content_ratings,release_dates'
TTL_LIST = 12 * 3600
TTL_DETAIL = 14 * 86400
TTL_GENRES = 30 * 86400

FETCH = None
_counter_lock = threading.Lock()
STATS = {'network': 0, 'cached': 0, 'failed': 0}


def _http(path, params):
    import requests
    import xbmcaddon
    from wcs.metadata.TMDbClient import get_tmdb_api_key
    query = dict(params)
    query['api_key'] = get_tmdb_api_key(xbmcaddon.Addon('plugin.video.milionar'))
    response = requests.get(BASE + path, params=query, timeout=12)
    response.raise_for_status()
    return response.json()


def _count(name):
    with _counter_lock:
        STATS[name] += 1


def get(path, params=None, ttl=TTL_LIST):
    """Cached TMDb JSON or None (offline, no key, error)."""
    from wcs.metadata import TMDbCache
    params = dict(params or {})
    params.setdefault('language', LANGUAGE)
    cached = TMDbCache.get_from_cache(path, params, ttl)
    if cached is not None:
        _count('cached')
        return cached
    try:
        data = (FETCH or _http)(path, params)
    except Exception as exc:
        _count('failed')
        xbmc.log('[Milionář ProTebe] TMDb {} failed: {}: {}'.format(path, type(exc).__name__, exc), xbmc.LOGWARNING)
        return None
    if not isinstance(data, dict):
        _count('failed')
        return None
    _count('network')
    TMDbCache.set_to_cache(path, params, data)
    return data


def detail(media, tmdb_id):
    return get('{}/{}'.format(media, int(tmdb_id)), {'append_to_response': DETAIL_APPEND}, TTL_DETAIL)


def results(path, params=None, ttl=TTL_LIST):
    data = get(path, params, ttl) or {}
    return [r for r in data.get('results') or [] if isinstance(r, dict) and r.get('id')]


TTL_LOGO = 7 * 86400
LOGO_BASE = 'https://image.tmdb.org/t/p/w500'


def logo(media, tmdb_id):
    """URL of the title's logo (Czech > English > textless, no SVG) or ''. Shares the cache entry of
    wcs/metadata/TMDbLogo.py (only the chosen logo is stored, not the whole image list)."""
    from wcs.metadata import TMDbCache
    from wcs.metadata.TMDbLogo import pick_logo
    endpoint = '{}/{}/images'.format(media, int(tmdb_id))
    cached = TMDbCache.get_from_cache(endpoint, {'kind': 'logo'}, TTL_LOGO)
    if cached is not None:
        _count('cached')
        return cached.get('url') or ''
    try:
        data = (FETCH or _http)(endpoint, {'include_image_language': 'cs,en,null'})
    except Exception as exc:
        _count('failed')
        xbmc.log('[Milionář ProTebe] logo {} failed: {}: {}'.format(endpoint, type(exc).__name__, exc), xbmc.LOGWARNING)
        return ''
    _count('network')
    chosen = pick_logo((data or {}).get('logos') or [])
    result = {'url': LOGO_BASE + chosen['file_path'], 'aspect': (chosen.get('width') or 0) / float(chosen.get('height') or 1)} \
        if chosen else {'url': ''}
    TMDbCache.set_to_cache(endpoint, {'kind': 'logo'}, result)
    return result['url']


def genre_names():
    """{genre_id: localized name} for films and series."""
    out = {}
    for media in ('tv', 'movie'):          # film names win for the shared ids
        data = get('genre/{}/list'.format(media), None, TTL_GENRES) or {}
        for genre in data.get('genres') or []:
            out[int(genre['id'])] = genre.get('name') or ''
    return out


def parallel(calls, workers=6):
    """Run zero-argument callables on a few threads; results in the same order (None for a failure)."""
    calls = list(calls)
    results_ = [None] * len(calls)
    if not calls:
        return results_
    index = {'next': 0}
    lock = threading.Lock()

    def worker():
        while True:
            with lock:
                i = index['next']
                index['next'] += 1
            if i >= len(calls):
                return
            try:
                results_[i] = calls[i]()
            except Exception as exc:
                xbmc.log('[Milionář ProTebe] task failed: {}: {}'.format(type(exc).__name__, exc), xbmc.LOGWARNING)
    threads = [threading.Thread(target=worker) for _ in range(min(workers, len(calls)))]
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()
    return results_
