# -*- coding: utf-8 -*-
"""Sdílené utility pro offline_cache a dfolder – bez závislosti na SeriesManager."""

import json
import re

import xbmcaddon
import xbmcvfs

from resources.lib.database.metadata_db import db as _metadata_db
from resources.lib.utils.log_utils import get_logger
from resources.lib.utils.ui_utils import clean_filename

log = get_logger()

ADDON_ID = 'plugin.video.mmirousek_v3'

_RE_STORAGE_TMDB = re.compile(r"^tmdb_\d+$", re.IGNORECASE)
_SEASON_DIR_RE = re.compile(
    r'(?i)(?:'
    r'(?:Season|Sezona|Sezóna)\s*(\d+)'
    r'|S(\d{1,2})'
    r')'
)
_VIDEO_EXTS = ('.mp4', '.mkv', '.avi', '.ts')


def parse_season_dir_name(dirname: str):
    """Vrátí číslo sezóny z názvu složky (Sezona 4, Season 4, S04, …)."""
    m = _SEASON_DIR_RE.search(dirname or '')
    if not m:
        return None
    raw = m.group(1) or m.group(2)
    try:
        return int(raw)
    except (TypeError, ValueError):
        return None


def _series_root_path(dfolder: str, dirname: str) -> str:
    path = f"{dfolder.rstrip('/')}/{dirname}/"
    return path.replace('\\', '/')


def _looks_like_series_folder(dfolder: str, dirname: str) -> bool:
    """True pokud složka vypadá jako seriál (sezóny nebo SxxExx soubory)."""
    path = _series_root_path(dfolder, dirname)
    if not xbmcvfs.exists(path):
        return False
    try:
        subdirs, files = xbmcvfs.listdir(path)
    except Exception:
        return False
    for sd in subdirs:
        if parse_season_dir_name(sd) is not None:
            return True
    for fname in files:
        low = fname.lower()
        if low.endswith(_VIDEO_EXTS) and re.search(r'(?i)S\d+E\d+', fname):
            return True
    return False


def list_series_root_dirs(dfolder: str = None) -> list:
    """Vrátí názvy podsložek v dfolder, které vypadají jako seriály."""
    dfolder = dfolder or resolve_dfolder_path()
    if not dfolder or not xbmcvfs.exists(dfolder):
        return []
    try:
        dirs, _files = xbmcvfs.listdir(dfolder)
    except Exception:
        return []
    return [d for d in dirs if _looks_like_series_folder(dfolder, d)]


def discover_existing_disk_folder_name(
    series_name: str, series_data: dict = None, dfolder: str = None
) -> str:
    """Najde existující složku seriálu na disku (surový název z listdir)."""
    dfolder = dfolder or resolve_dfolder_path()
    if not dfolder:
        return ''

    storage_key = clean_filename(series_name)
    folder_index = build_disk_folder_to_storage_key_index()
    expected_human = ''
    if isinstance(series_data, dict):
        expected_human = clean_filename(
            resolve_folder_display_name(series_name, series_data)
        )

    ranked = []
    for dirname in list_series_root_dirs(dfolder):
        cd = clean_filename(dirname)
        if expected_human and cd == expected_human and not _RE_STORAGE_TMDB.match(dirname):
            ranked.append((0, dirname))
        elif folder_index.get(cd) == storage_key and not _RE_STORAGE_TMDB.match(dirname):
            ranked.append((1, dirname))
        elif cd == storage_key and _RE_STORAGE_TMDB.match(dirname):
            ranked.append((2, dirname))
        elif folder_index.get(cd) == storage_key:
            ranked.append((3, dirname))

    if not ranked:
        return ''
    ranked.sort(key=lambda x: x[0])
    return ranked[0][1]


def resolve_series_folder_label(
    series_name: str,
    series_data: dict = None,
    dfolder: str = None,
    hint_display_name: str = None,
) -> str:
    """Název složky seriálu pro nové stahování – preferuje existující disk."""
    existing = discover_existing_disk_folder_name(series_name, series_data, dfolder)
    if existing:
        return clean_filename(existing)

    for candidate in (
        hint_display_name,
        resolve_folder_display_name(series_name, series_data or {}) if series_data else '',
        series_name,
    ):
        label = (candidate or '').strip()
        if label and not _RE_STORAGE_TMDB.match(label):
            return clean_filename(label)
    return clean_filename(series_name)


def merge_offline_scan_items(existing: list, new_items: list) -> list:
    """Sloučí položky offline scanu podle (season, episode)."""
    merged = {}
    for it in (existing or []) + (new_items or []):
        try:
            sig = (int(it.get('season', -1)), int(it.get('episode', -1)))
        except (TypeError, ValueError):
            continue
        merged[sig] = it
    return list(merged.values())


def resolve_folder_display_name(series_name: str, data: dict) -> str:
    """
    Název složky na disku: preferuj lidský titul z DB/TMDb/search query,
    storage key `tmdb_<id>` používej jen jako poslední fallback.
    """
    if not isinstance(data, dict):
        data = {}

    def _usable_title(value) -> str:
        value = (value or "").strip()
        return value if value and not _RE_STORAGE_TMDB.match(value) else ""

    title = _usable_title(data.get("name"))
    if title:
        return title

    tmdb = data.get("tmdb") or {}
    if isinstance(tmdb, dict):
        for key in ("title", "name", "original_title", "original_name"):
            title = _usable_title(tmdb.get(key))
            if title:
                return title

    title = _usable_title(data.get("search_query_name"))
    if title:
        return title

    aliases = data.get("aliases") or []
    if isinstance(aliases, list):
        for alias in aliases:
            title = _usable_title(alias)
            if title:
                return title

    fallback = (series_name or "").strip()
    if _RE_STORAGE_TMDB.match(fallback):
        log.debug(f"[offline] Používám storage key jako fallback názvu složky: {fallback}")
    return fallback


def sync_active_dfolder_setting(addon=None):
    """Zkopíruje aktivní slot (dfolder_1/2/3) do nastavení dfolder."""
    try:
        addon = addon or xbmcaddon.Addon(ADDON_ID)
        raw_slot = 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_path = addon.getSetting(f'dfolder_{slot_idx + 1}') or ''
        if slot_path and slot_path != (addon.getSetting('dfolder') or ''):
            addon.setSetting('dfolder', slot_path)
    except Exception as e:
        log.debug(f"[offline] sync_active_dfolder_setting: {e}")


def resolve_dfolder_path(addon=None):
    """Vrátí přeloženou VFS cestu aktivní offline složky (s trailing /)."""
    addon = addon or xbmcaddon.Addon(ADDON_ID)
    sync_active_dfolder_setting(addon)
    raw = (addon.getSetting('dfolder') or '').strip()
    if not raw:
        return ''
    try:
        real = xbmcvfs.translatePath(raw) or raw
    except Exception:
        real = raw
    if not real:
        return ''
    if not xbmcvfs.exists(real) and raw != real and xbmcvfs.exists(raw):
        real = raw
    real = real.replace('\\', '/')
    if not real.endswith('/'):
        real += '/'
    return real


def offline_cache_lookup_keys(series_name: str, series_data: dict = None) -> list:
    """Všechny možné klíče offline_cache pro seriál (storage key + název složky na disku)."""
    keys = []

    def _add(value):
        key = clean_filename(value) if value else ''
        if key and key not in keys:
            keys.append(key)

    _add(series_name)
    if isinstance(series_data, dict):
        _add(resolve_folder_display_name(series_name, series_data))
        _add(series_data.get('name'))
    try:
        disk_name = discover_existing_disk_folder_name(series_name, series_data)
        if disk_name:
            _add(disk_name)
    except Exception:
        pass
    return keys


def build_disk_folder_to_storage_key_index() -> dict:
    """Mapa clean_filename(název_složky_na_disku) -> clean_filename(storage_key)."""
    index = {}
    if not _metadata_db.conn:
        return index
    try:
        cur = _metadata_db.conn.execute("SELECT series_name, json_data FROM series_data")
        for storage_key, json_blob in cur.fetchall():
            try:
                data = json.loads(json_blob or '{}')
            except Exception:
                data = {}
            sk = clean_filename(storage_key)
            if sk:
                index[sk] = sk
            for candidate in (
                resolve_folder_display_name(storage_key, data),
                (data.get('name') or ''),
            ):
                folder_key = clean_filename(candidate)
                if folder_key:
                    index[folder_key] = sk or folder_key
    except Exception as e:
        log.warning(f"[offline] Nelze sestavit index složek seriálů: {e}")
    return index


def resolve_offline_cache_key_for_disk_folder(folder_name: str, folder_index: dict = None) -> str:
    """Určí primární klíč offline_cache pro složku nalezenou na disku."""
    folder_key = clean_filename(folder_name)
    if not folder_key:
        return ''
    if _RE_STORAGE_TMDB.match(folder_name):
        return folder_key
    if folder_index is None:
        folder_index = build_disk_folder_to_storage_key_index()
    return folder_index.get(folder_key, folder_key)


def get_merged_offline_cache(series_name: str, series_data: dict = None) -> list:
    """Sloučí offline_cache záznamy ze všech aliasů seriálu (bez duplicit S/E)."""
    merged = []
    seen = set()
    for key in offline_cache_lookup_keys(series_name, series_data):
        for item in _metadata_db.get_offline_cache_for_series(key) or []:
            try:
                sig = (int(item.get('season', -1)), int(item.get('episode', -1)))
            except (TypeError, ValueError):
                continue
            if sig in seen:
                continue
            seen.add(sig)
            merged.append(item)
    return merged
