# wcs/webshare_api.py

import requests
from xml.etree import ElementTree as ET
import hashlib
from md5crypt import md5crypt
import uuid
import xbmcgui
import xbmcplugin
from wcs.ui import UI as ui
from wcs import user_data
import xbmc
import datetime as py_datetime
import re
import xbmcaddon
def _parse_date_ymd(date_str):
    """Rychlý parser YYYY-MM-DD bez použití datetime.strptime (kvůli stabilitě).
    Vrací py_datetime.date nebo vyhodí ValueError.
    """
    if not date_str or len(date_str) < 10:
        raise ValueError('date_str too short')
    y = int(date_str[0:4])
    m = int(date_str[5:7])
    d = int(date_str[8:10])
    if date_str[4] != '-' or date_str[7] != '-':
        raise ValueError('invalid separators')
    return py_datetime.date(y, m, d)


BASE = 'https://webshare.cz'
API = BASE + '/api/'
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"
HEADERS = {'User-Agent': UA, 'Referer': BASE}
REALM = ':Webshare:'

_session = requests.Session()
_session.headers.update(HEADERS)
_vip_check_lock = False

# Funkce pro volání Webshare API
def api_call(fnct, data, addon_for_vip_check=None):
    """Jednoduché volání Webshare API s nenápadnou VIP kontrolou po volání.

    - Vyhne se rekurzi a volání u 'user_data', 'login', 'salt'.
    - Notifikace expirace se řeší uvnitř refresh_vip_info.
    """
    global _vip_check_lock
    response = _session.post(API + fnct + "/", data=data)
    try:
        if addon_for_vip_check and fnct not in ('user_data', 'login', 'salt') and not _vip_check_lock:
            _vip_check_lock = True
            try:
                # Tichá obnova VIP info + případné varování podle prahu
                refresh_vip_info(addon_for_vip_check, token_override=None, show_notification=False, log_context="api_call")
            finally:
                _vip_check_lock = False
    except Exception:
        pass
    return response

def is_ok(xml):
    status = xml.find('status').text
    return status == 'OK'

# Funkce pro login a získání tokenu
def login(username, password, addon, popinfo, open_settings_on_fail=True):
    if username == '' or password == '':
        popinfo(addon.getLocalizedString(30101), addon.getLocalizedString(30100), sound=True)
        if open_settings_on_fail:
            addon.openSettings()
        return None
    response = api_call('salt', {'username_or_email': username}, addon_for_vip_check=addon)
    xml = ET.fromstring(response.content)
    if is_ok(xml):
        salt = xml.find('salt').text
        try:
            encrypted_pass = hashlib.sha1(md5crypt(password.encode('utf-8'), salt.encode('utf-8'))).hexdigest()
            pass_digest = hashlib.md5(username.encode('utf-8') + REALM + encrypted_pass.encode('utf-8')).hexdigest()
        except TypeError:
            encrypted_pass = hashlib.sha1(md5crypt(password.encode('utf-8'), salt.encode('utf-8')).encode('utf-8')).hexdigest()
            pass_digest = hashlib.md5(username.encode('utf-8') + REALM.encode('utf-8') + encrypted_pass.encode('utf-8')).hexdigest()
        response = api_call('login', {'username_or_email': username, 'password': encrypted_pass, 'digest': pass_digest, 'keep_logged_in': 1}, addon_for_vip_check=addon)
        xml = ET.fromstring(response.content)
        if is_ok(xml):
            token = xml.find('token').text
            addon.setSetting('token', token)
            return token
        else:
            popinfo(addon.getLocalizedString(30157), addon.getAddonInfo('name'), icon='error', sound=True)
            if open_settings_on_fail:
                addon.openSettings()
    else:
        popinfo(addon.getLocalizedString(30157), addon.getAddonInfo('name'), icon='error', sound=True)
        if open_settings_on_fail:
            addon.openSettings()
    return None

def revalidate(addon, popinfo):
    token = addon.getSetting('token')
    if len(token) == 0:
        token = login(addon.getSetting('wsuser'), addon.getSetting('wspass'), addon, popinfo, open_settings_on_fail=False)
        if token:
            return revalidate(addon, popinfo)
        return None
    else:
        response = api_call('user_data', { 'wst': token }, addon_for_vip_check=addon)
        xml = ET.fromstring(response.content)
        if is_ok(xml):
            vip = xml.find('vip').text
            if vip != '1':
                username = (xml.find('username').text or '').strip() if xml.find('username') is not None else ''
                non_vip_text = addon.getLocalizedString(32179).format(name=username)
                popinfo(non_vip_text, addon.getAddonInfo('name'), icon='warning')
            return token
        else:
            token = login(addon.getSetting('wsuser'), addon.getSetting('wspass'), addon, popinfo, open_settings_on_fail=False)
            if token:
                return revalidate(addon, popinfo)
    return None

# Získání informací o uživateli (VIP stav a expirace)
def get_user_info(addon, popinfo, token_override=None):
    """Načte detailní informace o účtu z Webshare API.

    Vrací slovník {tag: text} nebo None při chybě.
    """
    token = token_override or addon.getSetting('token')
    if not token:
        return None
    try:
        response = api_call('user_data', {'wst': token}, addon_for_vip_check=addon)
        xml = ET.fromstring(response.content)
        if not is_ok(xml):
            return None
        info = {e.tag: (e.text or '').strip() for e in xml}
        # Normalize vip_until to plain date if possible for consistent use
        try:
            m = re.search(r"(\d{4}-\d{2}-\d{2})", info.get('vip_until', ''))
            if m:
                info['vip_until'] = m.group(1)
        except Exception:
            pass
        return info
    except Exception:
        return None

def _build_vip_text(addon, vip_flag, vip_until_value):
    """Sestaví lokalizovaný text VIP expirace.
    Vrací (text, is_valid) kde is_valid značí, zda obsahuje platné datum a výpočet dnů.
    Formát: "Zbývá dnů: {počet}" (sjednoceno s KRA.sk)
    """
    try:
        raw_flag = (vip_flag or '').strip()
        raw_until = (vip_until_value or '').strip()
        xbmc.log(f"[plugin.video.milionar] _build_vip_text: raw_flag='{raw_flag}', raw_until='{raw_until}'", level=xbmc.LOGINFO)
        if raw_flag == '1' and raw_until:
            # Extrahuj prvních 10 znaků jako YYYY-MM-DD bez regexu (spolehlivější napříč runtime)
            date_part = (raw_until.strip() + '          ')[:10]
            # Rychlá validace formátu
            if not (len(date_part) == 10 and date_part[4] == '-' and date_part[7] == '-'):
                xbmc.log("[plugin.video.milionar] _build_vip_text: date_part invalid -> '" + date_part + "'", level=xbmc.LOGINFO)
                return "Zbývá dnů: N/A", False
            # Výpočet zbývajících dnů bez datetime.strptime (stabilní napříč runtime)
            dt = _parse_date_ymd(date_part)
            today = py_datetime.date.today()
            diff = (dt - today).days
            if diff < 0:
                diff = 0
            # Jednotný formát jako KRA.sk: "Zbývá dnů: {počet}"
            text = f"Zbývá dnů: {diff}"
            return text, True
        else:
            xbmc.log("[plugin.video.milionar] _build_vip_text: invalid inputs, returning fallback", level=xbmc.LOGINFO)
            return "Zbývá dnů: N/A", False
    except Exception as e:
        xbmc.log(f"[plugin.video.milionar] _build_vip_text: exception {e}", level=xbmc.LOGINFO)
        return "Zbývá dnů: N/A", False

def refresh_vip_info(addon, token_override=None, show_notification=False, log_context="", notification_prefix=None, login_username=None, show_warning=False):
    """Jednotná funkce pro načtení a uložení VIP expirace.

    - Používá retry (3x) s krátkou prodlevou, protože Webshare může bezprostředně po loginu vracet prázdné `vip_until`.
    - Uloží `webshare_vip_info` a volitelně zobrazí notifikaci.
    - Loguje průběh.
    """
    token = token_override or addon.getSetting('token')
    if not token:
        xbmc.log(f"[plugin.video.milionar] VIP refresh({log_context}): missing token", level=xbmc.LOGINFO)
        return False

    last_text = addon.getSetting('webshare_vip_info') or ''
    for attempt in range(1, 4):
        info = get_user_info(addon, lambda *a, **k: None, token_override=token) or {}
        vip_flag = (info.get('vip') or '').strip()
        vip_until = (info.get('vip_until') or '').strip()
        xbmc.log(f"[plugin.video.milionar] VIP refresh({log_context}) attempt {attempt}: vip={vip_flag}, vip_until='{vip_until}'", level=xbmc.LOGINFO)
        # Rychlá cesta pro ne-VIP účty (bez dalších pokusů)
        if vip_flag != '1':
            # Zkusíme získat jméno účtu pro personalizovaný text
            username = (info.get('username') or '').strip()
            if not username:
                try:
                    pre = api_call('user_data', {'wst': token}, addon_for_vip_check=addon)
                    pre_xml = ET.fromstring(pre.content)
                    username = (pre_xml.find('username').text or '').strip() if pre_xml is not None and pre_xml.find('username') is not None else ''
                except Exception:
                    username = ''
            non_vip_text = addon.getLocalizedString(32179).format(name=username)
            addon.setSetting('webshare_vip_info', non_vip_text)
            if show_notification:
                # Bez prefixu pro případ, kdy není aktivní VIP (požadavek)
                xbmcgui.Dialog().notification(addon.getAddonInfo('name'), non_vip_text, xbmcgui.NOTIFICATION_INFO, 4000)
            xbmc.log(f"[plugin.video.milionar] VIP refresh({log_context}) non-vip: {non_vip_text}", level=xbmc.LOGINFO)
            return True
        text, is_valid = _build_vip_text(addon, vip_flag, vip_until)
        xbmc.log(f"[plugin.video.milionar] VIP refresh({log_context}) attempt {attempt}: is_valid={is_valid}, text='{text}'", level=xbmc.LOGINFO)
        if is_valid:
            addon.setSetting('webshare_vip_info', text)
            # Varování na blížící se expiraci pouze pokud je to povoleno kontextem
            if show_warning:
                try:
                    warn_days = int(addon.getSetting('webshare_vip_warn_days') or '7')
                except Exception:
                    warn_days = 7
                try:
                    date_part = vip_until.split('T')[0].split(' ')[0]
                    dt = _parse_date_ymd(date_part)
                    today = py_datetime.date.today()
                    diff_days = (dt - today).days
                    if diff_days < 0:
                        diff_days = 0
                    if warn_days > 0 and diff_days <= warn_days:
                        remaining_text = f"{diff_days} {addon.getLocalizedString(32177)}"
                        warn_template = addon.getLocalizedString(32181)
                        warn_text = warn_template.format(date=date_part, remaining=remaining_text)
                        xbmcgui.Dialog().notification(addon.getAddonInfo('name'), warn_text, xbmcgui.NOTIFICATION_WARNING, 5000)
                except Exception:
                    pass
            if show_notification:
                # Sestavit notifikaci: "Přihlášen: {username}, Zbývá dnů: X" (jako KRA.sk)
                if notification_prefix:
                    notif_text = f"{notification_prefix}{text}"
                else:
                    # Získat username pro personalizovanou notifikaci
                    username = (info.get('username') or '').strip() if info else ''
                    notif_text = f"Přihlášen: {username}, {text}" if username else text
                xbmcgui.Dialog().notification(addon.getAddonInfo('name'), notif_text, xbmcgui.NOTIFICATION_INFO, 5000)
            xbmc.log(f"[plugin.video.milionar] VIP refresh({log_context}) success: {text}", level=xbmc.LOGINFO)
            return True
        # fallback pro případ, že API vrací validní vip/vip_until, ale parser vrátí False
        if vip_flag == '1' and vip_until:
            try:
                date_part = vip_until.split('T')[0].split(' ')[0]
                dt = _parse_date_ymd(date_part)
                today = py_datetime.date.today()
                diff = (dt - today).days
                if diff < 0:
                    diff = 0
                days_display = f"{diff} {addon.getLocalizedString(32177)}"
                forced_text = f"{addon.getLocalizedString(32174)} {date_part}, {addon.getLocalizedString(32175)} {days_display}"
                addon.setSetting('webshare_vip_info', forced_text)
                # Varování na blížící se expiraci i v forced větvi (jen pokud je povoleno)
                if show_warning:
                    try:
                        warn_days = int(addon.getSetting('webshare_vip_warn_days') or '7')
                    except Exception:
                        warn_days = 7
                    try:
                        if warn_days > 0 and diff <= warn_days:
                            remaining_text = f"{diff} {addon.getLocalizedString(32177)}"
                            warn_template = addon.getLocalizedString(32181)
                            warn_text = warn_template.format(date=date_part, remaining=remaining_text)
                            xbmcgui.Dialog().notification(addon.getAddonInfo('name'), warn_text, xbmcgui.NOTIFICATION_WARNING, 5000)
                    except Exception:
                        pass
                if show_notification:
                    # Sestavit notifikaci: "Přihlášen: {username}, Zbývá dnů: X" (jako KRA.sk)
                    if notification_prefix:
                        notif_text = f"{notification_prefix}Zbývá dnů: {diff}"
                    else:
                        username = (info.get('username') or '').strip() if info else ''
                        notif_text = f"Přihlášen: {username}, Zbývá dnů: {diff}" if username else f"Zbývá dnů: {diff}"
                    xbmcgui.Dialog().notification(addon.getAddonInfo('name'), notif_text, xbmcgui.NOTIFICATION_INFO, 5000)
                xbmc.log(f"[plugin.video.milionar] VIP refresh({log_context}) forced set on attempt {attempt}: {forced_text}", level=xbmc.LOGINFO)
                return True
            except Exception as e:
                xbmc.log(f"[plugin.video.milionar] VIP refresh({log_context}) forced parse error: {e}", level=xbmc.LOGINFO)
        # not valid → počkáme a zkusíme znovu
        xbmc.sleep(400)

    # Po 3 pokusech uloží fallback jen pokud nebyla žádná předchozí hodnota
    if not last_text:
        fallback, _ = _build_vip_text(addon, None, None)
        addon.setSetting('webshare_vip_info', fallback)
        if show_notification:
            # Sestavit notifikaci: prefix + VIP info (jako KRA.sk)
            if notification_prefix:
                notif_text = f"{notification_prefix}{fallback}"
            else:
                notif_text = fallback
            xbmcgui.Dialog().notification(addon.getAddonInfo('name'), notif_text, xbmcgui.NOTIFICATION_INFO, 5000)
    xbmc.log(f"[plugin.video.milionar] VIP refresh({log_context}) finished without valid date", level=xbmc.LOGINFO)
    return False

# Získání informací o souboru
def getinfo(ident, wst):
    response = api_call('file_info', {'ident': ident, 'wst': wst}, addon_for_vip_check=xbmcaddon.Addon())
    xml = ET.fromstring(response.content)
    if not is_ok(xml):
        response = api_call('file_info', {'ident': ident, 'wst': wst, 'maybe_removed': 'true'}, addon_for_vip_check=xbmcaddon.Addon())
        xml = ET.fromstring(response.content)
    if is_ok(xml):
        return xml
    return None

# Získání odkazu na stream/stahování
def getlink(ident, wst, dtype='video_stream', addon=None):
    duuid = addon.getSetting('duuid') if addon else None
    if addon and not duuid:
        duuid = str(uuid.uuid4())
        addon.setSetting('duuid', duuid)
    data = {'ident': ident, 'wst': wst, 'download_type': dtype}
    if duuid:
        data['device_uuid'] = duuid
    response = api_call('file_link', data, addon_for_vip_check=addon)
    xml = ET.fromstring(response.content)
    if is_ok(xml):
        return xml.find('link').text
    return None

# Získání odkazu a vyřešení přehratelné položky pro Kodi
# Funkce pro získání hratelné položky (pro setResolvedUrl)
def resolve_playable_item(ident, name, addon, params, handle, use_player=False):
    """
    Vyřeší hratelnou položku - buď zavolá setResolvedUrl (pro plugin directory),
    nebo přímo spustí přehrávač (pro skripty/isFolder=False).
    """
    # --- Kontrola přihlášení ---
    if not user_data.check_providers_and_prompt(addon):
        if not use_player:
            xbmcplugin.setResolvedUrl(handle, False, xbmcgui.ListItem()) # Selhat s prázdnou položkou
        return
    
    # Získání tokenu
    token = revalidate(addon, ui.popinfo) # Opraveno: Volání bez api. prefixu
    if not token:
        # Teoreticky by se sem kód neměl dostat, pokud check_providers_and_prompt uspěl
        # ale pro jistotu selžeme
        if not use_player:
            xbmcplugin.setResolvedUrl(handle, False, xbmcgui.ListItem())
        return

    # V kontextu přehrávání zkontroluj VIP expiraci s varováním dle prahu
    try:
        refresh_vip_info(addon, token_override=token, show_notification=False, log_context="playback", show_warning=True)
    except Exception:
        pass

    # Získání streamovacího odkazu
    link = getlink(ident, token, dtype='video_stream', addon=addon)
    
    if link is not None:
        # Získání názvu, popisu a dalších metadat z params nebo fallback
        title = params.get('tmdb_title', name)
        plot = params.get('tmdb_plot', '')
        thumb = params.get('tmdb_thumb', '')
        backdrop = params.get('tmdb_fanart', '')
        year = params.get('tmdb_year', '')
        
        listitem = xbmcgui.ListItem(label=title, path=link)
        
        # Nastavení obrázků z TMDb
        art = {}
        if thumb:
            art.update({'thumb': thumb, 'icon': thumb, 'poster': thumb})
        if backdrop:
            art['fanart'] = backdrop
        if art:
            listitem.setArt(art)
        
        # Nastavení informací pro OSD
        info_tag = listitem.getVideoInfoTag()
        info_tag.setTitle(title)
        info_tag.setPlot(plot)
        if year:
            try:
                info_tag.setYear(int(year))
            except:
                pass
        
        listitem.setProperty('mimetype', 'application/octet-stream')
        listitem.setProperty('IsPlayable', 'true')
        
        if use_player:
            # Přímé přehrání (pro skripty/isFolder=False)
            xbmc.Player().play(link, listitem)
        else:
            # Standardní plugin resolving (pro složky/isFolder=True)
            xbmcplugin.setResolvedUrl(handle, True, listitem)
    else:
        if not use_player:
            xbmcplugin.setResolvedUrl(handle, False, xbmcgui.ListItem())
        ui.popinfo(addon.getLocalizedString(30158), addon.getAddonInfo('name'), icon='error')
        
 