import re
import time
import requests
from threading import Thread
from caches import mdbl_cache
from caches.main_cache import cache_object, timedelta, MainCache
from caches.meta_cache import cache_function
from indexers.metadata import movie_external_id, tvshow_external_id
from modules import kodi_utils, settings
from modules.cache import check_databases
from modules.utils import make_thread_list, paginate_list, sort_for_article, jsondate_to_datetime, TaskPool
# logger = kodi_utils.logger

get_setting, set_setting, js2date = kodi_utils.get_setting, kodi_utils.set_setting, jsondate_to_datetime
review_provider_id = {1: 'Trakt', 2: 'TMDb', 3: 'RT', 4: 'Metacritics'}
rank_map = {'0': 'mild', '1': 'mild', '2': 'moderate', '3': 'moderate', '4': 'severe', '5': 'severe'}
guide_map = {'Nudity': 'Sex & Nudity', 'Violence': 'Violence & Gore', 'Profanity': 'Profanity', 'Alcohol': 'Alcohol, Drugs & Smoking'}
MDBL_FAVORITES_NAMES = ('favoris', 'favorites', 'favourites')
MAX_LIST_ITEMS, EXPIRES_1_HOURS = 250_000, 1
base_url = 'https://api.mdblist.com/%s'
timeout = 5.05
session = requests.Session()
session.mount('https://api.mdblist.com', requests.adapters.HTTPAdapter(pool_maxsize=100))
MDBLIST_DEFAULT_CLIENT_ID = 'EUk8hb6sCGab70Z08k9EKMv1kahOh311Xxk4fDrj'
MDBLIST_TOKEN_ENDPOINTS = ('oauth/token/', 'oauth/token')

def _safe_int(value, default=0):
	try: return int(value)
	except: return default

def _store_oauth_tokens(result):
	access_token = result.get('access_token')
	if not access_token: return False
	refresh_token = result.get('refresh_token') or get_setting('mdblist.refresh_token')
	expires = int(time.time()) + _safe_int(result.get('expires_in'), 2592000)
	set_setting('mdblist.auth_type', 'oauth')
	set_setting('mdblist.access_token', access_token)
	set_setting('mdblist.refresh_token', refresh_token or '')
	set_setting('mdblist.expires', str(expires))
	set_setting('mdblist.client_id', get_setting('mdblist.client_id') or MDBLIST_DEFAULT_CLIENT_ID)
	return True

def _refresh_oauth_token():
	refresh_token = get_setting('mdblist.refresh_token')
	if not refresh_token: return False
	client_id = get_setting('mdblist.client_id') or MDBLIST_DEFAULT_CLIENT_ID
	payload = {'grant_type': 'refresh_token', 'refresh_token': refresh_token, 'client_id': client_id}
	for endpoint in MDBLIST_TOKEN_ENDPOINTS:
		try:
			response = session.post(base_url % endpoint, data=payload, timeout=timeout)
			result = response.json() if 'json' in response.headers.get('Content-Type', '') else {}
		except requests.exceptions.RequestException as e:
			kodi_utils.logger('mdblist refresh error', str(e))
			continue
		if response.ok and _store_oauth_tokens(result): return True
	return False

def _oauth_headers():
	access_token = get_setting('mdblist.access_token')
	if not access_token: return None
	expires = _safe_int(get_setting('mdblist.expires'))
	if expires and time.time() > expires - 300:
		_refresh_oauth_token()
		access_token = get_setting('mdblist.access_token')
	return {'Authorization': 'Bearer %s' % access_token} if access_token else None

def _mdbl_account_active():
	return bool(get_setting('mdblist_user', '') or get_setting('mdblist.access_token', '') or get_setting('mdblist.token', ''))

def _mdbl_personal_path(path):
	path = str(path or '')
	return path.startswith(('sync/', 'watchlist/', 'user')) or path in ('watchlist/items',)

def call_mdblist(path, params=None, json=None, method=None):
	params = dict(params or {})
	if _mdbl_personal_path(path) and not _mdbl_account_active():
		kodi_utils.logger('mdblist skipped', 'no account | path=%s' % path)
		return None
	headers = _oauth_headers()
	if not headers:
		params['apikey'] = get_setting('mdblist.token')
	try:
		response = session.request(method or 'get', base_url % path, params=params, json=json, headers=headers, timeout=timeout)
		if response.status_code in (401, 403) and headers and _refresh_oauth_token():
			headers = _oauth_headers()
			response = session.request(method or 'get', base_url % path, params=params, json=json, headers=headers, timeout=timeout)
		result = response.json() if 'json' in response.headers.get('Content-Type', '') else response.text
		if not response.ok: response.raise_for_status()
		return result
	except requests.exceptions.RequestException as e:
		kodi_utils.logger('mdblist error', str(e))

def mdbl_searchlists(query):
	query = requests.utils.quote(query)
	string = 'mdbl_searchlists_%s' % query
	url = 'lists/search?query=%s' % query
	return cache_object(call_mdblist, string, url, json=False, expiration=EXPIRES_1_HOURS)

def mdbl_userlists():
	if not _mdbl_account_active(): return []
	string = 'mdbl_userlists'
	url = 'lists/user'
	return cache_object(call_mdblist, string, url, json=False, expiration=EXPIRES_1_HOURS)

def _mdbl_normalize_name(name):
	return re.sub(r'[^a-z0-9]+', '', str(name or '').lower())

def mdbl_create_static_list(name='Favoris', private=True):
	result = call_mdblist('lists/user/add', json={'name': name, 'private': private}, method='post')
	if not result or not result.get('id'): return None
	clear_mdbl_cache(silent=True)
	return {'id': result.get('id'), 'name': name, 'items': 0, 'dynamic': False, 'private': private}

def mdbl_favorites_list(create=False):
	user_lists = mdbl_userlists() or []
	favorites = [i for i in user_lists if not i.get('dynamic') and _mdbl_normalize_name(i.get('name')) in MDBL_FAVORITES_NAMES]
	if favorites:
		favorites.sort(key=lambda k: (0 if _mdbl_normalize_name(k.get('name')) == 'favoris' else 1, str(k.get('name', '')).lower()))
		return favorites[0]
	if not create: return None
	return mdbl_create_static_list()

def mdbl_externallists():
	string = 'mdbl_externallists'
	url = 'external/lists/user'
	return cache_object(call_mdblist, string, url, json=False, expiration=EXPIRES_1_HOURS)

def mdbl_toplists():
	string = 'mdbl_toplists'
	url = 'lists/top'
	return cache_object(call_mdblist, string, url, json=False)

def mdbl_parentsguide(imdb_id, media_type):
	media_type = 'show' if media_type == 'tvshow' else 'movie'
	url = 'https://www.mdblist.com/%s/%s' % (media_type, imdb_id)
	string = 'mdbl_%s_parentsguide_%s' % (media_type, imdb_id)
	params = {'url': url, 'action': 'mdbl_parentsguide'}
	return cache_function(get_mdbl, string, params)

def mdbl_media_info(imdb_id, media_type):
	if not (get_setting('mdblist.access_token') or get_setting('mdblist.token')): return
	media_type = 'show' if media_type == 'tvshow' else 'movie'
	string = 'mdbl_%s_mediainfo_%s' % (media_type, imdb_id)
	url = '%s/%s/%s?append_to_response=review' % ('imdb', media_type, imdb_id)
	return cache_function(call_mdblist, string, url)

def mdbl_media_info_batch(items, provider, media_type):
	url = '%s/%s' % (provider, media_type)
	return call_mdblist(url, json=items, method='post')

def mdblist_collection(media_type, page_no, letter):
	string = 'mdbl_collection'
	url = 'sync/collection'
	original_list = mdbl_cache.cache_mdbl_object(mdbl_collection_watchlist_items, string, url)
	if media_type == 'all':
		original_list = original_list['movies'] + original_list['shows']
		for i in original_list: i.update({'id': i['movie' if 'movie' in i else 'show']['ids']['tmdb']})
		return original_list
	original_list = original_list[media_type]
	key = 'movie' if media_type == 'movies' else 'show'
	for i in original_list: i.update({
		'id': i[key]['ids']['tmdb'], 'imdb_id': i[key]['ids']['imdb'],
		'title': i[key]['title'], 'release_year': i[key]['year']
	})
	sort_key = settings.lists_sort_order('collection')
	reverse = settings.lists_sort_reverse('collection')
	if   sort_key == 2: original_list.sort(key=lambda k: k.get('release_year') or '', reverse=reverse)
	elif sort_key == 1: original_list.sort(key=lambda k: k.get('collected_at') or '', reverse=reverse)
	else:
		original_list = sort_for_article(original_list, 'title', settings.ignore_articles())
		if reverse: original_list.reverse()
	if settings.paginate():
		limit = settings.page_limit()
		final_list, total_pages = paginate_list(original_list, page_no, letter, limit)
	else: final_list, total_pages = original_list, 1
	return final_list, total_pages

def mdblist_watchlist(media_type, page_no, letter):
	string = 'mdbl_watchlist'
	url = 'watchlist/items'
	original_list = mdbl_cache.cache_mdbl_object(mdbl_collection_watchlist_items, string, url)
	if media_type == 'all':
		original_list = original_list['movies'] + original_list['shows']
		return original_list
	original_list = original_list[media_type]
	sort_key = settings.lists_sort_order('watchlist')
	reverse = settings.lists_sort_reverse('watchlist')
	if   sort_key == 2: original_list.sort(key=lambda k: k.get('release_year') or 0, reverse=reverse)
	elif sort_key == 1: original_list.sort(key=lambda k: k.get('watchlist_at') or '', reverse=reverse)
	else:
		original_list = sort_for_article(original_list, 'title', settings.ignore_articles())
		if reverse: original_list.reverse()
	if settings.paginate():
		limit = settings.page_limit()
		final_list, total_pages = paginate_list(original_list, page_no, letter, limit)
	else: final_list, total_pages = original_list, 1
	return final_list, total_pages

def mdblist_favorites(media_type, page_no, letter):
	favorites_list = mdbl_favorites_list(create=False)
	if not favorites_list: return [], 1
	original_list = mdbl_list_items(str(favorites_list['id']), None) or []
	if media_type == 'all': return original_list
	media_type = 'movie' if media_type == 'movies' else 'show'
	original_list = [i for i in original_list if i.get('mediatype') == media_type]
	sort_key = settings.lists_sort_order('favorites')
	reverse = settings.lists_sort_reverse('favorites')
	if   sort_key == 2: original_list.sort(key=lambda k: k.get('release_year') or 0, reverse=reverse)
	elif sort_key == 1:
		date_items = any(k.get('added') or k.get('added_at') or k.get('created_at') for k in original_list)
		if date_items: original_list.sort(key=lambda k: k.get('added') or k.get('added_at') or k.get('created_at') or '', reverse=reverse)
		else: original_list.sort(key=lambda k: k.get('rank') or 0, reverse=not reverse)
	else:
		original_list = sort_for_article(original_list, 'title', settings.ignore_articles())
		if reverse: original_list.reverse()
	if settings.paginate():
		limit = settings.page_limit()
		final_list, total_pages = paginate_list(original_list, page_no, letter, limit)
	else: final_list, total_pages = original_list, 1
	return final_list, total_pages

def mdbl_collection_watchlist_items(url):
	params = {'limit': 5000 if 'sync' in url else 1000}
	items = {'movies': [], 'shows': []}
	try:
		for _ in range(MAX_LIST_ITEMS // params['limit']):
			result = call_mdblist(url, params=params)
			if result is None: break
			if 'movies' in result: items['movies'] += result['movies']
			if 'shows' in result: items['shows'] += result['shows']
			if not result['pagination']['has_more']: break
			if 'sync' in url:
				params['offset'] = result['pagination']['offset'] + result['pagination']['limit']
			else: params['offset'] = result['pagination']['page'] * result['pagination']['limit']
	finally: return items

def _mdbl_normalize_list_items(result):
	if not result: return []
	if isinstance(result, dict):
		merged = []
		for key, media_type in (('movies', 'movie'), ('shows', 'show')):
			for item in result.get(key, []) or []:
				item = dict(item)
				item.setdefault('mediatype', media_type)
				merged.append(item)
		result = merged
	for item in result:
		try:
			ids = item.get('ids') or {}
			item.setdefault('id', ids.get('tmdb') or item.get('tmdb') or item.get('tmdb_id'))
			item.setdefault('imdb_id', ids.get('imdb') or item.get('imdb'))
		except: pass
	return result

def mdbl_list_items(list_id, list_type):
	if list_id == 'collection': return mdblist_collection('all', None, '')
	if list_id == 'watchlist': return mdblist_watchlist('all', None, '')
	if list_id == 'favorites': return mdblist_favorites('all', None, '')
	if list_type == 'external': url = 'external/lists/%s/items' % list_id
	else: url = 'lists/%s/items' % list_id
	cache = MainCache()
	cache_get, cache_set = cache.get, cache.set
	string = 'mdbl_userlists_%s' % list_id
	result = cache_get(string)
	if result: return result
	result = _mdbl_normalize_list_items(call_mdblist(url, params={'unified': 'true'}))
	if result: cache_set(string, result, expiration=timedelta(hours=EXPIRES_1_HOURS))
	return result

def mdbl_modify_collection(data, action=True):
	if action: url, key = 'sync/collection', 'updated'
	else: url, key = 'sync/collection/remove', 'removed'
	if 'movies' in data: data['movies'] = [{'ids': i} for i in data['movies']]
	if 'shows' in data: data['shows'] = [{'ids': i} for i in data['shows']]
	result = call_mdblist(url, json=data, method='post')
	success = bool(result) and key in result and any(result[key][i] for i in ('movies', 'shows'))
	return success

def mdbl_modify_list(list_id, data, action=True):
	if action: url, key = 'add', 'added'
	else: url, key = 'remove', 'removed'
	if list_id == 'collection': return mdbl_modify_collection(data, action)
	elif list_id == 'watchlist': url = 'watchlist/items/%s' % url
	elif list_id == 'favorites':
		favorites_list = mdbl_favorites_list(create=action)
		if not favorites_list: return False
		url = 'lists/%s/items/%s' % (favorites_list['id'], url)
	else: url = 'lists/%s/items/%s' % (list_id, url)
	result = call_mdblist(url, json=data, method='post')
	success = bool(result) and key in result and any(result[key][i] for i in ('movies', 'shows'))
	return success


def _mdbl_valid_id(value):
	return not value in ('', 'None', None)

def _mdbl_ids_from_params(params):
	ids = {}
	for key in ('imdb', 'tmdb', 'tvdb'):
		value = params.get('%s_id' % key)
		if not _mdbl_valid_id(value): continue
		try: ids[key] = value if key == 'imdb' else int(value)
		except: ids[key] = value
	return ids

def _mdbl_rating_payload(params, rating=None):
	media_type = params.get('media_type')
	ids = _mdbl_ids_from_params(params)
	if not ids: return None, None
	if media_type == 'movie':
		item = {'ids': ids}
		if rating is not None: item['rating'] = int(rating)
		return {'movies': [item]}, 'movies'
	show = {'ids': ids}
	if media_type == 'episode':
		episode = {'number': int(params['episode'])}
		if rating is not None: episode['rating'] = int(rating)
		show['seasons'] = [{'number': int(params['season']), 'episodes': [episode]}]
		return {'shows': [show]}, 'episodes'
	if media_type == 'season':
		season = {'number': int(params['season'])}
		if rating is not None: season['rating'] = int(rating)
		show['seasons'] = [season]
		return {'shows': [show]}, 'seasons'
	if rating is not None: show['rating'] = int(rating)
	return {'shows': [show]}, 'shows'

def mdbl_set_rating(params, rating):
	data, success_key = _mdbl_rating_payload(params, rating)
	if not data: return False
	result = call_mdblist('sync/ratings', json=data, method='post')
	try: success = bool(result) and bool(result.get('updated'))
	except: success = bool(result)
	if success: mdbl_sync_activities()
	return success

def mdbl_remove_rating(params):
	data, success_key = _mdbl_rating_payload(params)
	if not data: return False
	result = call_mdblist('sync/ratings/remove', json=data, method='post')
	try: success = bool(result) and bool(result.get('removed'))
	except: success = bool(result)
	if success: mdbl_sync_activities()
	return success

def _mdbl_ids_match(ids, params):
	ids = ids or {}
	for key in ('tmdb', 'imdb', 'tvdb'):
		value = params.get('%s_id' % key)
		if _mdbl_valid_id(value) and str(ids.get(key)) == str(value): return True
	return False

def mdbl_user_ratings():
	params = {'limit': 1000, 'offset': 0}
	results = {'movies': [], 'shows': [], 'seasons': [], 'episodes': []}
	try:
		for _ in range(MAX_LIST_ITEMS // params['limit']):
			result = call_mdblist('sync/ratings', params=params)
			if result is None: break
			for key in results: results[key] += result.get(key, []) or []
			pagination = result.get('pagination') or {}
			if not pagination.get('has_more'): break
			params['offset'] = pagination.get('offset', params['offset']) + pagination.get('limit', params['limit'])
	except: pass
	return results

def mdbl_get_rating(params):
	media_type = params.get('media_type')
	data = mdbl_user_ratings()
	if media_type == 'movie':
		for item in data.get('movies', []):
			if _mdbl_ids_match((item.get('movie') or {}).get('ids'), params): return item.get('rating')
	elif media_type == 'episode':
		for item in data.get('episodes', []):
			episode = item.get('episode') or {}
			show = episode.get('show') or item.get('show') or {}
			try:
				if int(episode.get('season')) == int(params.get('season')) and int(episode.get('number')) == int(params.get('episode')) and _mdbl_ids_match(show.get('ids'), params):
					return item.get('rating')
			except: pass
	elif media_type == 'season':
		for item in data.get('seasons', []):
			season = item.get('season') or {}
			show = season.get('show') or item.get('show') or {}
			try:
				number = season.get('number', item.get('number'))
				if int(number) == int(params.get('season')) and _mdbl_ids_match(show.get('ids'), params):
					return item.get('rating')
			except: pass
	else:
		for item in data.get('shows', []):
			if _mdbl_ids_match((item.get('show') or {}).get('ids'), params): return item.get('rating')
	return None

def mdbl_watched_unwatched(action, media, media_id, tvdb_id=0, data=None, season=None, episode=None, key='tmdb'):
	if action == 'mark_as_watched': url, result_key = 'sync/watched', 'updated'
	else: url, result_key = 'sync/watched/remove', 'removed'
	try: media_id = {key: int(media_id)}
	except: pass
	if media == 'movies':
		success_key = 'movies'
		data = {'movies': [{'ids': media_id}]}
	else:
		success_key = 'episodes'
		if media == 'episode': data = {'shows': [{'seasons': [{'episodes': [{'number': int(episode)}], 'number': int(season)}], 'ids': media_id}]}
		else: data = {'shows': [{'ids': media_id, 'seasons': data}]}
	result = call_mdblist(url, json=data, method='post')
	success = result[result_key][success_key] > 0
	if not success:
		if media != 'movies' and tvdb_id != 0:
			return mdbl_watched_unwatched(action, media, tvdb_id, 0, data, season, episode, 'tvdb')
	return success

def mdbl_indicators_movies(watched_info):
	def _process(item):
		tmdb_id = get_mdbl_movie_id(item['movie']['ids'])
		if not tmdb_id: return
		insert_append((
			'movie', str(tmdb_id), '', '', item['last_watched_at'], item['movie']['title']
		))
	insert_list = []
	insert_append = insert_list.append
	watched_items = [(i,) for i in watched_info['movies']] # TaskPool requires tuple
	if not watched_items: return
#	threads = list(make_thread_list(_process, watched_items, Thread))
	for i in TaskPool().tasks(_process, watched_items, Thread): i.join()
	mdbl_cache.MDBLCache().set_bulk_movie_watched(insert_list)

def mdbl_indicators_tv(watched_info):
	def _process(item):
		tmdb_id = get_mdbl_tvshow_id(item['episode']['show']['ids'])
		if not tmdb_id: return
		season, episode = item['episode']['season'], item['episode']['number']
		insert_append((
			'episode', str(tmdb_id), season, episode, item['last_watched_at'], item['episode']['show']['title']
		))
	insert_list = []
	insert_append = insert_list.append
	watched_items = [(i,) for i in watched_info['episodes']] # TaskPool requires tuple
	if not watched_items: return
#	threads = list(make_thread_list(_process, watched_items, Thread))
	for i in TaskPool().tasks(_process, watched_items, Thread): i.join()
	mdbl_cache.MDBLCache().set_bulk_tvshow_watched(insert_list)

def mdbl_progress(action, media, media_id, percent, season=None, episode=None, resume_id=None, refresh_mdb=False):
	if action == 'clear_progress':
		url = 'scrobble/clear'
		data = {'id': resume_id}
	else:
		url = 'scrobble/pause'
		try: media_id = int(media_id)
		except: pass
		if media in ('movie', 'movies'): data = {'movie': {'ids': {'tmdb': media_id}}, 'progress': float(percent)}
		else: data = {'show': {'ids': {'tmdb': media_id}, 'season': {'number': int(season), 'episode': {'number': int(episode)}}}, 'progress': float(percent)}
	call_mdblist(url, json=data, method='post')
	if refresh_mdb: mdbl_sync_activities()

def mdbl_progress_movies(progress_info):
	def _process(item):
		tmdb_id = get_mdbl_movie_id(item['movie']['ids'])
		if not tmdb_id: return
		season, episode = '', ''
		insert_append((
			'movie', str(tmdb_id), season, episode, str(round(float(item['progress']), 1)),
			0, item['paused_at'], item['id'], item['movie']['title']
		))
	insert_list = []
	insert_append = insert_list.append
	progress_items = [i for i in progress_info if i['type'] == 'movie' and float(i['progress']) > 1]
	if progress_items:
		threads = list(make_thread_list(_process, progress_items, Thread))
		[i.join() for i in threads]
	mdbl_cache.MDBLCache().set_bulk_movie_progress(insert_list)

def mdbl_progress_tv(progress_info):
	def _process(item):
		tmdb_id = get_mdbl_tvshow_id(item['show']['ids'])
		if not tmdb_id: return
		season, episode = item['episode']['season'], item['episode']['number']
		if season < 1: return
		insert_append((
			'episode', str(tmdb_id), season, episode, str(round(float(item['progress']), 1)),
			0, item['paused_at'], item['id'], item['show']['title']
		))
	insert_list = []
	insert_append = insert_list.append
	progress_items = [i for i in progress_info if i['type'] == 'episode' and float(i['progress']) > 1]
	if progress_items:
		threads = list(make_thread_list(_process, progress_items, Thread))
		[i.join() for i in threads]
	mdbl_cache.MDBLCache().set_bulk_tvshow_progress(insert_list)

def get_mdbl_movie_id(item):
	if item['tmdb']: return item['tmdb']
	tmdb_id = None
	if item['imdb']:
		try:
			meta = movie_external_id('imdb_id', item['imdb'])
			tmdb_id = meta['id']
		except: pass
	return tmdb_id

def get_mdbl_tvshow_id(item):
	if item['tmdb']: return item['tmdb']
	tmdb_id = None
	if item['imdb']:
		try:
			meta = tvshow_external_id('imdb_id', item['imdb'])
			tmdb_id = meta['id']
		except: tmdb_id = None
	if not tmdb_id:
		if item['tvdb']:
			try:
				meta = tvshow_external_id('tvdb_id', item['tvdb'])
				tmdb_id = meta['id']
			except: tmdb_id = None
	return tmdb_id

def mdbl_get_activity():
	url = 'sync/last_activities'
	return call_mdblist(url)

def mdbl_playback_progress():
	url = 'sync/playback'
	return call_mdblist(url)

def mdbl_watched_progress():
	params = {'limit': 5000}
	watched = {'movies': [], 'episodes': []}
	try:
		for _ in range(MAX_LIST_ITEMS // params['limit']):
			result = call_mdblist('sync/watched', params=params)
			if result is None: break
			if 'movies' in result: watched['movies'] += result['movies']
			if 'episodes' in result: watched['episodes'] += result['episodes']
			if not result['pagination']['has_more']: break
			params['offset'] = result['pagination']['offset'] + result['pagination']['limit']
	finally: return watched

def mdbl_sync_activities_thread(*args, **kwargs):
	Thread(target=mdbl_sync_activities, args=args, kwargs=kwargs).start()

def mdbl_sync_activities(force_update=False):
	def _get_timestamp(date_time):
		return int(time.mktime(date_time.timetuple()))
	def _compare(latest, cached, res_format='%Y-%m-%dT%H:%M:%SZ'):
		try: result = _get_timestamp(js2date(latest, res_format)) > _get_timestamp(js2date(cached, res_format))
		except: result = True
		return result
	if not get_setting('mdblist_user', ''): return 'no account'
	if force_update:
		check_databases()
		mdbl_cache.clear_all_mdbl_cache_data(refresh=False)
	latest = mdbl_get_activity()
	if latest is None:
		mdbl_cache.clear_all_mdbl_cache_data(refresh=False)
		return 'failed'
	progress_info = mdbl_playback_progress()
	if progress_info is None:
		mdbl_cache.clear_all_mdbl_cache_data(refresh=False)
		return 'failed'
	progress_info.sort(key=lambda k: k['paused_at'], reverse=True)
	latest['paused_at'] = progress_info[0]['paused_at'] if progress_info else None
	cached = mdbl_cache.reset_activity(latest)
	refresh_movies_watched = _compare(latest['watched_at'], cached['watched_at'])
	refresh_episodes_watched = _compare(latest['episode_watched_at'], cached['episode_watched_at'])
	success = 'not needed'
	if refresh_movies_watched or refresh_episodes_watched:
		success = 'success'
		watched_info = mdbl_watched_progress()
		if refresh_movies_watched: mdbl_indicators_movies(watched_info)
		if refresh_episodes_watched: mdbl_indicators_tv(watched_info)
	if _compare(latest['paused_at'], cached['paused_at']):
		success = 'success'
		mdbl_progress_movies(progress_info)
		mdbl_progress_tv(progress_info)
	if _compare(latest['watchlisted_at'], cached['watchlisted_at']):
		success = 'success'
		mdbl_cache.clear_mdbl_collection_watchlist_data('watchlist')
	if _compare(latest['collected_at'], cached['collected_at']):
		success = 'success'
		mdbl_cache.clear_mdbl_collection_watchlist_data('collection')
	if _compare(latest.get('rated_at'), cached.get('rated_at')):
		success = 'success'
		try:
			from modules import user_ratings
			user_ratings.clear_user_ratings_cache(1)
		except: pass
	return success

def clear_mdbl_cache(silent=False):
	from modules.kodi_utils import path_exists, clear_property, database_connect, maincache_db
	try:
		if not path_exists(maincache_db): return True
		dbcon = database_connect(maincache_db, isolation_level=None)
		dbcur = dbcon.cursor()
		dbcur.execute("""PRAGMA synchronous = OFF""")
		dbcur.execute("""PRAGMA journal_mode = OFF""")
		dbcur.execute("""SELECT id FROM maincache WHERE id LIKE ?""", ('mdbl_%',))
		mdb_results = [str(i[0]) for i in dbcur.fetchall()]
		if not mdb_results: return True
		dbcur.execute("""DELETE FROM maincache WHERE id LIKE ?""", ('mdbl_%',))
		for i in mdb_results: clear_property(i)
		return True
	except: return False

def get_mdbl(params):
	results = []
	action = params['action']
	url = params['url']
	response = requests.get(url, timeout=timeout)
	if action == 'mdbl_parentsguide':
		def _process():
			for key, val in guide_map.items():
				try:
					if not (match := re.search(rf"{key}\:\ \d", html)): continue
					rank = rank_map[match.group().split(': ')[-1]]
					yield {'title': val, 'ranking': rank, 'listings': []}
				except: pass
		html = response.text.replace('\n', ' ')
		results = list(_process())
	return results

