# -*- coding: utf-8 -*-
#
# CloudSync – FTP synchronizace uživatelské historie
# Merge mechanismus přes JSON na FTP
#

import os
import time
import ftplib
import json
import hashlib
import socket
import xbmc
from resources.lib.database.metadata_db import db
import xbmcaddon
import xbmcvfs
import xbmcgui
from resources.lib.utils.log_utils import get_logger

log = get_logger()


class CloudSync:
    # Fallbacky (když nejsou v settings)
    FTP_HOST = "repo.ido.cz"
    FTP_PORT = 21
    FTP_USER = "mirahoresrepo"
    FTP_PASS = "bovh#DPF29"
    FTP_ROOT = "/web/databaze"

    def __init__(self, addon):
        """addon = xbmcaddon.Addon() instance"""
        self.addon = addon

        # Hlavní přepínače
        self.enabled = addon.getSettingBool("cloud_sync_enabled")
        self.username = addon.getSetting("cloud_sync_user") or ""

        # Parametry serveru z settings (fallbacky na konstanty)
        self.host = addon.getSetting("cloud_sync_host") or self.FTP_HOST
        try:
            self.port = int(addon.getSetting("cloud_sync_port") or self.FTP_PORT)
        except Exception:
            self.port = self.FTP_PORT
        self.root = addon.getSetting("cloud_sync_root") or self.FTP_ROOT

        # Credentials (heslo nelogujeme)
        self.user = addon.getSetting("cloud_sync_login") or self.FTP_USER
        self.pwd = addon.getSetting("cloud_sync_password") or self.FTP_PASS

        # Co synchronizovat
        self.sync_user_data = addon.getSettingBool("cloud_sync_user_data")
        self.sync_media_metadata = addon.getSettingBool("cloud_sync_media_metadata")
        self.sync_cache = addon.getSettingBool("cloud_sync_cache")

        # Hooky
        self.sync_on_start = addon.getSettingBool("cloud_sync_on_start")
        self.sync_on_exit = addon.getSettingBool("cloud_sync_on_exit")

        self.ftp = None

        log.info(f"Init enabled={self.enabled} user={self.username}")

    # ---------------------------------------------------------------------
    # FTP připojení
    # ---------------------------------------------------------------------
    def _ensure_ftp(self):
        if self.ftp:
            return
        try:
            self.ftp = ftplib.FTP()
            self.ftp.connect(self.host, self.port, timeout=15)
            self.ftp.login(self.user, self.pwd)
            # binární režim
            try:
                self.ftp.voidcmd('TYPE I')
            except Exception:
                pass
            # pasivní mód
            try:
                self.ftp.set_pasv(True)
            except Exception:
                pass
            log.info("FTP connected")
        except Exception as e:
            log.error(f"FTP connect FAILED: {e}")
            self.ftp = None

    def close(self):
        if self.ftp:
            try:
                self.ftp.quit()
            except Exception:
                try:
                    self.ftp.close()
                except Exception:
                    pass
            self.ftp = None

    # ---------------------------------------------------------------------
    # Remote cesty
    # ---------------------------------------------------------------------
    def _remote_user_data(self):
        return f"{self.root}/{self.username}/user_history_master.json"

    def _remote_offline_settings(self):
        return f"{self.root}/{self.username}/offline_folders.json"

    def _remote_media_data(self):
        return f"{self.root}/{self.username}/media_metadata.json"

    def _remote_cache_data(self):
        return f"{self.root}/{self.username}/api_cache.json"

    # ---------------------------------------------------------------------
    # Lokální timestamp
    # ---------------------------------------------------------------------
    def _local_ts(self, path):
        try:
            p = xbmcvfs.translatePath(path)
            return os.path.getmtime(p)
        except Exception:
            return 0

    # ---------------------------------------------------------------------
    # Remote timestamp (MDTM + MLSD fallback)
    # ---------------------------------------------------------------------
    def _remote_ts(self, remote_path):
        """
        Vrátí unix timestamp (float) nebo None.
        1) MDTM (YYYYMMDDhhmmss)
        2) MLSD fallback (modify)
        """
        if not self.ftp:
            return None

        # 1) MDTM
        try:
            resp = self.ftp.sendcmd("MDTM " + remote_path)
            if resp.startswith("213"):
                ts = time.mktime(time.strptime(resp[4:], "%Y%m%d%H%M%S"))
                return ts
        except Exception:
            pass

        # 2) MLSD fallback
        try:
            dirname, basename = os.path.split(remote_path)
            if not dirname:
                dirname = "."
            lines = []
            self.ftp.retrlines("MLSD " + dirname, lines.append)
            # typicky: type=file;modify=20240210153045;size=...; filename
            for ln in lines:
                if ln.endswith(basename):
                    parts = ln.split(";")
                    for p in parts:
                        if p.startswith("modify="):
                            val = p.split("=", 1)[1]
                            ts = time.mktime(time.strptime(val, "%Y%m%d%H%M%S"))
                            return ts
        except Exception:
            pass

        return None

    # ---------------------------------------------------------------------
    # Bezpečný DOWNLOAD (do .part + atomický replace)
    # ---------------------------------------------------------------------
    def _download(self, remote_path, local_path):
        tmp_path = None
        try:
            local_path = xbmcvfs.translatePath(local_path)
            local_dir = os.path.dirname(local_path)
            if not xbmcvfs.exists(local_dir):
                ok = xbmcvfs.mkdirs(local_dir)
                log.info(f"mkdirs {local_dir} -> {ok}")

            tmp_path = local_path + ".part"
            with open(tmp_path, "wb") as out_f:
                self.ftp.retrbinary("RETR " + remote_path, out_f.write)

            os.replace(tmp_path, local_path)
            log.info(f"Download OK: {remote_path} -> {local_path}")
            return True
        except Exception as e:
            log.error(f"Download FAIL {remote_path}: {e}")
            try:
                if tmp_path and os.path.exists(tmp_path):
                    os.remove(tmp_path)
            except Exception:
                pass
            return False

    # ---------------------------------------------------------------------
    # Bezpečný UPLOAD (STOR .part -> RNFR/RNTO)
    # ---------------------------------------------------------------------
    def _upload(self, local_path, remote_path):
        tmp_remote = remote_path + ".part"
        try:
            local_path = xbmcvfs.translatePath(local_path)
            # ensure remote folder exists
            try:
                remote_dir = os.path.dirname(remote_path)
                self.ftp.mkd(remote_dir)
            except Exception:
                pass

            with open(local_path, "rb") as f:
                self.ftp.storbinary("STOR " + tmp_remote, f)

            # atomický rename
            try:
                self.ftp.rename(tmp_remote, remote_path)
            except Exception:
                # RNFR/RNTO fallback
                try:
                    self.ftp.sendcmd("RNFR " + tmp_remote)
                    self.ftp.sendcmd("RNTO " + remote_path)
                except Exception:
                    pass

            # vyrovnání lokálního timestampu na vzdálený (pokud jde zjistit)
            remote_ts = self._remote_ts(remote_path)
            if remote_ts:
                try:
                    os.utime(local_path, (remote_ts, remote_ts))
                    log.info(f"Local timestamp updated to match remote: {remote_ts}")
                except Exception as e:
                    log.error(f"Failed to update local timestamp: {e}")

            log.info(f"Upload OK: {remote_path}")
            return True
        except Exception as e:
            log.error(f"Upload FAIL {remote_path}: {e}")
            # úklid .part na serveru
            try:
                self.ftp.delete(tmp_remote)
            except Exception:
                pass
            return False

    # ---------------------------------------------------------------------
    # Merge User Data
    # ---------------------------------------------------------------------
    def _sync_user_history(self):
        if not self.sync_user_data:
            return
            
        remote_path = self._remote_user_data()
        
        # 1. Lokální data (Export z DB)
        log.info("Exporting local user data...")
        local_data = db.export_user_data()
        
        # 2. Vzdálená data (Stáhnout z cloudu)
        remote_data = {"local_history": [], "search_history": [], "series_data": [], "deleted_series": []}

        remote_ts = self._remote_ts(remote_path)
        if remote_ts is not None:
            log.info("Downloading remote user data...")
            import io
            bio = io.BytesIO()
            try:
                self.ftp.retrbinary("RETR " + remote_path, bio.write)
                bio.seek(0)
                raw = bio.read().decode("utf-8", "ignore") or "{}"
                if raw.strip():
                    remote_data = json.loads(raw)
            except Exception as e:
                log.error(f"Failed to read remote user data: {e}")
                
        # 3. MERGE
        merged_data = self._merge_user_data(local_data, remote_data)
        
        log.info(f"[CloudSync] Merge user_data hotov. Lokální smazání: {len(local_data.get('deleted_series', []))}, Vzdálená: {len(remote_data.get('deleted_series', []))}")

        # 4. Uložit zpět do DB
        db.import_merged_user_data(merged_data)

        
        # 5. Odeslat na cloud
        import io
        json_dump = json.dumps(merged_data, ensure_ascii=False)
        bio = io.BytesIO(json_dump.encode("utf-8"))
        tmp_remote = remote_path + ".part"
        try:
            self.ftp.storbinary("STOR " + tmp_remote, bio)
            # rename
            try:
                self.ftp.rename(tmp_remote, remote_path)
            except Exception:
                self.ftp.sendcmd("RNFR " + tmp_remote)
                self.ftp.sendcmd("RNTO " + remote_path)
            log.info("Uploaded merged user data to cloud.")
        except Exception as e:
            log.error(f"Failed to upload merged user data: {e}")
            try:
                self.ftp.delete(tmp_remote)
            except Exception:
                pass

    def _merge_user_data(self, local: dict, remote: dict) -> dict:
        merged = {"local_history": [], "search_history": [], "series_data": [], "deleted_series": []}

        # Merge search history
        search_dict = {}
        for x in remote.get("search_history", []):
            search_dict[x["query"]] = x.get("timestamp", 0)
        for x in local.get("search_history", []):
            q = x["query"]
            t = x.get("timestamp", 0)
            if q not in search_dict or t > search_dict[q]:
                search_dict[q] = t

        for q, t in search_dict.items():
            merged["search_history"].append({"query": q, "timestamp": t})

        # Merge local history (klíč je tmdb_id a media_type)
        history_dict = {}

        def _get_key(item):
            return f"{item.get('media_type')}_{item.get('tmdb_id')}"

        def _get_ts(item):
            val = item.get("watched_at", "1970-01-01 00:00:00")
            if not val: val = "1970-01-01 00:00:00"
            return val

        for x in remote.get("local_history", []):
            history_dict[_get_key(x)] = x

        for x in local.get("local_history", []):
            k = _get_key(x)
            if k not in history_dict:
                history_dict[k] = x
            else:
                ts_local = _get_ts(x)
                ts_remote = _get_ts(history_dict[k])
                if ts_local > ts_remote:
                    history_dict[k] = x

        merged["local_history"] = list(history_dict.values())

        # ── TOMBSTONES: sloučit deleted_series z obou stran ─────────────────
        # Vítěz = novější deleted_at (ten, kdo smazal later, má přednost)
        tombstone_dict = {}
        for t in remote.get("deleted_series", []):
            tombstone_dict[t["series_name"]] = t["deleted_at"]
        for t in local.get("deleted_series", []):
            k = t["series_name"]
            if k not in tombstone_dict or t["deleted_at"] > tombstone_dict[k]:
                tombstone_dict[k] = t["deleted_at"]
        merged["deleted_series"] = [{"series_name": k, "deleted_at": v} for k, v in tombstone_dict.items()]
        # ─────────────────────────────────────────────────────────────────────

        # Merge series_data (klíč je series_name, merge podle last_modified)
        series_dict = {}

        def _get_series_ts(item):
            val = item.get("last_modified", "1970-01-01 00:00:00")
            if not val: val = "1970-01-01 00:00:00"
            return val

        for x in remote.get("series_data", []):
            series_dict[x["series_name"]] = x

        for x in local.get("series_data", []):
            k = x["series_name"]
            if k not in series_dict:
                series_dict[k] = x
            else:
                ts_local = _get_series_ts(x)
                ts_remote = _get_series_ts(series_dict[k])
                if ts_local > ts_remote:
                    series_dict[k] = x

        # ── Tombstone filtr: odstraň seriály smazané po jejich last_modified ─
        filtered_series = []
        from resources.lib.utils.ui_utils import clean_filename
        
        for s in series_dict.values():
            sname = s["series_name"]
            # Zkusíme najít tombstone pro tento název nebo jeho varianty
            variants = [sname, clean_filename(sname), sname.replace(':', '_').replace('?', '_')]
            
            # Pokud máme v JSONu tmdb_id, přidáme i tuhle variantu
            try:
                s_json = json.loads(s.get("json_data", "{}"))
                tid = (s_json.get("tmdb") or {}).get("id")
                if tid:
                    variants.append(f"tmdb_{tid}")
            except:
                pass

            tombstone_found = False
            for v in variants:
                if v in tombstone_dict:
                    deleted_at = tombstone_dict[v]
                    last_mod = _get_series_ts(s)
                    if deleted_at >= last_mod:
                        log.info(f"[CloudSync] Tombstone ('{v}' @ {deleted_at}) odstranil sériál ze merge: {sname} (mod: {last_mod})")
                        tombstone_found = True
                        break
            
            if not tombstone_found:
                filtered_series.append(s)

        # ─────────────────────────────────────────────────────────────────────

        merged["series_data"] = filtered_series


        return merged



    # ---------------------------------------------------------------------
    # Sync Media Metadata (tabulka 'media' z metadata.db)
    # ---------------------------------------------------------------------
    def _sync_media_metadata(self):
        """Synchronizuje tabulku media (TMDb/OMDb metadata) přes FTP jako JSON."""
        if not self.sync_media_metadata:
            return

        remote_path = self._remote_media_data()
        log.info("[CloudSync] Exportuji media metadata...")
        local_list = db.export_media_data()

        # Stáhnout remote data
        remote_list = []
        remote_ts = self._remote_ts(remote_path)
        if remote_ts is not None:
            log.info("[CloudSync] Stahuju remote media_metadata.json...")
            import io
            bio = io.BytesIO()
            try:
                self.ftp.retrbinary("RETR " + remote_path, bio.write)
                bio.seek(0)
                raw = bio.read().decode("utf-8", "ignore") or "[]"
                if raw.strip():
                    remote_list = json.loads(raw)
            except Exception as e:
                log.error(f"[CloudSync] Chyba při stahování media_metadata.json: {e}")

        # Merge: klíč = tmdb_id, vítěz = novější last_updated
        merged_dict = {}
        for item in remote_list:
            tid = item.get('tmdb_id')
            if tid:
                merged_dict[tid] = item
        for item in local_list:
            tid = item.get('tmdb_id')
            if not tid:
                continue
            if tid not in merged_dict:
                merged_dict[tid] = item
            else:
                local_ts = item.get('last_updated', '1970-01-01 00:00:00') or '1970-01-01 00:00:00'
                remote_ts_val = merged_dict[tid].get('last_updated', '1970-01-01 00:00:00') or '1970-01-01 00:00:00'
                if local_ts > remote_ts_val:
                    merged_dict[tid] = item

        merged_list = list(merged_dict.values())
        log.info(f"[CloudSync] Media merge: {len(merged_list)} záznamů.")

        # Importovat do lokální DB
        db.import_merged_media_data(merged_list)

        # Upload na FTP
        import io
        json_dump = json.dumps(merged_list, ensure_ascii=False)
        bio = io.BytesIO(json_dump.encode("utf-8"))
        tmp_remote = remote_path + ".part"
        try:
            self.ftp.storbinary("STOR " + tmp_remote, bio)
            try:
                self.ftp.rename(tmp_remote, remote_path)
            except Exception:
                self.ftp.sendcmd("RNFR " + tmp_remote)
                self.ftp.sendcmd("RNTO " + remote_path)
            log.info("[CloudSync] Sync media_metadata OK.")
        except Exception as e:
            log.error(f"[CloudSync] Upload media_metadata.json selhal: {e}")
            try:
                self.ftp.delete(tmp_remote)
            except Exception:
                pass

    # ---------------------------------------------------------------------
    # Sync API Cache (cache.db, pouze namespace 'tmdb')
    # ---------------------------------------------------------------------
    def _sync_cache_data(self):
        """Synchronizuje platné záznamy z cache.db (namespace tmdb) přes FTP."""
        if not self.sync_cache:
            return

        import io, sqlite3, time as _time

        remote_path = self._remote_cache_data()
        now = int(_time.time())

        # Lokální export: pouze platné záznamy namespace='tmdb'
        local_items = []
        cache_db_path = ''
        try:
            import xbmcvfs, xbmcaddon
            profile = xbmcvfs.translatePath(xbmcaddon.Addon('plugin.video.mmirousek_v3').getAddonInfo('profile'))
            cache_db_path = os.path.join(profile, 'cache.db')
            if os.path.exists(cache_db_path):
                conn_c = sqlite3.connect(cache_db_path, timeout=10, check_same_thread=False)
                cur = conn_c.execute(
                    "SELECT namespace, key_hash, ts, ttl, hdr, data FROM api_cache "
                    "WHERE namespace='tmdb' AND (ttl = 0 OR (? - ts) < ttl)",
                    (now,)
                )
                for row in cur.fetchall():
                    local_items.append({
                        "namespace": row[0],
                        "key_hash": row[1],
                        "ts": row[2],
                        "ttl": row[3],
                        "hdr": row[4],
                        "data": row[5]
                    })
                conn_c.close()
            log.info(f"[CloudSync] API cache export: {len(local_items)} platných záznamů.")
        except Exception as e:
            log.error(f"[CloudSync] Cache export error: {e}")


        # Stáhnout remote data
        remote_items = []
        remote_ts = self._remote_ts(remote_path)
        if remote_ts is not None:
            log.info("[CloudSync] Stahuju remote api_cache.json...")
            bio = io.BytesIO()
            try:
                self.ftp.retrbinary("RETR " + remote_path, bio.write)
                bio.seek(0)
                raw = bio.read().decode("utf-8", "ignore") or "[]"
                if raw.strip():
                    remote_items = json.loads(raw)
            except Exception as e:
                log.error(f"[CloudSync] Chyba při stahování api_cache.json: {e}")

        # Merge: klíč = (namespace, key_hash), vítěz = novější ts
        merged_dict = {}
        for item in remote_items:
            k = (item.get('namespace'), item.get('key_hash'))
            merged_dict[k] = item
        for item in local_items:
            k = (item.get('namespace'), item.get('key_hash'))
            if k not in merged_dict or item.get('ts', 0) > merged_dict[k].get('ts', 0):
                merged_dict[k] = item

        # Vyfiltrovat stále platné po merge
        merged_list = [
            it for it in merged_dict.values()
            if it.get('ttl', 0) == 0 or (now - it.get('ts', 0)) < it.get('ttl', 0)
        ]
        log.info(f"[CloudSync] API cache merge: {len(merged_list)} záznamů.")

        # Importovat do lokální cache.db
        try:
            if local_items or remote_items:
                conn_c = sqlite3.connect(cache_db_path, timeout=10, check_same_thread=False)
                conn_c.execute("PRAGMA journal_mode = WAL")
                for it in merged_list:
                    conn_c.execute(
                        "INSERT OR REPLACE INTO api_cache (namespace, key_hash, ts, ttl, hdr, data) "
                        "VALUES (?, ?, ?, ?, ?, ?)",
                        (it['namespace'], it['key_hash'], it['ts'], it['ttl'], it['hdr'], it['data'])
                    )
                conn_c.commit()
                conn_c.close()
        except Exception as e:
            log.error(f"[CloudSync] Cache import error: {e}")

        # Upload na FTP
        json_dump = json.dumps(merged_list, ensure_ascii=False)
        bio = io.BytesIO(json_dump.encode("utf-8"))
        tmp_remote = remote_path + ".part"
        try:
            self.ftp.storbinary("STOR " + tmp_remote, bio)
            try:
                self.ftp.rename(tmp_remote, remote_path)
            except Exception:
                self.ftp.sendcmd("RNFR " + tmp_remote)
                self.ftp.sendcmd("RNTO " + remote_path)
            log.info("[CloudSync] Sync api_cache OK.")
        except Exception as e:
            log.error(f"[CloudSync] Upload api_cache.json selhal: {e}")
            try:
                self.ftp.delete(tmp_remote)
            except Exception:
                pass

    # ---------------------------------------------------------------------
    # FULL SYNC
    # ---------------------------------------------------------------------
    def sync_all(self):
        if not self.enabled:
            log.info("Disabled → skip sync")
            return

        self._ensure_ftp()
        if not self.ftp:
            return

        try:
            self._ensure_remote_dirs()
            if self.sync_user_data:
                self._sync_user_history()
            if self.sync_media_metadata:
                self._sync_media_metadata()
            if self.sync_cache:
                self._sync_cache_data()
            log.info("Sync ALL complete")
        finally:
            self.close()

    # ---------------------------------------------------------------------
    # Hooky pro service
    # ---------------------------------------------------------------------
    def on_start(self):
        if self.enabled and self.sync_on_start:
            log.info("on_start → sync_all()")
            self.sync_all()

    def on_exit(self):
        if self.enabled and self.sync_on_exit:
            log.info("on_exit → sync_all()")
            self.sync_all()

    # ---------------------------------------------------------------------
    # Přenos nastavení složek
    # ---------------------------------------------------------------------
    def upload_offline_settings(self, data: list) -> bool:
        """Nahraje seznam cest do souboru offline_folders.json na FTP."""
        if not self.enabled or not self.username:
            return False
        
        remote_path = self._remote_offline_settings()
        try:
            self._ensure_ftp()
            if not self.ftp:
                return False
            
            self._ensure_remote_dirs()
            
            import io
            json_data = json.dumps(data, indent=2, ensure_ascii=False)
            bio = io.BytesIO(json_data.encode('utf-8'))
            
            self.ftp.storbinary(f"STOR {remote_path}", bio)
            log.info(f"upload_offline_settings OK: {remote_path}")
            return True
        except Exception as e:
            log.error(f"upload_offline_settings FAIL: {e}")
            return False
        finally:
            self.close()

    def download_offline_settings(self) -> list:
        """Stáhne seznam cest ze souboru offline_folders.json na FTP. Vrací list nebo None."""
        if not self.enabled or not self.username:
            return None
        
        remote_path = self._remote_offline_settings()
        try:
            self._ensure_ftp()
            if not self.ftp:
                return None
            
            import io
            bio = io.BytesIO()
            self.ftp.retrbinary(f"RETR {remote_path}", bio.write)
            
            bio.seek(0)
            return json.loads(bio.read().decode('utf-8'))
        except ftplib.error_perm as e:
            if str(e).startswith("550"): # Not found
                return []
            log.error(f"download_offline_settings FTP error: {e}")
            return None
        except Exception as e:
            log.error(f"download_offline_settings FAIL: {e}")
            return None
        finally:
            self.close()

    def sync_offline_folders_ui(self, addon_name="YAWsP"):
        """Lokální synchronizace aktivní složky ('dfolder') z vybraného slotu."""
        try:
            raw_slot = self.addon.getSetting('dfolder_slot') or '0'
            slot_idx = 0
            if raw_slot == 'Složka 1': slot_idx = 0
            elif raw_slot == 'Složka 2': slot_idx = 1
            elif raw_slot == 'Složka 3': slot_idx = 2
            else:
                try:
                    slot_idx = int(raw_slot)
                except ValueError:
                    slot_idx = 0
            
            slot_key = f'dfolder_{slot_idx + 1}'
            slot_path = self.addon.getSetting(slot_key) or ''
            
            self.addon.setSetting('dfolder', slot_path)
            
            log.info(f"Aktivní složka ručně nastavena na slot {slot_idx+1}: {slot_path}")
            xbmcgui.Dialog().notification(addon_name, f"Aktivní složka synchronizována (Slot {slot_idx+1})", xbmcgui.NOTIFICATION_INFO, 3000)
        except Exception as e:
            log.error(f"Sync offline folders UI error: {e}")
            xbmcgui.Dialog().ok(addon_name, f"Chyba při synchronizaci složek: {e}")

    # ---------------------------------------------------------------------
    # UI Akce (přesunuto z kořenového router bridge)
    # ---------------------------------------------------------------------

    def export_offline_settings_ui(self, addon_name="YAWsP"):
        """UI obálka pro export nastavení složek do cloudu."""
        try:
            paths = []
            for i in range(1, 4):
                path = self.addon.getSetting(f'dfolder_{i}') or ""
                paths.append(path)
            
            if not self.enabled or not self.username:
                xbmcgui.Dialog().ok(addon_name, "CloudSync není povolen nebo chybí uživatelské jméno.")
                return

            if self.upload_offline_settings(paths):
                xbmcgui.Dialog().notification(addon_name, "Cesty úspěšně exportovány do cloudu", xbmcgui.NOTIFICATION_INFO, 3000)
            else:
                xbmcgui.Dialog().ok(addon_name, "Export do cloudu selhal (viz log).")
        except Exception as e:
            log.error(f"CloudSync export UI error: {e}")
            xbmcgui.Dialog().ok(addon_name, f"Chyba při exportu: {e}")

    def import_offline_settings_ui(self, addon_name="YAWsP"):
        """UI obálka pro výběr a import nastavení složek z cloudu."""
        try:
            if not self.enabled or not self.username:
                xbmcgui.Dialog().ok(addon_name, "CloudSync není povolen nebo chybí uživatelské jméno.")
                return

            cloud_paths = self.download_offline_settings()
            if cloud_paths is None:
                xbmcgui.Dialog().ok(addon_name, "Nepodařilo se stáhnout data z cloudu.")
                return
            
            if not cloud_paths:
                xbmcgui.Dialog().ok(addon_name, "V cloudu nebyly nalezeny žádné uložené cesty.")
                return

            valid_paths = [p for p in cloud_paths if p]
            if not valid_paths:
                 xbmcgui.Dialog().ok(addon_name, "V cloudu jsou jen prázdné cesty.")
                 return

            idx = xbmcgui.Dialog().select("Vyberte cestu z cloudu pro import", valid_paths)
            if idx < 0: return
            
            chosen_path = valid_paths[idx]

            slots = ["Složka 1", "Složka 2", "Složka 3"]
            target_idx = xbmcgui.Dialog().select(f"Importovat do lokálního slotu:", slots)
            if target_idx < 0: return

            self.addon.setSetting(f'dfolder_{target_idx + 1}', chosen_path)
            xbmcgui.Dialog().notification(addon_name, f"Cesta importována do Slotu {target_idx + 1}", xbmcgui.NOTIFICATION_INFO, 3000)
        except Exception as e:
            log.error(f"CloudSync import UI error: {e}")
            xbmcgui.Dialog().ok(addon_name, f"Chyba při importu: {e}")

    # ---------------------------------------------------------------------
    # Remote dirs (mkdir)
    # ---------------------------------------------------------------------
    def _ensure_remote_dirs(self):
        if not self.username or not self.ftp:
            log.error("Missing username or ftp is None - skip mkdir")
            return
        base = f"{self.root}/{self.username}"
        for folder in [base]:
            try:
                self.ftp.mkd(folder)
            except Exception:
                pass


def upload_support_log(local_path, remote_name=None):
    """
    Nahraje support log do globální FTP složky `${FTP_ROOT}/Logy`.
    Vrací (ok: bool, remote_path: str|None) a nikdy nevyhazuje výjimku ven.
    """
    ftp = None
    try:
        if not local_path:
            return False, None
        lp = xbmcvfs.translatePath(local_path)
        if not os.path.exists(lp):
            return False, None

        install_id = "unknown"
        try:
            addon = xbmcaddon.Addon('plugin.video.mmirousek_v3')
            profile = xbmcvfs.translatePath(addon.getAddonInfo('profile'))
            iid_path = os.path.join(profile, 'telemetry_install_id.txt')
            if os.path.exists(iid_path):
                with open(iid_path, 'r', encoding='utf-8') as f:
                    install_id = (f.read() or '').strip() or install_id
        except Exception:
            pass
        install_id = ''.join(ch for ch in str(install_id) if ch.isalnum() or ch in ('-', '_'))[:36] or "unknown"

        if not remote_name:
            ts = time.strftime("%Y%m%d-%H%M%S")
            host = ''.join(ch for ch in socket.gethostname() if ch.isalnum() or ch in ('-', '_'))[:24] or "host"
            remote_name = f"sm2_log_{ts}_{host}_{install_id}.txt"

        remote_dir = f"{CloudSync.FTP_ROOT.rstrip('/')}/Logy"
        remote_path = f"{remote_dir}/{remote_name}"
        tmp_remote = remote_path + ".part"

        ftp = ftplib.FTP()
        ftp.connect(CloudSync.FTP_HOST, CloudSync.FTP_PORT, timeout=15)
        ftp.login(CloudSync.FTP_USER, CloudSync.FTP_PASS)
        try:
            ftp.voidcmd('TYPE I')
        except Exception:
            pass
        try:
            ftp.set_pasv(True)
        except Exception:
            pass

        for folder in (CloudSync.FTP_ROOT.rstrip('/'), remote_dir):
            try:
                ftp.mkd(folder)
            except Exception:
                pass

        with open(lp, "rb") as f:
            ftp.storbinary("STOR " + tmp_remote, f)

        try:
            ftp.rename(tmp_remote, remote_path)
        except Exception:
            ftp.sendcmd("RNFR " + tmp_remote)
            ftp.sendcmd("RNTO " + remote_path)

        return True, remote_path
    except Exception as e:
        log.warning(f"upload_support_log FAIL: {e}")
        try:
            if ftp:
                ftp.delete(tmp_remote)
        except Exception:
            pass
        return False, None
    finally:
        try:
            if ftp:
                ftp.quit()
        except Exception:
            try:
                if ftp:
                    ftp.close()
            except Exception:
                pass
