# -*- coding: utf-8 -*-
# plugin.video.alkoflix
# resources/lib/magneto/hosters/common.py

import json
import re
import math
from html import unescape
from urllib.parse import quote, urljoin, urlparse
from xml.etree import ElementTree

import requests

from alko import source_utils
from alko.control import setting as getSetting
from modules.source_objects import _is_blocked_file_source


DEFAULT_TIMEOUT = 8
VIDEO_EXTENSIONS = ('.mkv', '.mp4', '.avi', '.m2ts', '.ts', '.m4v', '.mov', '.wmv', '.webm')
QUALITY_ORDER = {'4K': 0, '1080p': 1, '720p': 2, 'SD': 3, 'SCR': 4, 'CAM': 5, 'TELE': 5}

HOSTER_ALIASES = {
	'1fichier.com': '1fichier', '1fichier': '1fichier', 'onefichier': '1fichier',
	'rapidgator.net': 'rapidgator', 'rapidgator': 'rapidgator',
	'turbobit.net': 'turbobit', 'turbobit': 'turbobit',
	'send.cm': 'sendcm', 'sendcm': 'sendcm',
	'darkibox.com': 'darkibox', 'darkibox': 'darkibox',
	'webshare.cz': 'webshare', 'webshare': 'webshare',
	'nitroflare.com': 'nitroflare', 'nitroflare': 'nitroflare',
	'katfile.com': 'katfile', 'katfile': 'katfile',
	'fikper.com': 'fikper', 'fikper': 'fikper',
	'ddownload.com': 'ddownload', 'ddownload': 'ddownload',
	'filejoker.net': 'filejoker', 'filejoker': 'filejoker',
	'filemoon.sx': 'filemoon', 'filemoon': 'filemoon',
	'streamtape.com': 'streamtape', 'streamtape': 'streamtape',
	'alldebrid.com': 'alldebrid', 'alldebrid': 'alldebrid',
	'uptobox.com': 'uptobox', 'uptobox': 'uptobox',
	'hoster': 'hoster', 'unknown': 'hoster'
}


def clean_text(value):
	try:
		value = unescape(str(value or ''))
		value = re.sub(r'<[^>]+>', ' ', value)
		value = value.replace('\xa0', ' ')
		return re.sub(r'\s+', ' ', value).strip()
	except:
		return ''


def normalize_text(value):
	try:
		value = clean_text(value).lower()
		for old, new in (('é', 'e'), ('è', 'e'), ('ê', 'e'), ('ë', 'e'), ('à', 'a'), ('â', 'a'), ('î', 'i'), ('ï', 'i'), ('ô', 'o'), ('ù', 'u'), ('û', 'u'), ('ç', 'c'), ('œ', 'oe')):
			value = value.replace(old, new)
		return re.sub(r'[^a-z0-9]+', ' ', value).strip()
	except:
		return ''


def normalize_quality(value):
	text = normalize_text(value)
	if re.search(r'\b(2160|2160p|4k|uhd)\b', text): return '4K'
	if re.search(r'\b(1080|1080p|fullhd|full hd)\b', text): return '1080p'
	if re.search(r'\b(720|720p|hd)\b', text): return '720p'
	if re.search(r'\b(cam|hdcam)\b', text): return 'CAM'
	if re.search(r'\b(ts|telesync|tc)\b', text): return 'TELE'
	if re.search(r'\b(scr|screener|dvdscr)\b', text): return 'SCR'
	return 'SD'


def language_code(value):
	text = normalize_text(value)
	if re.search(r'\b(vostfr|vo stfr|subfrench|fr sub|french sub)\b', text): return 'fr'
	if re.search(r'\b(fr|fra|fre|french|truefrench|true french|vf|vfi|vff|vfq|vf2|vof|voq|multi|multilangue)\b', text): return 'fr'
	return 'en'


def parse_size_gb(value):
	try:
		if value in ('', None): return 0.0
		if isinstance(value, (int, float)):
			# Les API Hydracker/Movix retournent souvent des octets.
			if float(value) > 1024 * 1024: return round(float(value) / 1073741824.0, 2)
			return round(float(value), 2)
		text = str(value).replace(',', '.').strip()
		match = re.search(r'([0-9]+(?:\.[0-9]+)?)\s*(gib|gb|go|mb|mib|mo)\b', text, re.I)
		if not match: return 0.0
		number, unit = float(match.group(1)), match.group(2).lower()
		if unit in ('mb', 'mib', 'mo'): number = number / 1024.0
		return round(number, 2)
	except:
		return 0.0


def hoster_key(value, url=''):
	try:
		candidates = []
		if value: candidates.append(str(value))
		if url:
			parsed = urlparse(str(url))
			host = (parsed.netloc or '').lower().split('@')[-1].split(':')[0]
			if host.startswith('www.'): host = host[4:]
			if host: candidates.append(host)
		for candidate in candidates:
			candidate = normalize_text(candidate).replace(' ', '')
			candidate = candidate.replace('onefichier', '1fichier')
			if candidate in HOSTER_ALIASES: return HOSTER_ALIASES[candidate]
			for key, mapped in HOSTER_ALIASES.items():
				if key.replace('.', '') in candidate or candidate in key.replace('.', ''):
					return mapped
	except:
		pass
	return 'hoster'


def hoster_label(value, url=''):
	key = hoster_key(value, url)
	labels = {
		'1fichier': '1fichier', 'rapidgator': 'Rapidgator', 'turbobit': 'Turbobit',
		'sendcm': 'Send.cm', 'darkibox': 'Hydracker', 'webshare': 'Webshare',
		'nitroflare': 'Nitroflare', 'katfile': 'Katfile', 'fikper': 'Fikper',
		'ddownload': 'DDownload', 'filejoker': 'Filejoker', 'filemoon': 'Filemoon',
		'streamtape': 'Streamtape', 'alldebrid': 'AllDebrid', 'uptobox': 'Uptobox',
		'hoster': clean_text(value) or 'Hoster'
	}
	return labels.get(key, clean_text(value) or key.upper())


def title_variants(data, limit=4):
	values, seen = [], set()
	try:
		for alias in data.get('aliases', []) or []:
			value = alias.get('title') if isinstance(alias, dict) else alias
			value = clean_text(value)
			key = value.casefold()
			if value and key not in seen:
				values.append(value)
				seen.add(key)
	except:
		pass
	try:
		value = clean_text(data.get('tvshowtitle') or data.get('title'))
		key = value.casefold()
		if value and key not in seen:
			values.insert(0, value)
			seen.add(key)
	except:
		pass
	return values[:limit]


def request_json(url, params=None, headers=None, timeout=DEFAULT_TIMEOUT):
	try:
		response = requests.get(url, params=params, headers=headers or {}, timeout=timeout)
		if response.status_code != 200: return None
		return response.json()
	except:
		return None


def request_text(url, params=None, data=None, headers=None, method='get', timeout=DEFAULT_TIMEOUT):
	try:
		if method == 'post': response = requests.post(url, params=params, data=data, headers=headers or {}, timeout=timeout)
		else: response = requests.get(url, params=params, headers=headers or {}, timeout=timeout)
		if response.status_code != 200: return ''
		return response.text or ''
	except:
		return ''


def abs_url(url, base):
	try:
		url = clean_text(url)
		if not url: return ''
		return urljoin(str(base).rstrip('/') + '/', url)
	except:
		return url or ''


def extract_attr(tag, attr):
	try:
		match = re.search(r'%s\s*=\s*(["\'])(.*?)\1' % re.escape(attr), tag, re.I | re.S)
		return unescape(match.group(2)) if match else ''
	except:
		return ''


def html_links(html, href_pattern=None):
	items = []
	try:
		for match in re.finditer(r'<a\b([^>]*)>(.*?)</a>', html or '', re.I | re.S):
			href = extract_attr(match.group(1), 'href')
			text = clean_text(match.group(2))
			if href and (not href_pattern or re.search(href_pattern, href, re.I)):
				items.append({'href': href, 'text': text, 'tag': match.group(0)})
	except:
		pass
	return items


def sort_results(items):
	try:
		return sorted(items or [], key=lambda i: (QUALITY_ORDER.get(i.get('quality', 'SD'), 9), -float(i.get('size', 0) or 0), i.get('name', '')))
	except:
		return items or []


class BaseHosterSource:
	provider_key = 'hoster'
	provider_name = 'Hoster'
	base_setting = ''
	default_base_url = ''
	api_key_setting = ''
	timeout = DEFAULT_TIMEOUT
	priority = 5
	pack_capable = False
	hasMovies = True
	hasEpisodes = True

	def setting(self, setting_id, default=''):
		try:
			return getSetting(setting_id) or default
		except:
			return default

	def base_url(self):
		return (self.setting(self.base_setting) or self.default_base_url or '').rstrip('/')

	def api_key(self):
		return self.setting(self.api_key_setting) if self.api_key_setting else ''

	def headers(self):
		return {}

	def sources(self, data, hostDict):
		try:
			if not self.base_url(): return []
			results = self.search(data) or []
			return sort_results([item for item in (self.format_result(data, raw) for raw in results) if item])
		except:
			source_utils.scraper_error(self.provider_name)
			return []

	def sources_packs(self, data, hostDict, search_series=False, total_seasons=None):
		# Première intégration hosters : épisodes exacts. Les packs saison/intégrale seront ajoutés séparément.
		return []

	def search(self, data):
		return []

	def make_display_name(self, data, quality='', language='', raw_name=''):
		try:
			title = data.get('tvshowtitle') or data.get('title') or ''
			year = str(data.get('year') or '')
			quality = normalize_quality(quality or raw_name)
			language = clean_text(language or '')
			if data.get('tvshowtitle'):
				return '%s S%02dE%02d %s %s' % (title, int(data.get('season') or 0), int(data.get('episode') or 0), quality, language)
			return '%s %s %s %s' % (title, year, quality, language)
		except:
			return raw_name or data.get('title') or self.provider_name

	def format_result(self, data, raw):
		try:
			if not isinstance(raw, dict): return None
			url = raw.get('link') or raw.get('url') or raw.get('download_url') or ''
			if not url: return None
			hoster = raw.get('hoster') or raw.get('host') or raw.get('host_name') or raw.get('source') or ''
			source_key = hoster_key(hoster, url)
			hoster_display = hoster_label(hoster, url)
			title = data.get('tvshowtitle') or data.get('title') or ''
			year = str(data.get('year') or '')
			episode_title = data.get('title') if data.get('tvshowtitle') else None
			season = str(data.get('season') or '') if data.get('tvshowtitle') else ''
			hdlr = 'S%02dE%02d' % (int(data.get('season')), int(data.get('episode'))) if data.get('tvshowtitle') else year
			raw_name = raw.get('display_name') or raw.get('name') or raw.get('title') or ''
			quality_hint = raw.get('quality') or raw_name or url
			language_hint = raw.get('language') or raw.get('raw_language') or raw_name or ''
			if not raw_name:
				raw_name = self.make_display_name(data, quality_hint, language_hint)
			name = source_utils.clean_name(raw_name)
			if not name: name = self.make_display_name(data, quality_hint, language_hint)
			if _is_blocked_file_source({'name': name, 'url': url, 'raw': raw}): return None
			name_info = source_utils.info_from_name(name, title, year, hdlr, episode_title, season=season)
			check_foreign_audio = source_utils.check_foreign_audio()
			if source_utils.remove_lang(name_info, check_foreign_audio): return None
			quality, info = source_utils.get_release_quality('%s %s' % (name_info, quality_hint), url)
			if quality in ('SD', None):
				q = normalize_quality(quality_hint)
				if q: quality = q
			size = parse_size_gb(raw.get('size') or raw.get('filesize') or raw.get('file_size'))
			if size:
				info.insert(0, '%.2f GB' % size)
			language = language_code('%s %s %s' % (name_info, language_hint, raw.get('raw_language', '')))
			item = {
				'source': source_key,
				'language': language,
				'direct': False,
				'debridonly': True,
				'provider': self.provider_key,
				'provider_name': self.provider_name,
				'tracker': self.provider_name,
				'source_site': self.provider_name,
				'hoster': hoster_display,
				'hoster_source': True,
				'url': url,
				'name': name,
				'name_info': name_info,
				'quality': quality or 'SD',
				'info': ' | '.join([i for i in info if i]),
				'size': size,
				'true_size': bool(size)
			}
			if data.get('tvshowtitle'):
				item['episode_start'] = int(data.get('episode') or 0)
				item['episode_end'] = int(data.get('episode') or 0)
			return item if not _is_blocked_file_source(item) else None
		except:
			source_utils.scraper_error(self.provider_name)
			return None
