# -*- coding: utf-8 -*-
"""
Záloha / obnova userdata settings.xml (lokální rotace + per-device CloudSync FTP).
"""
from __future__ import annotations

import os
import re
import socket
import time

import xbmc
import xbmcaddon
import xbmcgui
import xbmcvfs

from resources.lib.utils.log_utils import get_logger

log = get_logger()

ADDON_ID = "plugin.video.mmirousek_v3"
MIN_SETTINGS_BYTES = 100
LOCAL_SLOTS = 3
_BACKUP_DEBOUNCE_S = 2.0
_last_backup_ts = 0.0


def _addon(addon=None):
    return addon or xbmcaddon.Addon(ADDON_ID)


def _profile(addon=None):
    a = _addon(addon)
    return xbmcvfs.translatePath(a.getAddonInfo("profile"))


def _settings_path(addon=None):
    return os.path.join(_profile(addon), "settings.xml")


def _backup_dir(addon=None):
    return os.path.join(_profile(addon), "backups", "settings")


def _meta_dir(addon=None):
    return os.path.join(_profile(addon), "backups")


def _ensure_dir(path):
    if not xbmcvfs.exists(path):
        xbmcvfs.mkdirs(path)


def _slot_path(addon, slot: int) -> str:
    return os.path.join(_backup_dir(addon), f"settings.xml.bak{slot}")


def _sanitize_id(text: str, max_len: int = 40) -> str:
    s = (text or "").strip().lower()
    s = re.sub(r"[^a-z0-9]+", "_", s)
    s = re.sub(r"_+", "_", s).strip("_")
    return (s or "device")[:max_len]


def _platform_hint() -> str:
    try:
        if xbmc.getCondVisibility("System.Platform.Android"):
            return "android"
        if xbmc.getCondVisibility("System.Platform.Windows"):
            return "windows"
        if xbmc.getCondVisibility("System.Platform.Linux"):
            return "linux"
        if xbmc.getCondVisibility("System.Platform.OSX"):
            return "osx"
        if xbmc.getCondVisibility("System.Platform.IOS"):
            return "ios"
        if xbmc.getCondVisibility("System.Platform.TVOS"):
            return "tvos"
    except Exception:
        pass
    return "other"


def _device_name_raw() -> str:
    try:
        friendly = (xbmc.getInfoLabel("System.FriendlyName") or "").strip()
        if friendly:
            return friendly
    except Exception:
        pass
    try:
        return socket.gethostname() or "device"
    except Exception:
        return "device"


def device_id(addon=None) -> str:
    """Stabilní slug platforma + jméno zařízení; cache v backups/device_id.txt."""
    meta = _meta_dir(addon)
    cache_path = os.path.join(meta, "device_id.txt")
    try:
        if os.path.exists(cache_path):
            with open(cache_path, "r", encoding="utf-8") as f:
                cached = (f.read() or "").strip()
            if cached and re.fullmatch(r"[a-z0-9_]{1,40}", cached):
                return cached
    except Exception:
        pass

    did = _sanitize_id(f"{_platform_hint()}_{_device_name_raw()}")
    try:
        _ensure_dir(meta)
        with open(cache_path, "w", encoding="utf-8") as f:
            f.write(did)
    except Exception as e:
        log.debug(f"[SettingsBackup] device_id cache write: {e}")
    return did


def _remember_cloud_user(username: str, addon=None):
    if not username:
        return
    try:
        meta = _meta_dir(addon)
        _ensure_dir(meta)
        with open(os.path.join(meta, "cloud_user.txt"), "w", encoding="utf-8") as f:
            f.write(username.strip())
    except Exception as e:
        log.debug(f"[SettingsBackup] cloud_user write: {e}")


def remembered_cloud_user(addon=None) -> str:
    try:
        path = os.path.join(_meta_dir(addon), "cloud_user.txt")
        if os.path.exists(path):
            with open(path, "r", encoding="utf-8") as f:
                return (f.read() or "").strip()
    except Exception:
        pass
    try:
        return (_addon(addon).getSetting("cloud_sync_user") or "").strip()
    except Exception:
        return ""


def _file_size(path: str) -> int:
    try:
        return os.path.getsize(path)
    except Exception:
        try:
            f = xbmcvfs.File(path)
            try:
                data = f.readBytes()
                return len(data) if data else 0
            finally:
                f.close()
        except Exception:
            return 0


def _is_valid_settings_file(path: str) -> bool:
    if not path or not xbmcvfs.exists(path):
        # xbmcvfs.exists na souboru někdy chce trailing slash quirks — fallback os.path
        if not os.path.exists(path):
            return False
    return _file_size(path) >= MIN_SETTINGS_BYTES


def _vfs_copy(src: str, dst: str) -> bool:
    try:
        src = xbmcvfs.translatePath(src)
        dst = xbmcvfs.translatePath(dst)
        parent = os.path.dirname(dst)
        _ensure_dir(parent)
        if xbmcvfs.exists(dst) or os.path.exists(dst):
            xbmcvfs.delete(dst)
        ok = xbmcvfs.copy(src, dst)
        if ok and _is_valid_settings_file(dst):
            return True
        # Fallback: binary read/write
        with open(src, "rb") as f_in:
            data = f_in.read()
        if len(data) < MIN_SETTINGS_BYTES:
            return False
        with open(dst, "wb") as f_out:
            f_out.write(data)
        return _is_valid_settings_file(dst)
    except Exception as e:
        log.error(f"[SettingsBackup] copy FAIL {src} -> {dst}: {e}")
        return False


def rotate_and_store_local(addon=None) -> bool:
    """Zkopíruje aktuální settings.xml do bak0 (rotace bak0→1→2)."""
    a = _addon(addon)
    src = _settings_path(a)
    if not _is_valid_settings_file(src):
        log.warning("[SettingsBackup] Přeskakuji lokální zálohu — settings.xml chybí nebo je příliš malý")
        return False

    bdir = _backup_dir(a)
    _ensure_dir(bdir)

    # rotace: bak1 -> bak2, bak0 -> bak1
    for slot in range(LOCAL_SLOTS - 2, -1, -1):
        older = _slot_path(a, slot)
        newer = _slot_path(a, slot + 1)
        if os.path.exists(older) or xbmcvfs.exists(older):
            try:
                if os.path.exists(newer) or xbmcvfs.exists(newer):
                    xbmcvfs.delete(newer)
                xbmcvfs.copy(older, newer)
            except Exception as e:
                log.debug(f"[SettingsBackup] rotate {slot}->{slot+1}: {e}")

    ok = _vfs_copy(src, _slot_path(a, 0))
    if ok:
        log.info(f"[SettingsBackup] Lokální záloha OK → bak0 ({device_id(a)})")
    return ok


def list_local_backups(addon=None):
    """Vrátí list dictů: {slot, path, mtime, label}."""
    a = _addon(addon)
    out = []
    for slot in range(LOCAL_SLOTS):
        path = _slot_path(a, slot)
        if not _is_valid_settings_file(path):
            continue
        try:
            mtime = os.path.getmtime(path)
            stamp = time.strftime("%d.%m.%Y %H:%M", time.localtime(mtime))
        except Exception:
            mtime = 0
            stamp = "?"
        out.append({
            "slot": slot,
            "path": path,
            "mtime": mtime,
            "label": f"Lokální bak{slot} — {stamp}",
        })
    return out


def restore_local(slot: int, addon=None) -> bool:
    a = _addon(addon)
    src = _slot_path(a, int(slot))
    if not _is_valid_settings_file(src):
        return False
    dst = _settings_path(a)
    return _vfs_copy(src, dst)


def backup_now(addon=None, silent: bool = False, force: bool = False) -> dict:
    """
    Lokální rotace + FTP per-device (pokud CloudSync enabled + username).
    silent=True: bez notifikací (service).
    force=False: debounce 2 s.
    """
    global _last_backup_ts
    now = time.time()
    if not force and (now - _last_backup_ts) < _BACKUP_DEBOUNCE_S:
        return {"ok": False, "skipped": "debounce"}

    a = _addon(addon)
    did = device_id(a)
    local_ok = rotate_and_store_local(a)
    cloud_ok = False
    cloud_msg = ""

    try:
        from resources.lib.utils.cloud_sync import CloudSync

        cs = CloudSync(a)
        if cs.enabled and cs.username:
            _remember_cloud_user(cs.username, a)
            local_bak = _slot_path(a, 0)
            if local_ok and _is_valid_settings_file(local_bak):
                cloud_ok = cs.upload_settings_backup(local_bak, did)
                cloud_msg = "FTP OK" if cloud_ok else "FTP selhalo"
            else:
                cloud_msg = "FTP přeskočeno (lokální záloha neplatná)"
        else:
            cloud_msg = "CloudSync vypnutý / chybí uživatel"
    except Exception as e:
        cloud_msg = f"FTP chyba: {e}"
        log.error(f"[SettingsBackup] cloud: {e}")

    if local_ok:
        _last_backup_ts = now

    if not silent:
        name = a.getAddonInfo("name")
        if local_ok:
            extra = f" · {cloud_msg}" if cloud_msg else ""
            xbmcgui.Dialog().notification(
                name,
                f"Nastavení zálohováno ({did}){extra}",
                xbmcgui.NOTIFICATION_INFO,
                3500,
            )
        else:
            xbmcgui.Dialog().notification(
                name,
                "Záloha nastavení selhala (neplatný settings.xml)",
                xbmcgui.NOTIFICATION_ERROR,
                3500,
            )

    return {"ok": local_ok, "cloud_ok": cloud_ok, "device_id": did, "cloud_msg": cloud_msg}


def _ask_cloud_username(addon=None) -> str:
    pre = remembered_cloud_user(addon)
    name = xbmcgui.Dialog().input(
        "CloudSync uživatel (složka na FTP)",
        default=pre,
        type=xbmcgui.INPUT_ALPHANUM,
    )
    if name:
        _remember_cloud_user(name, addon)
    return (name or "").strip()


def restore_menu(addon=None):
    """Interaktivní dialog obnovy (lokální + cloud)."""
    a = _addon(addon)
    name = a.getAddonInfo("name")
    did = device_id(a)

    choices = []
    actions = []

    for item in list_local_backups(a):
        choices.append(item["label"])
        actions.append(("local", item["slot"]))

    choices.append(f"Cloud — toto zařízení ({did})")
    actions.append(("cloud_self", did))

    choices.append("Cloud — jiné zařízení…")
    actions.append(("cloud_other", None))

    if not choices:
        xbmcgui.Dialog().ok(name, "Žádná záloha není k dispozici.")
        return

    idx = xbmcgui.Dialog().select("Obnovit nastavení z…", choices)
    if idx < 0:
        return

    kind, payload = actions[idx]
    src_label = choices[idx]

    if kind == "local":
        if not xbmcgui.Dialog().yesno(
            name,
            f"Obnovit z {src_label}?\n\nPo obnově restartujte Kodi.",
        ):
            return
        ok = restore_local(payload, a)
    elif kind == "cloud_self":
        username = remembered_cloud_user(a) or _ask_cloud_username(a)
        if not username:
            return
        if not xbmcgui.Dialog().yesno(
            name,
            f"Obnovit cloudovou zálohu tohoto zařízení ({did})?\n\nPo obnově restartujte Kodi.",
        ):
            return
        ok = restore_cloud(did, username=username, addon=a)
    else:
        username = remembered_cloud_user(a) or _ask_cloud_username(a)
        if not username:
            return
        try:
            from resources.lib.utils.cloud_sync import CloudSync

            cs = CloudSync(a)
            remote_list = cs.list_settings_backups(username=username) or []
        except Exception as e:
            xbmcgui.Dialog().ok(name, f"Nelze načíst seznam z FTP:\n{e}")
            return
        if not remote_list:
            xbmcgui.Dialog().ok(name, "Na FTP nejsou žádné zálohy nastavení.")
            return
        labels = [f"{x['device_id']}" for x in remote_list]
        pick = xbmcgui.Dialog().select("Vyberte zařízení (cesty nemusí sedět)", labels)
        if pick < 0:
            return
        other_id = remote_list[pick]["device_id"]
        if not xbmcgui.Dialog().yesno(
            name,
            f"Obnovit nastavení ze zařízení „{other_id}“?\n"
            f"Pozor: cesty (dfolder, ffprobe…) mohou být neplatné na tomto zařízení.\n\n"
            f"Po obnově restartujte Kodi.",
        ):
            return
        ok = restore_cloud(other_id, username=username, addon=a)

    if ok:
        xbmcgui.Dialog().ok(
            name,
            "Nastavení obnoveno.\n\nRestartujte Kodi, aby se změny načetly.",
        )
    else:
        xbmcgui.Dialog().notification(
            name,
            "Obnova nastavení selhala",
            xbmcgui.NOTIFICATION_ERROR,
            3500,
        )


def restore_cloud(device_id_str: str, username: str = None, addon=None) -> bool:
    a = _addon(addon)
    from resources.lib.utils.cloud_sync import CloudSync

    cs = CloudSync(a)
    user = (username or remembered_cloud_user(a) or "").strip()
    if not user:
        return False
    _remember_cloud_user(user, a)

    tmp = os.path.join(_backup_dir(a), f"_restore_{_sanitize_id(device_id_str)}.xml")
    _ensure_dir(_backup_dir(a))
    ok = cs.download_settings_backup(device_id_str, username=user, local_path=tmp)
    if not ok or not _is_valid_settings_file(tmp):
        return False
    return _vfs_copy(tmp, _settings_path(a))
