# -*- coding: utf-8 -*-
# plugin.video.alkoflix
# resources/lib/caches/favourites_cache.py

from caches import BaseCache, favourites_db, container_refresh
from modules import settings
from modules.utils import sort_for_article, paginate_list, get_datetime
from datetime import datetime
import time
import json
import re
import unicodedata
# from modules.kodi_utils import logger

INSERT_FAV = 'INSERT OR IGNORE INTO favourites (db_type, tmdb_id, title, added_at) VALUES (?, ?, ?, ?)'
UPSERT_FAV = 'INSERT OR REPLACE INTO favourites (db_type, tmdb_id, title, added_at) VALUES (?, ?, ?, ?)'
DELETE_FAV = 'DELETE FROM favourites where db_type = ? and tmdb_id = ?'
SELECT_FAV = 'SELECT tmdb_id, title, added_at FROM favourites WHERE db_type = ?'
CLEAR_FAV = 'DELETE FROM favourites WHERE db_type = ?'
INSERT_TOMBSTONE = 'INSERT OR REPLACE INTO favourite_tombstones (db_type, tmdb_id, title, deleted_at) VALUES (?, ?, ?, ?)'
DELETE_TOMBSTONE = 'DELETE FROM favourite_tombstones WHERE db_type = ? AND tmdb_id = ?'
SELECT_TOMBSTONES = 'SELECT db_type, tmdb_id, title, deleted_at FROM favourite_tombstones'

# Les clés ci-dessous gardent le type média réel dans le nom du favori local.
# Cela permet d'avoir des dossiers de favoris spécialisés sans multiplier les
# types techniques Kodi utilisés pour la lecture.
FAVOURITE_TYPES = (
	'movie', 'tvshow', 'season', 'episode',
	'movie_family', 'tv_family', 'season_family', 'episode_family',
	'movie_animation', 'tv_animation', 'season_animation', 'episode_animation',
	'anime_movie', 'anime_tv', 'anime_season', 'anime_episode',
	'movie_documentary', 'movie_tv_movie',
	'tv_documentary', 'tv_reality', 'tv_talkshow', 'tv_news', 'season_documentary', 'episode_documentary',
	'person', 'list', 'collection', 'collection_family', 'collection_animation', 'collection_anime'
)


# Les familles favorites restent locales. Les services externes ne voient que
# les films et séries globaux, tandis qu'alkoFlix conserve le classement précis.
MOVIE_FAVOURITE_TYPES = ('movie', 'movie_family', 'movie_animation', 'anime_movie', 'movie_documentary', 'movie_tv_movie')
TV_FAVOURITE_TYPES = ('tvshow', 'tv_family', 'tv_animation', 'anime_tv', 'tv_documentary', 'tv_reality', 'tv_talkshow', 'tv_news')
SEASON_FAVOURITE_TYPES = ('season', 'season_family', 'season_animation', 'anime_season', 'season_documentary')
EPISODE_FAVOURITE_TYPES = ('episode', 'episode_family', 'episode_animation', 'anime_episode', 'episode_documentary')
MOVIE_SPECIAL_FAVOURITE_TYPES = tuple(i for i in MOVIE_FAVOURITE_TYPES if i != 'movie')
TV_SPECIAL_FAVOURITE_TYPES = tuple(i for i in TV_FAVOURITE_TYPES if i != 'tvshow')
SEASON_SPECIAL_FAVOURITE_TYPES = tuple(i for i in SEASON_FAVOURITE_TYPES if i != 'season')
EPISODE_SPECIAL_FAVOURITE_TYPES = tuple(i for i in EPISODE_FAVOURITE_TYPES if i != 'episode')
TV_OBJECT_FAVOURITE_TYPES = TV_FAVOURITE_TYPES + SEASON_FAVOURITE_TYPES + EPISODE_FAVOURITE_TYPES
COLLECTION_FAVOURITE_TYPES = ('collection', 'collection_family', 'collection_animation', 'collection_anime')
LIST_FAVOURITE_TYPES = ('list',)
ALL_LOCAL_FAVOURITE_TYPES = MOVIE_FAVOURITE_TYPES + TV_OBJECT_FAVOURITE_TYPES + ('person',) + LIST_FAVOURITE_TYPES + COLLECTION_FAVOURITE_TYPES

CUSTOM_FAVOURITES_SETTING = 'favourites.custom_folders'
CUSTOM_FAVOURITE_PREFIX = 'custom:'
CUSTOM_FAVOURITE_MEDIA_TYPES = ('movie', 'tvshow', 'season', 'episode', 'collection', 'person', 'list')
CUSTOM_FAVOURITES_ENABLED = False

SEASON_FAVOURITES_FROM_TV = {
	'tvshow': 'season',
	'tv_family': 'season_family',
	'tv_animation': 'season_animation',
	'anime_tv': 'anime_season',
	'tv_documentary': 'season_documentary',
	'tv_reality': 'season_documentary',
	'tv_talkshow': 'season_documentary',
	'tv_news': 'season_documentary'
}
EPISODE_FAVOURITES_FROM_TV = {
	'tvshow': 'episode',
	'tv_family': 'episode_family',
	'tv_animation': 'episode_animation',
	'anime_tv': 'anime_episode',
	'tv_documentary': 'episode_documentary',
	'tv_reality': 'episode_documentary',
	'tv_talkshow': 'episode_documentary',
	'tv_news': 'episode_documentary'
}

MOVIE_SPECIAL_FAVOURITES = {
	'movie_family': 'movie_family',
	'movie_family_kids': 'movie_family',
	'movie_animation': 'movie_animation',
	'anime_movie': 'anime_movie',
	'movie_documentary': 'movie_documentary',
	'movie_tv_movie': 'movie_documentary'
}

TV_SPECIAL_FAVOURITES = {
	'series_family': 'tv_family',
	'series_family_kids': 'tv_family',
	'series_animation': 'tv_animation',
	'anime_series': 'anime_tv',
	'anime_tv': 'anime_tv',
	'series_documentary': 'tv_documentary',
	'series_reality': 'tv_documentary',
	'series_talkshow': 'tv_documentary',
	'series_news': 'tv_documentary',
	'tv_documentary': 'tv_documentary',
	'tv_reality': 'tv_documentary',
	'tv_talkshow': 'tv_documentary',
	'tv_news': 'tv_documentary'
}

COLLECTION_SPECIAL_FAVOURITES = {
	'family': 'collection_family',
	'movie_family': 'collection_family',
	'animation': 'collection_animation',
	'movie_animation': 'collection_animation',
	'anime': 'collection_anime',
	'anime_movie': 'collection_anime'
}


def _safe_custom_folder_name(name):
	name = str(name or '').strip()
	name = re.sub(r'\s+', ' ', name)
	return name[:40]


def _custom_folder_id_from_name(name):
	value = unicodedata.normalize('NFKD', str(name or '')).encode('ascii', 'ignore').decode('ascii').lower()
	value = re.sub(r'[^a-z0-9]+', '-', value).strip('-')
	if not value: value = 'dossier'
	return value[:32]


def get_custom_folders():
	# Les dossiers favoris personnalisés sont désactivés.
	# Les favoris restent classés dans les dossiers standards afin d'éviter les
	# mélanges de types média impossibles à reconstruire proprement dans Kodi.
	try:
		settings.set_setting(CUSTOM_FAVOURITES_SETTING, '[]')
	except Exception:
		pass
	return []


def save_custom_folders(folders):
	# Compatibilité avec les anciennes sauvegardes WIP : on ignore les dossiers
	# personnalisés et on nettoie le réglage invisible local.
	try:
		settings.set_setting(CUSTOM_FAVOURITES_SETTING, '[]')
	except Exception:
		pass
	return []


def create_custom_folder(name):
	return None


def custom_favourite_type(folder_id, media_type):
	return ''


def parse_custom_favourite_type(favourite_type):
	return None, None


def is_custom_favourite_type(favourite_type):
	return False


def custom_favourite_types(folder_id=None, media_types=None):
	return ()


def all_local_favourite_types(include_custom=True):
	return tuple(dict.fromkeys(ALL_LOCAL_FAVOURITE_TYPES))


def get_custom_folder_name(folder_id, fallback='Dossier personnalisé'):
	return fallback

def collection_favourite_type_from_saga_type(saga_type):
	return COLLECTION_SPECIAL_FAVOURITES.get(_normal_text(saga_type), 'collection')


def season_favourite_type_from_tv(tv_fav_type):
	return SEASON_FAVOURITES_FROM_TV.get(_normal_text(tv_fav_type), 'season')


def episode_favourite_type_from_tv(tv_fav_type):
	return EPISODE_FAVOURITES_FROM_TV.get(_normal_text(tv_fav_type), 'episode')


def tv_favourite_type_from_child(fav_type):
	fav_type = _normal_text(fav_type)
	if fav_type in SEASON_FAVOURITE_TYPES:
		for tv_type, season_type in SEASON_FAVOURITES_FROM_TV.items():
			if season_type == fav_type: return tv_type
	if fav_type in EPISODE_FAVOURITE_TYPES:
		for tv_type, episode_type in EPISODE_FAVOURITES_FROM_TV.items():
			if episode_type == fav_type: return tv_type
	return 'tvshow'


def child_favourite_type_from_parent(media_type, parent_fav_type):
	media_type = _base_media_type(media_type)
	if media_type == 'season': return season_favourite_type_from_tv(parent_fav_type)
	if media_type == 'episode': return episode_favourite_type_from_tv(parent_fav_type)
	return _normal_text(parent_fav_type) or media_type



def _normal_text(value):
	try:
		return str(value or '').strip().lower()
	except:
		return ''


def _meta_values(meta, key):
	try: value = (meta or {}).get(key)
	except: value = None
	if isinstance(value, (list, tuple, set)): return [_normal_text(i) for i in value if _normal_text(i)]
	return [_normal_text(i) for i in str(value or '').replace('|', ',').split(',') if _normal_text(i)]


def _meta_has_any(meta, key, names):
	values = _meta_values(meta, key)
	return any(any(name in value for name in names) for value in values)


def _meta_genre_ids(meta):
	ids = set()
	try: values = (meta or {}).get('genre_ids') or []
	except: values = []
	if isinstance(values, (str, bytes)):
		values = str(values).replace('|', ',').split(',')
	for value in values:
		value = str(value or '').strip()
		if value: ids.add(value)
	return ids


def _meta_has_genre_id(meta, ids):
	ids = set(str(i) for i in ids)
	return bool(_meta_genre_ids(meta).intersection(ids))


def _meta_has_genre_name(meta, names):
	return _meta_has_any(meta, 'genre', names) or _meta_has_any(meta, 'genres', names)


def _meta_is_japanese(meta):
	country_codes = [i.upper() for i in _meta_values(meta, 'country_codes')]
	if 'JP' in country_codes: return True
	countries = _meta_values(meta, 'country')
	if any(i in countries for i in ('japan', 'japon')): return True
	language = _normal_text((meta or {}).get('original_language') or (meta or {}).get('original_lang'))
	return language == 'ja'


def _base_media_type(media_type):
	media_type = _normal_text(media_type)
	if media_type in ('tv', 'show', 'shows', 'series', 'tvshows', 'tvshow'): return 'tvshow'
	if media_type in ('movie', 'movies', 'film', 'films'): return 'movie'
	if media_type in ('person', 'people', 'actor', 'actress'): return 'person'
	if media_type in ('list', 'lists', 'liste', 'listes'): return 'list'
	if media_type in ('season', 'seasons'): return 'season'
	if media_type in ('episode', 'episodes'): return 'episode'
	if media_type in ('collection', 'collections', 'set', 'sets', 'saga', 'sagas'): return 'collection'
	return media_type


def favourite_type_from_context(media_type, params=None, action='', meta=None):
	media_type = _base_media_type(media_type)
	params = params or {}
	fav_type = _normal_text(params.get('fav_type') or params.get('favourite_type') or params.get('favorite_type'))
	if fav_type and fav_type not in ('movie', 'tvshow'):
		if fav_type in ('movie_tv_movie',): return 'movie_documentary'
		if fav_type in ('tv_reality', 'tv_talkshow', 'tv_news'): return 'tv_documentary'
		return fav_type

	if media_type in ('season', 'episode'):
		parent_fav_type = _normal_text(params.get('parent_fav_type') or params.get('series_fav_type') or params.get('tv_fav_type'))
		if parent_fav_type: return child_favourite_type_from_parent(media_type, parent_fav_type)
		return media_type
	if media_type in ('person', 'list'): return media_type
	if media_type == 'collection':
		return collection_favourite_type_from_saga_type(params.get('saga_type'))

	documentary_type = _normal_text(params.get('documentary_type'))
	if documentary_type:
		if documentary_type in ('movie_documentary', 'movie_tv_movie') or documentary_type.startswith('movie'): return 'movie_documentary'
		if documentary_type.startswith('series') or documentary_type.startswith('tv'): return 'tv_documentary'
	family_type = _normal_text(params.get('family_type'))
	if family_type:
		if media_type == 'movie': return MOVIE_SPECIAL_FAVOURITES.get(family_type, 'movie')
		if media_type == 'tvshow': return TV_SPECIAL_FAVOURITES.get(family_type, 'tvshow')
	# Certains anciens raccourcis n'avaient pas family_type mais transportaient
	# déjà le groupe de favoris. On l'utilise comme filet de sécurité.
	fav_group = _normal_text(params.get('fav_group') or params.get('group'))
	if media_type == 'movie' and fav_group == 'family': return 'movie_family'
	if media_type == 'tvshow' and fav_group in ('family', 'series_family'): return 'tv_family'
	native_type = _normal_text(params.get('native_type'))
	if native_type == 'anime_movie': return 'anime_movie'
	if native_type == 'anime_series': return 'anime_tv'

	action = _normal_text(action or params.get('action'))
	if media_type == 'movie':
		# Les listes de favoris spécialisées doivent aussi transmettre leur contexte
		# lorsque l'URL vient d'un ancien cache sans fav_type explicite.
		if action == 'favourites_movie_family': return 'movie_family'
		if action == 'favourites_movie_animation': return 'movie_animation'
		if action == 'favourites_anime_movie': return 'anime_movie'
		if action in ('favourites_movie_documentary', 'favourites_movie_tv_movie'): return 'movie_documentary'
		if action.startswith(('tmdb_moviesanime_', 'trakt_moviesanime_')): return 'anime_movie'
		if action.startswith('tmdb_movies_family_animation_'): return 'movie_animation'
		if action.startswith('tmdb_movies_family_'): return 'movie_family'
		if action.startswith(('tmdb_movies_documentary_', 'tmdb_movies_tv_movie_')): return 'movie_documentary'
	elif media_type == 'tvshow':
		# Même principe pour les séries : si le menu Famille/Favoris arrive
		# sans fav_type, l'action suffit à retrouver le bon panier local.
		if action == 'favourites_tv_family': return 'tv_family'
		if action == 'favourites_tv_animation': return 'tv_animation'
		if action == 'favourites_anime_tv': return 'anime_tv'
		if action in ('favourites_tv_documentary', 'favourites_tv_reality', 'favourites_tv_talkshow', 'favourites_tv_news'): return 'tv_documentary'
		if action.startswith(('tmdb_tvanime_', 'trakt_tvanime_')): return 'anime_tv'
		if action.startswith('tmdb_tv_family_animation_'): return 'tv_animation'
		if action.startswith('tmdb_tv_family_'): return 'tv_family'
		# tmdb_tv_networks est utilisé ailleurs que dans Famille. On ne le classe
		# Famille que si le menu a réellement transmis un family_type.
		if action == 'tmdb_tv_networks' and family_type: return 'tv_family'
		if action.startswith(('tmdb_tv_documentary_', 'tmdb_tv_reality_', 'tmdb_tv_talkshow_', 'tmdb_tv_news_')): return 'tv_documentary'

	# Sans contexte explicite de menu, on peut classer le favori grâce aux genres
	# TMDb déjà connus. Cela évite que les ajouts faits depuis une recherche, une
	# fiche d'info ou un widget tombent systématiquement dans les favoris globaux.
	# Les contextes de menu transmis plus haut gardent toujours la priorité.
	if meta and media_type == 'movie':
		if _meta_is_japanese(meta) and (_meta_has_genre_id(meta, ('16',)) or _meta_has_genre_name(meta, ('animation', 'anime', 'animé'))): return 'anime_movie'
		if _meta_has_genre_id(meta, ('99', '10770')) or _meta_has_genre_name(meta, ('documentary', 'documentaire', 'tv movie', 'téléfilm', 'telefilm')): return 'movie_documentary'
		if _meta_has_genre_id(meta, ('10751',)) or _meta_has_genre_name(meta, ('family', 'familial', 'famille')): return 'movie_family'
		if _meta_has_genre_id(meta, ('16',)) or _meta_has_genre_name(meta, ('animation', 'animé', 'anime')): return 'movie_animation'
	elif meta and media_type == 'tvshow':
		if _meta_is_japanese(meta) and (_meta_has_genre_id(meta, ('16',)) or _meta_has_genre_name(meta, ('animation', 'anime', 'animé'))): return 'anime_tv'
		if _meta_has_genre_id(meta, ('99', '10763', '10764', '10767')) or _meta_has_genre_name(meta, ('documentary', 'documentaire', 'news', 'actualité', 'actualités', 'reality', 'téléréalité', 'telerealite', 'talk', 'talkshow')): return 'tv_documentary'
		if _meta_has_genre_id(meta, ('10751', '10762')) or _meta_has_genre_name(meta, ('family', 'familial', 'famille', 'kids', 'enfants', 'jeunesse')): return 'tv_family'
		if _meta_has_genre_id(meta, ('16',)) or _meta_has_genre_name(meta, ('animation', 'animé', 'anime')): return 'tv_animation'

	return media_type


def local_favourite_keys(media_type, favourite_type):
	media_type = _base_media_type(media_type)
	favourite_type = favourite_type_from_context(media_type, {'fav_type': favourite_type})
	# Un favori local ne doit vivre que dans sa catégorie alkoFlix réelle.
	# Trakt/MDBList/TMDb restent une sauvegarde externe globale films/séries,
	# mais on ne duplique plus les documentaires/animes/animations dans les
	# paniers racine movie/tvshow.
	return [favourite_type]

class Favourites(BaseCache):
	db_file = favourites_db

	def __init__(self):
		BaseCache.__init__(self)
		self._ensure_schema()

	def _ensure_schema(self):
		# Migration douce : les anciennes bases de favoris n'avaient pas de date d'ajout.
		try:
			columns = [i[1] for i in self.dbcur.execute("""PRAGMA table_info(favourites)""").fetchall()]
			if 'added_at' not in columns:
				self.dbcur.execute("""ALTER TABLE favourites ADD COLUMN added_at text""")
				now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
				self.dbcur.execute("""UPDATE favourites SET added_at = ? WHERE added_at IS NULL OR added_at = ''""", (now,))
		except: pass
		try:
			self.dbcur.execute("""CREATE TABLE IF NOT EXISTS favourite_tombstones (db_type text, tmdb_id text, title text, deleted_at text, PRIMARY KEY (db_type, tmdb_id))""")
		except: pass
		# Les favoris sont indépendants par menu.
		# On ne supprime pas un favori global simplement parce que le même média existe
		# aussi dans un panier spécialisé (Famille, Animation, Docus, Anime, etc.).
		# Le dossier racine « Favoris » déduplique seulement à l'affichage.

	def _now(self):
		# Timestamp portable entre Windows / Android / Linux.
		# Les anciennes dates texte restent lisibles par la couche de synchro.
		return str(int(time.time()))

	def _active_title(self, media_type, tmdb_id, fallback=''):
		try:
			self.dbcur.execute("""SELECT title FROM favourites WHERE db_type = ? AND tmdb_id = ?""", (media_type, str(tmdb_id)))
			row = self.dbcur.fetchone()
			if row and row[0]: return str(row[0])
		except: pass
		return fallback or ''

	def record_favourite_tombstone(self, media_type, tmdb_id, title='', deleted_at=''):
		try:
			deleted_at = deleted_at or self._now()
			title = title or self._active_title(media_type, tmdb_id, '')
			self.dbcur.execute(INSERT_TOMBSTONE, (media_type, str(tmdb_id), title or '', deleted_at))
			return True
		except: return False

	def delete_favourite_tombstone(self, media_type, tmdb_id):
		try:
			self.dbcur.execute(DELETE_TOMBSTONE, (media_type, str(tmdb_id)))
			return True
		except: return False

	def add_to_favourites(self, media_type, tmdb_id, title):
		try:
			added_at = self._now()
			self.dbcur.execute(UPSERT_FAV, (media_type, str(tmdb_id), title, added_at))
			self.delete_favourite_tombstone(media_type, tmdb_id)
			return True
		except: return False

	def add_to_favourites_at(self, media_type, tmdb_id, title, added_at=''):
		try:
			added_at = added_at or self._now()
			self.dbcur.execute(UPSERT_FAV, (media_type, str(tmdb_id), title, added_at))
			self.delete_favourite_tombstone(media_type, tmdb_id)
			return True
		except: return False

	def remove_from_favourites_at(self, media_type, tmdb_id, title='', deleted_at='', refresh=True):
		try:
			title = title or self._active_title(media_type, tmdb_id, '')
			self.record_favourite_tombstone(media_type, tmdb_id, title, deleted_at or self._now())
			self.dbcur.execute(DELETE_FAV, (media_type, str(tmdb_id)))
			if is_custom_favourite_type(media_type):
				try: prune_empty_custom_folders()
				except: pass
			if refresh: container_refresh()
			return True
		except: return False

	def remove_from_favourites(self, media_type, tmdb_id, title=''):
		return self.remove_from_favourites_at(media_type, tmdb_id, title, '', True)

	def get_favourites(self, media_type):
		try: self._ensure_schema()
		except: pass
		self.dbcur.execute(SELECT_FAV, (media_type,))
		result = self.dbcur.fetchall()
		result = [{'tmdb_id': str(i[0]), 'title': str(i[1]), 'added_at': str(i[2] or '')} for i in result]
		return _sort_favourites(media_type, result)

	def clear_favourites(self, media_type):
		try:
			deleted_at = self._now()
			self.dbcur.execute(SELECT_FAV, (media_type,))
			for tmdb_id, title, added_at in self.dbcur.fetchall() or []:
				self.record_favourite_tombstone(media_type, tmdb_id, title, deleted_at)
			self.dbcur.execute(CLEAR_FAV, (media_type,))
			if is_custom_favourite_type(media_type):
				try: prune_empty_custom_folders()
				except: pass
			self.dbcur.execute("""VACUUM""")
			return True
		except: return False

	def get_tombstones(self, favourite_types=None):
		try: self._ensure_schema()
		except: pass
		try:
			if isinstance(favourite_types, str): favourite_types = (favourite_types,)
			favourite_types = tuple(favourite_types or ())
			if favourite_types:
				placeholders = ','.join('?' for _ in favourite_types)
				self.dbcur.execute("""SELECT db_type, tmdb_id, title, deleted_at FROM favourite_tombstones WHERE db_type IN (%s)""" % placeholders, favourite_types)
			else:
				self.dbcur.execute(SELECT_TOMBSTONES)
			return [{'db_type': str(i[0]), 'tmdb_id': str(i[1]), 'title': str(i[2] or ''), 'deleted_at': str(i[3] or '')} for i in self.dbcur.fetchall() or []]
		except: return []

	def get_tombstone(self, media_type, tmdb_id):
		try:
			self._ensure_schema()
			self.dbcur.execute("""SELECT title, deleted_at FROM favourite_tombstones WHERE db_type = ? AND tmdb_id = ?""", (media_type, str(tmdb_id)))
			row = self.dbcur.fetchone()
			if row: return {'title': str(row[0] or ''), 'deleted_at': str(row[1] or '')}
		except: pass
		return None

	def get_favourite_row(self, media_type, tmdb_id):
		try:
			self._ensure_schema()
			self.dbcur.execute("""SELECT title, added_at FROM favourites WHERE db_type = ? AND tmdb_id = ?""", (media_type, str(tmdb_id)))
			row = self.dbcur.fetchone()
			if row: return {'title': str(row[0] or ''), 'added_at': str(row[1] or '')}
		except: pass
		return None

	def count_favourites(self, media_types):
		try:
			if isinstance(media_types, str): media_types = (media_types,)
			media_types = tuple(str(i) for i in media_types if str(i or '').strip())
			if not media_types: return 0
			placeholders = ','.join('?' for _ in media_types)
			self.dbcur.execute("""SELECT COUNT(1) FROM favourites WHERE db_type IN (%s)""" % placeholders, media_types)
			row = self.dbcur.fetchone()
			return int(row[0] or 0) if row else 0
		except: return 0

	def has_favourites(self, media_types):
		return self.count_favourites(media_types) > 0

favourites_cache = Favourites()


def is_favourite(favourite_type, tmdb_id):
	try:
		if tmdb_id in ('', 'None', None): return False
		return any(str(i.get('tmdb_id')) == str(tmdb_id) for i in favourites_cache.get_favourites(favourite_type))
	except: return False

def favourites_context_label(favourite_type, tmdb_id):
	# Libellé volontairement neutre : l'action réelle (ajout/retrait) est confirmée dans la boîte de dialogue.
	return 'Favoris (alkoFlix)'

def clear_favourites_many(favourite_types):
	success = False
	for favourite_type in favourite_types:
		try: success = favourites_cache.clear_favourites(favourite_type) or success
		except: pass
	try: prune_empty_custom_folders()
	except: pass
	return success


def has_favourites_types(favourite_types):
	try:
		return favourites_cache.has_favourites(favourite_types)
	except: return False


def count_favourites_types(favourite_types):
	try:
		return favourites_cache.count_favourites(favourite_types)
	except: return 0


def prune_empty_custom_folders():
	try:
		folders = get_custom_folders()
		if not folders: return []
		kept = []
		for folder in folders:
			folder_id = folder.get('id')
			if not folder_id: continue
			if favourites_cache.has_favourites(custom_favourite_types(folder_id)):
				kept.append(folder)
		if len(kept) != len(folders): save_custom_folders(kept)
		return kept
	except:
		return get_custom_folders()

def _release_date_for_item(favourite_type, item):
	try:
		if favourite_type not in MOVIE_FAVOURITE_TYPES + TV_FAVOURITE_TYPES: return ''
		from indexers.metadata import movie_meta, tvshow_meta
		user_info = settings.metadata_user_info()
		current_date = get_datetime()
		if favourite_type in MOVIE_FAVOURITE_TYPES:
			meta = movie_meta('tmdb_id', item.get('tmdb_id'), user_info, current_date) or {}
		else:
			meta = tvshow_meta('tmdb_id', item.get('tmdb_id'), user_info, current_date) or {}
		return meta.get('premiered') or ''
	except: return ''


def _sort_favourites(favourite_type, data):
	try: sort_key = settings.lists_sort_order('favorites')
	except: sort_key = 0
	reverse = settings.lists_sort_reverse('favorites')
	if sort_key == 1:
		return sorted(data, key=lambda k: k.get('added_at') or '', reverse=reverse)
	if sort_key == 2:
		return sorted(data, key=lambda k: _release_date_for_item(favourite_type, k), reverse=reverse)
	data = sort_for_article(data, 'title', settings.ignore_articles())
	if reverse: data.reverse()
	return data


def _paginate_favourites_list(original_list, page_no, letter):
	paginate = settings.paginate()
	limit = settings.page_limit()
	try: page_no = int(page_no)
	except: page_no = 1
	if not original_list: return [], 1
	if paginate: final_list, total_pages = paginate_list(original_list, page_no, letter, limit)
	else: final_list, total_pages = original_list, 1
	return final_list or [], total_pages or 1


def get_favourites_by_type(favourite_type, page_no, letter):
	data = favourites_cache.get_favourites(favourite_type) or []
	original_list = [{'media_id': i['tmdb_id'], 'title': i['title'], 'added_at': i.get('added_at', '')} for i in data if i.get('tmdb_id')]
	return _paginate_favourites_list(original_list, page_no, letter)


def get_favourites_custom(favourite_type, page_no, letter):
	return get_favourites_by_type(favourite_type, page_no, letter)


def get_favourites_by_types(favourite_types, page_no, letter):
	# Agrégats utilisés uniquement par le dossier racine « Favoris ».
	# Les favoris restent stockés avec leur type réel dans favourites.db.
	seen, original_list = set(), []
	for favourite_type in favourite_types:
		for item in favourites_cache.get_favourites(favourite_type) or []:
			tmdb_id = item.get('tmdb_id')
			if not tmdb_id: continue
			key = str(tmdb_id)
			if key in seen: continue
			seen.add(key)
			original_list.append({'media_id': key, 'tmdb_id': key, 'title': item.get('title', ''), 'added_at': item.get('added_at', ''), 'favourite_type': favourite_type})
	try: sort_key = settings.lists_sort_order('favorites')
	except: sort_key = 1
	reverse = settings.lists_sort_reverse('favorites')
	if sort_key == 1:
		original_list = sorted(original_list, key=lambda k: k.get('added_at') or '', reverse=reverse)
	elif sort_key == 2:
		original_list = sorted(original_list, key=lambda k: _release_date_for_item(k.get('favourite_type'), k), reverse=reverse)
	else:
		original_list = sort_for_article(original_list, 'title', settings.ignore_articles())
		if reverse: original_list.reverse()
	return _paginate_favourites_list(original_list, page_no, letter)


def get_favourites(media_type, page_no, letter):
	return get_favourites_by_type(media_type, page_no, letter)


def get_favourites_movies_all(media_type, page_no, letter):
	# Tous les films non japonais : films racine + famille + animation + docus/téléfilms.
	return get_favourites_by_types(('movie', 'movie_family', 'movie_animation', 'movie_documentary', 'movie_tv_movie'), page_no, letter)


def get_favourites_tvshows_all(media_type, page_no, letter):
	# Toutes les séries non japonaises : séries racine + famille + animation + docus/télé.
	return get_favourites_by_types(('tvshow', 'tv_family', 'tv_animation', 'tv_documentary', 'tv_reality', 'tv_talkshow', 'tv_news'), page_no, letter)


def get_favourites_seasons_all(media_type, page_no, letter):
	return get_favourites_by_types(('season', 'season_family', 'season_animation', 'season_documentary'), page_no, letter)


def get_favourites_episodes_all(media_type, page_no, letter):
	return get_favourites_by_types(('episode', 'episode_family', 'episode_animation', 'episode_documentary'), page_no, letter)

def get_favourites_movie_family(media_type, page_no, letter):
	return get_favourites_by_type('movie_family', page_no, letter)

def get_favourites_tv_family(media_type, page_no, letter):
	return get_favourites_by_type('tv_family', page_no, letter)

def get_favourites_movie_animation(media_type, page_no, letter):
	return get_favourites_by_type('movie_animation', page_no, letter)

def get_favourites_tv_animation(media_type, page_no, letter):
	return get_favourites_by_type('tv_animation', page_no, letter)

def get_favourites_anime_movie(media_type, page_no, letter):
	return get_favourites_by_type('anime_movie', page_no, letter)

def get_favourites_anime_tv(media_type, page_no, letter):
	return get_favourites_by_type('anime_tv', page_no, letter)

def get_favourites_movie_documentary(media_type, page_no, letter):
	return get_favourites_by_type('movie_documentary', page_no, letter)

def get_favourites_movie_tv_movie(media_type, page_no, letter):
	return get_favourites_by_type('movie_documentary', page_no, letter)

def get_favourites_tv_documentary(media_type, page_no, letter):
	return get_favourites_by_type('tv_documentary', page_no, letter)

def get_favourites_tv_reality(media_type, page_no, letter):
	return get_favourites_by_type('tv_documentary', page_no, letter)

def get_favourites_tv_talkshow(media_type, page_no, letter):
	return get_favourites_by_type('tv_documentary', page_no, letter)

def get_favourites_tv_news(media_type, page_no, letter):
	return get_favourites_by_type('tv_documentary', page_no, letter)


def get_favourites_seasons(media_type, page_no, letter):
	return get_favourites_by_type('season', page_no, letter)

def get_favourites_episodes(media_type, page_no, letter):
	return get_favourites_by_type('episode', page_no, letter)
