# -*- coding: utf-8 -*-
# plugin.video.alkoflix
# resources/lib/modules/list_sorting.py
# © Les alKODIques

import random
from indexers.metadata import movie_meta, tvshow_meta
from modules import kodi_utils, settings
from modules.utils import get_datetime, sort_for_article


_LEGACY_SORT_MAP = {
	'': 'original',
	'original': 'original',
	'default': 'original',
	'predefined': 'original',
	'title': 'title_asc',
	'title_az': 'title_asc',
	'title_za': 'title_desc',
	'date': 'date_desc',
	'new': 'date_desc',
	'newest': 'date_desc',
	'oldest': 'date_asc',
	'added': 'added_desc',
	'listed': 'added_desc',
	'rated': 'rating_desc',
	'rating': 'rating_desc',
	'watched': 'watched_desc',
	'played': 'watched_desc',
	'random': 'random'
}


def normalise_list_sort_key(list_sort):
	list_sort = str(list_sort or '').strip().lower()
	return _LEGACY_SORT_MAP.get(list_sort, list_sort or 'original')


def _metadata_user_info():
	# Le tri de listes a seulement besoin des métadonnées textuelles.
	# On évite donc d'appeler Fanart.tv pendant ce pré-traitement.
	try:
		user_info = dict(settings.metadata_user_info())
		user_info['extra_fanart_enabled'] = False
		return user_info
	except:
		return settings.metadata_user_info()


def _ids_from_trakt_item(item):
	media_type = item.get('type')
	if media_type == 'movie': return 'movie', (item.get('movie') or {}).get('ids') or {}
	if media_type in ('show', 'season', 'episode'):
		show = item.get('show') or {}
		if not show and media_type in item:
			show = (item.get(media_type) or {}).get('show') or {}
		return 'tvshow', show.get('ids') or {}
	return None, {}


def _ids_from_mdbl_item(item):
	media_type = 'movie' if item.get('mediatype') == 'movie' else 'tvshow' if item.get('mediatype') == 'show' else None
	return media_type, {'tmdb': item.get('id') or item.get('tmdb') or item.get('tmdb_id'), 'imdb': item.get('imdb_id') or item.get('imdb')}


def _meta_for_item(service, item, user_info, current_date):
	try:
		media_type, ids = _ids_from_trakt_item(item) if service == 'trakt' else _ids_from_mdbl_item(item)
		if not media_type or not ids: return {}
		meta = movie_meta('trakt_dict', ids, user_info, current_date) if media_type == 'movie' else tvshow_meta('trakt_dict', ids, user_info, current_date)
		meta = dict(meta or {})
		meta['_alkoflix_list_media_type'] = media_type
		return meta
	except Exception as exc:
		try: kodi_utils.logger('list sort metadata skipped', '%s: %s' % (type(exc).__name__, exc))
		except: pass
		return {}


def _nested_item(item):
	for key in ('movie', 'show', 'season', 'episode'):
		value = item.get(key)
		if isinstance(value, dict): return value
	return {}


def _first_text(*values):
	for value in values:
		if value not in ('', None): return str(value)
	return ''


def _fallback_title_value(item):
	nested = _nested_item(item)
	return _first_text(
		item.get('title'), item.get('name'), item.get('original_title'), item.get('original_name'),
		nested.get('title'), nested.get('name'), nested.get('original_title'), nested.get('original_name')
	).lower()


def _title_value(meta, item):
	return _first_text(meta.get('title'), meta.get('rootname'), meta.get('originaltitle'), meta.get('original_title'), _fallback_title_value(item)).lower()


def _fallback_date_value(item):
	nested = _nested_item(item)
	return _first_text(
		item.get('released'), item.get('first_aired'), item.get('release_date'), item.get('air_date'), item.get('year'), item.get('release_year'),
		nested.get('released'), nested.get('first_aired'), nested.get('release_date'), nested.get('air_date'), nested.get('year')
	)


def _date_value(meta, item):
	return _first_text(meta.get('premiered'), meta.get('aired'), meta.get('release_date'), _fallback_date_value(item))


def _added_value(item):
	nested = _nested_item(item)
	return _first_text(
		item.get('listed_at'), item.get('collected_at'), item.get('favorited_at'), item.get('updated_at'), item.get('created_at'), item.get('last_played'),
		nested.get('listed_at'), nested.get('collected_at'), nested.get('favorited_at'), nested.get('updated_at'), nested.get('created_at')
	)


def _float_value(*values):
	for value in values:
		try:
			if value not in ('', None): return float(str(value).replace(',', '.'))
		except: pass
	return 0.0


def _int_value(*values):
	for value in values:
		try:
			if value not in ('', None): return int(float(str(value).replace(',', '').replace(' ', '')))
		except: pass
	return 0


def _rating_value(meta, item):
	nested = _nested_item(item)
	return _float_value(
		meta.get('rating'), meta.get('vote_average'), item.get('rating'), item.get('score'), item.get('imdb_rating'),
		item.get('tmdb_rating'), nested.get('rating'), nested.get('score')
	)


def _votes_value(meta, item):
	nested = _nested_item(item)
	return _int_value(meta.get('votes'), meta.get('vote_count'), item.get('votes'), item.get('vote_count'), nested.get('votes'), nested.get('vote_count'))


def _sort_title(enriched, key, reverse=False):
	try: sorted_items = sort_for_article(enriched, key, settings.ignore_articles())
	except: sorted_items = sorted(enriched, key=lambda k: k.get(key) or '')
	if reverse: sorted_items.reverse()
	return sorted_items


def _shuffle_items(enriched, params):
	seed = str((params or {}).get('list_random_seed') or '')
	if seed: random.Random(seed).shuffle(enriched)
	else: random.shuffle(enriched)
	return enriched


def apply_context_list_sort(results, service, params):
	# Les listes Trakt/MDBList sont des listes utilisateurs thématiques.
	# On trie donc les éléments présents dans la liste, sans filtrage régional,
	# sans seuil de votes et sans obligation de visuel.
	list_sort = normalise_list_sort_key((params or {}).get('list_sort'))
	if list_sort == 'original': return results
	user_info, current_date = _metadata_user_info(), get_datetime()
	enriched = []
	for index, item in enumerate(results or [], 1):
		try: item_data = dict(item)
		except: item_data = item
		try:
			meta = _meta_for_item(service, item_data, user_info, current_date)
		except:
			meta = {}
		try:
			item_data['_alkoflix_list_sort_index'] = index
			item_data['_alkoflix_list_sort_title'] = _title_value(meta, item_data)
			item_data['_alkoflix_list_sort_date'] = _date_value(meta, item_data)
			item_data['_alkoflix_list_sort_added'] = _added_value(item_data)
			item_data['_alkoflix_list_sort_rating'] = _rating_value(meta, item_data)
			item_data['_alkoflix_list_sort_votes'] = _votes_value(meta, item_data)
		except:
			item_data['_alkoflix_list_sort_index'] = index
			item_data['_alkoflix_list_sort_title'] = _fallback_title_value(item_data)
			item_data['_alkoflix_list_sort_date'] = _fallback_date_value(item_data)
			item_data['_alkoflix_list_sort_added'] = _added_value(item_data)
			item_data['_alkoflix_list_sort_rating'] = 0
			item_data['_alkoflix_list_sort_votes'] = 0
		enriched.append(item_data)
	if list_sort == 'random': return _shuffle_items(enriched, params)
	if list_sort.startswith('title'):
		return _sort_title(enriched, '_alkoflix_list_sort_title', list_sort.endswith('_desc'))
	if list_sort.startswith('date'):
		enriched.sort(key=lambda k: (k.get('_alkoflix_list_sort_date') or '', k.get('_alkoflix_list_sort_title') or ''), reverse=list_sort.endswith('_desc'))
	elif list_sort.startswith('added'):
		enriched.sort(key=lambda k: (k.get('_alkoflix_list_sort_added') or '', k.get('_alkoflix_list_sort_title') or ''), reverse=list_sort.endswith('_desc'))
	elif list_sort.startswith('rating'):
		enriched.sort(key=lambda k: (k.get('_alkoflix_list_sort_rating') or 0, k.get('_alkoflix_list_sort_votes') or 0, k.get('_alkoflix_list_sort_title') or ''), reverse=list_sort.endswith('_desc'))
	return enriched


def _dict_value(item, *keys):
	for key in keys:
		try:
			value = item.get(key)
		except:
			value = None
		if value not in ('', None): return value
	return ''


def apply_simple_context_sort(results, params, title_key='title', date_key='premiered', added_key='collected_at', watched_key='last_played'):
	# Tri léger pour les listes déjà normalisées par alkoFlix : collection,
	# watchlist, favoris Trakt et historiques locaux/Trakt/MDBList.
	list_sort = normalise_list_sort_key((params or {}).get('list_sort'))
	if list_sort == 'original': return results
	try: enriched = list(results or [])
	except: enriched = results or []
	if not enriched: return enriched
	if list_sort == 'random': return _shuffle_items(enriched, params)
	if list_sort.startswith('title'):
		return _sort_title(enriched, title_key, list_sort.endswith('_desc'))
	if list_sort.startswith('date'):
		enriched.sort(key=lambda k: (_dict_value(k, date_key, 'premiered', 'first_aired', 'release_date', 'year'), _dict_value(k, title_key, 'title')), reverse=list_sort.endswith('_desc'))
	elif list_sort.startswith('added'):
		enriched.sort(key=lambda k: (_dict_value(k, added_key, 'listed_at', 'favorited_at', 'collected_at', 'created_at', 'last_played'), _dict_value(k, title_key, 'title')), reverse=list_sort.endswith('_desc'))
	elif list_sort.startswith('watched'):
		enriched.sort(key=lambda k: (_dict_value(k, watched_key, 'last_played', added_key), _dict_value(k, title_key, 'title')), reverse=list_sort.endswith('_desc'))
	elif list_sort.startswith('rating'):
		enriched.sort(key=lambda k: (_float_value(_dict_value(k, 'rating', 'vote_average', 'score')), _int_value(_dict_value(k, 'votes', 'vote_count')), _dict_value(k, title_key, 'title')), reverse=list_sort.endswith('_desc'))
	return enriched
