# -*- coding: utf-8 -*-
# Module: download_manager
# Author: mmirousek (upraveno pro VFS)
# License: AGPL v3

import os
import io
import re
import traceback
import requests
import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
from urllib.parse import parse_qsl
from resources.lib.auth.webshare_auth import WebshareClient
from resources.lib.utils.ui_utils import clean_filename
from resources.lib.utils.ui_utils import reset_cancel_flags, set_cancel_flags, is_cancel_requested
from resources.lib.utils.log_utils import get_logger

log = get_logger()

ADDON_ID = 'plugin.video.mmirousek_v3'
ADDON = xbmcaddon.Addon(ADDON_ID)
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"
BASE = 'https://webshare.cz'
_session = requests.Session()
_session.headers.update({'User-Agent': UA, 'Referer': BASE})

# --- Cancel flag: lokální (pro běžící instanci) + globální přes Window(10000) ---
CANCEL_DOWNLOAD = False
WIN_KEY_TRANSFER = 'mm_cancel_download'       # cancel současného přenosu (epizody)
WIN_KEY_ALL      = 'mm_cancel_download_all'   # cancel celé sezóny / všech přenosů

def cancel_download():
    """Nastaví globální cancel flagy. Volá se z routeru (jiná RunPlugin instance)."""
    set_cancel_flags()  # nově z ui_utils
    log.info("Cancel flags set (transfer + all)")

def reset_cancel_flag():
    """Resetuje lokální cancel flag (pro nově spouštěná stahování)."""
    reset_cancel_flags()  # nově z ui_utils
    log.debug("Cancel flags reset")

def popinfo(message,
            heading=ADDON.getAddonInfo('name'),
            icon=xbmcgui.NOTIFICATION_INFO,
            time=3000,
            sound=False):
    """Wrapper pro notifikace (defaultně bez zvuku)."""
    try:
        xbmcgui.Dialog().notification(heading, message, icon, time, sound=sound)
    except Exception:
        xbmcgui.Dialog().notification(heading, message, icon, time)

def download(params):
    """
    Stáhne soubor z Webshare podle identu.
    - Podporuje lokální i síťové (VFS) cesty.
    """
    try:
        log.debug(f"params={params}")
        reset_cancel_flag()

        if not params or not params.get('ident'):
            log.error("Missing ident in params")
            popinfo('Vznikla chyba při komunikaci s Webshare API.', icon=xbmcgui.NOTIFICATION_WARNING)
            return

        ws = WebshareClient(ADDON_ID)
        _ = ws.revalidate()
        link = ws.getlink(params['ident'], dtype='download')
        log.debug(f"getlink returned: {link}")
        if not link:
            popinfo('Vznikla chyba při komunikaci s Webshare API.', icon=xbmcgui.NOTIFICATION_WARNING)
            return

        url = link
        _orig_session_headers = None
        if '\n' in link:
            try:
                url, hdrs = link.split('\n', 1)
                pairs = dict(parse_qsl(hdrs, encoding='utf-8'))
                _orig_session_headers = dict(_session.headers)
                _session.headers.update(pairs)
            except Exception as e:
                log.warning(f"failed parsing headers: {e}")

        # Cílová složka
        dfolder = ADDON.getSetting('dfolder') or ''
        if dfolder:
            dest_real = xbmcvfs.translatePath(dfolder)
            # Změna: xbmcvfs.mkdirs funguje i pro síťové cesty, os.makedirs jen pro lokální
            if not xbmcvfs.exists(dest_real):
                try:
                    xbmcvfs.mkdirs(dest_real)
                except Exception as e:
                    log.error(f"cannot create destination {dest_real}: {e}")
                    # Fallback na výběr složky
                    destination = xbmcgui.Dialog().browse(3, 'Vyberte složku pro stahování', 'files')
                    if not destination:
                        return
                    dest_real = xbmcvfs.translatePath(destination)
        else:
            destination = xbmcgui.Dialog().browse(3, 'Vyberte složku pro stahování', 'files')
            if not destination:
                return
            dest_real = xbmcvfs.translatePath(destination)

        # Název souboru
        raw_name = params.get('name') or url.split('/')[-1].split('?')[0] or params.get('ident')
        base_name = clean_filename(raw_name)
        _, ext = os.path.splitext(url.rsplit('/', 1)[-1].split('?', 1)[0])
        _, base_ext = os.path.splitext(base_name)
        filename = base_name if base_ext else (base_name + (ext or ""))
        
        # Sestavení finální cesty (join funguje univerzálně pro stringy)
        if dest_real.endswith('/') or dest_real.endswith('\\'):
            target_path = dest_real + filename
        else:
            target_path = dest_real + '/' + filename
            
        log.info(f"final target_path={target_path}")

        try:
            download_file(url, target_path, expected_size=None)
        finally:
            if _orig_session_headers is not None:
                _session.headers.clear()
                _session.headers.update(_orig_session_headers)
    except Exception as e:
        log.error(f"unexpected error: {e}\n{traceback.format_exc()}")
        popinfo("Chyba při zahájení stahování", icon=xbmcgui.NOTIFICATION_ERROR)

def download_file(url, name, expected_size=None):
    """
    Stáhne URL na zadanou cestu.
    - Rozlišuje lokální a síťové (VFS) cesty.
    - Lokální cesty: Používá io.open, podporuje RESUME.
    - Síťové cesty: Používá xbmcvfs.File, nepodporuje RESUME (vždy od začátku).
    """
    response = None
    bf = None # Buffer file object
    
    try:
        log.info(f"start url={url} target={name}")

        # Nastavení notifikací
        notify_enabled = ADDON.getSettingBool('dnotify')
        interval_raw = ADDON.getSetting('dnevery') or '10%'
        try:
            interval = int(str(interval_raw).replace('%', '').strip())
            if interval <= 0 or interval > 100:
                interval = 10
        except Exception:
            interval = 10

        # Přelož cestu (pro jistotu, i když už by měla být)
        real_path = xbmcvfs.translatePath(name)
        
        # Detekce, zda se jedná o lokální cestu (viditelnou pro OS)
        # Zkontrolujeme rodičovský adresář, protože soubor ještě nemusí existovat
        parent_dir = os.path.dirname(real_path)
        is_local = os.path.exists(parent_dir)

        # --- Logika RESUME a velikosti ---
        current_size = 0
        
        if is_local:
            # Pro lokální soubory zkusíme resume
            if os.path.exists(real_path):
                try:
                    current_size = os.path.getsize(real_path)
                except Exception:
                    current_size = 0
        else:
            # Pro síťové soubory (VFS) začínáme vždy od nuly (resume je riskantní/složité přes VFS)
            current_size = 0
            # Pokud soubor existuje na síti, bude přepsán
            
        # HTTP Range pro resume (jen pokud je local a máme nějaká data)
        headers = {}
        if current_size > 0:
            headers['Range'] = f"bytes={current_size}-"
            log.info(f"Resume (Local) od {current_size} bajtů")
        elif not is_local:
            log.info("VFS (Network) download - always fresh start")

        # HTTP GET
        response = _session.get(url, headers=headers, stream=True, timeout=60, allow_redirects=True)
        status = getattr(response, "status_code", None)
        
        # Kontrola status kódu (200 OK, 206 Partial Content)
        if status not in (200, 206):
            if notify_enabled:
                popinfo("Stahování selhalo (server error)", icon=xbmcgui.NOTIFICATION_ERROR)
            return

        # Celková velikost
        total_size = 0
        try:
            total_size = int(response.headers.get("content-length") or 0)
        except Exception:
            total_size = 0
            
        if total_size <= 0 and expected_size:
            try:
                total_size = int(expected_size)
            except Exception:
                total_size = 0

        # Pokud máme resume (status 206), přičti current_size k total_size
        if status == 206:
            total_size += current_size
        elif status == 200 and current_size > 0:
            # Server nepodporuje Range, stahuje se od začátku -> reset current_size
            current_size = 0

        chunk_size = 4 * 1024 * 1024 # 4 MB chunks
        downloaded = current_size

        # Inicializace prahu pro notifikace
        if total_size > 0:
            pct_now = int(downloaded * 100 / total_size)
            next_threshold = ((pct_now // interval) * interval) + interval
            if next_threshold <= pct_now:
                next_threshold += interval
            next_threshold = min(next_threshold, 100)
        else:
            next_threshold = interval

        # --- Otevření souboru pro zápis ---
        if is_local:
            # Lokální disk: io.open s 'ab' (append) nebo 'wb' (write)
            mode = 'ab' if current_size > 0 else 'wb'
            bf = io.open(real_path, mode)
        else:
            # Síťový disk: xbmcvfs.File (vždy write 'w')
            bf = xbmcvfs.File(real_path, 'w')

        # --- Smyčka stahování ---
        try:
            for chunk in response.iter_content(chunk_size=chunk_size):
                if is_cancel_requested():
                    log.info("Cancel requested")
                    if notify_enabled:
                        popinfo("Stahování zrušeno", icon=xbmcgui.NOTIFICATION_INFO, time=2000)
                    raise KeyboardInterrupt("Cancelled by user")

                if not chunk:
                    continue

                bf.write(chunk)
                downloaded += len(chunk)
                # Notifikace a refresh UI
                if total_size > 0:
                    pct = int(downloaded * 100 / total_size)
                    should_notify = False
                    while pct >= next_threshold and next_threshold <= 100:
                        should_notify = True
                        next_threshold += interval
                        
                    if should_notify:
                        # Ošetření: Zrušen automatický periodický Container.Refresh 
                        # Narušoval ovládání a "kradl" focus i když byl uživatel pryč z doplňku.
                            
                        if notify_enabled:
                            # Krátký label: SxxExx nebo zkrácený název
                            short_label = os.path.basename(real_path)[:15]  # fallback
                            popinfo(f"{short_label} – {pct}%", time=1500, sound=False)

        finally:
            # Bezpečné uzavření souboru
            if bf:
                bf.close()
            log.info(f"finished downloaded={downloaded}")

            # Vynucení update cache ihned po stažení, aby UI vidělo nový soubor
            try:
                from resources.lib.utils.offline_scanner import run_offline_scan
                run_offline_scan(force=True)
            except Exception as e:
                log.error(f"Nelze vynutit offline_scan po stažení: {e}")

            try:
                xbmc.executebuiltin('Container.Refresh')
            except Exception:
                pass
            if notify_enabled:
                # Vyčisti předchozí notifikace (trik: prázdná notifikace na 1 ms)
                xbmc.executebuiltin("Notification(Clean, , 1)")
                # Krátká finální notifikace
                popinfo(f"Staženo: {os.path.basename(real_path)}", icon=xbmcgui.NOTIFICATION_INFO,)

    except KeyboardInterrupt:
        log.info("cancelled by user")
        # U VFS (xbmcvfs) často zůstane soubor nevalidní/nedokončený, to je OK chování
        pass

    except Exception as e:
        log.error(f"error: {e}\n{traceback.format_exc()}")
        if notify_enabled:
            popinfo("Chyba při stahování", icon=xbmcgui.NOTIFICATION_ERROR)

    finally:
        try:
            if response:
                response.close()
        except Exception:
            pass
