# -*- coding: utf-8 -*-
import hashlib
import json
import os
import sqlite3
import threading
import time

import xbmc
import xbmcaddon
import xbmcvfs

ADDON_ID = "plugin.video.xstream-pro"
_MEM = {}
_ADDON = None
_DB_PATH = None
_SCHEMA_READY = False
_SCHEMA_LOCK = threading.Lock()


def _addon():
    global _ADDON
    if _ADDON is None:
        _ADDON = xbmcaddon.Addon(ADDON_ID)
    return _ADDON


def _db_path():
    global _DB_PATH
    if _DB_PATH is None:
        profile = xbmcvfs.translatePath(_addon().getAddonInfo("profile"))
        folder = os.path.join(profile, "cache")
        os.makedirs(folder, exist_ok=True)
        _DB_PATH = os.path.join(folder, "xstream_cache.db")
    return _DB_PATH


def _init_schema(db):
    db.execute("PRAGMA journal_mode=WAL")
    db.execute(
        "CREATE TABLE IF NOT EXISTS kv_cache ("
        "namespace TEXT NOT NULL, "
        "cache_key TEXT NOT NULL, "
        "payload TEXT NOT NULL, "
        "expires INTEGER NOT NULL, "
        "created INTEGER NOT NULL, "
        "PRIMARY KEY(namespace, cache_key))"
    )
    db.execute(
        "CREATE TABLE IF NOT EXISTS meta_cache ("
        "media_type TEXT NOT NULL, "
        "tmdb_id TEXT NOT NULL, "
        "payload TEXT NOT NULL, "
        "expires INTEGER NOT NULL, "
        "PRIMARY KEY(media_type, tmdb_id))"
    )
    db.commit()


def _connect():
    global _SCHEMA_READY
    db = sqlite3.connect(_db_path(), timeout=20)
    db.execute("PRAGMA synchronous=NORMAL")
    if not _SCHEMA_READY:
        with _SCHEMA_LOCK:
            if not _SCHEMA_READY:
                _init_schema(db)
                _SCHEMA_READY = True
    return db


def make_key(*parts):
    raw = json.dumps(parts, sort_keys=True, separators=(",", ":"))
    return hashlib.md5(raw.encode("utf-8")).hexdigest()


def get(namespace, cache_key):
    now = int(time.time())
    mem_key = (namespace, cache_key)
    item = _MEM.get(mem_key)
    if item and item[0] > now:
        return item[1]
    try:
        db = _connect()
        row = db.execute(
            "SELECT payload, expires FROM kv_cache WHERE namespace=? AND cache_key=?",
            (namespace, cache_key),
        ).fetchone()
        db.close()
        if not row:
            return None
        payload, expires = row
        if int(expires) <= now:
            delete(namespace, cache_key)
            return None
        data = json.loads(payload)
        _MEM[mem_key] = (int(expires), data)
        return data
    except Exception:
        return None


def set(namespace, cache_key, payload, ttl_seconds):
    now = int(time.time())
    expires = now + int(ttl_seconds)
    _MEM[(namespace, cache_key)] = (expires, payload)
    try:
        db = _connect()
        db.execute(
            "INSERT OR REPLACE INTO kv_cache VALUES (?, ?, ?, ?, ?)",
            (namespace, cache_key, json.dumps(payload, ensure_ascii=False), expires, now),
        )
        db.commit()
        db.close()
    except Exception as exc:
        xbmc.log("[Xstream Pro Cache] write failed: {0}".format(exc), xbmc.LOGWARNING)


def delete(namespace, cache_key):
    _MEM.pop((namespace, cache_key), None)
    try:
        db = _connect()
        db.execute("DELETE FROM kv_cache WHERE namespace=? AND cache_key=?", (namespace, cache_key))
        db.commit()
        db.close()
    except Exception:
        pass


def clear_namespace(namespace):
    for key in list(_MEM):
        if key[0] == namespace:
            _MEM.pop(key, None)
    try:
        db = _connect()
        db.execute("DELETE FROM kv_cache WHERE namespace=?", (namespace,))
        db.commit()
        db.close()
    except Exception:
        pass


def meta_get(media_type, tmdb_id):
    now = int(time.time())
    mem_key = ("meta", media_type, str(tmdb_id))
    item = _MEM.get(mem_key)
    if item and item[0] > now:
        return item[1]
    try:
        db = _connect()
        row = db.execute(
            "SELECT payload, expires FROM meta_cache WHERE media_type=? AND tmdb_id=?",
            (media_type, str(tmdb_id)),
        ).fetchone()
        db.close()
        if not row:
            return None
        payload, expires = row
        if int(expires) <= now:
            meta_delete(media_type, tmdb_id)
            return None
        data = json.loads(payload)
        _MEM[mem_key] = (int(expires), data)
        return data
    except Exception:
        return None


def meta_get_many(pairs):
    now = int(time.time())
    results = {}
    missing = []
    for media_type, tmdb_id in pairs or []:
        media_type = str(media_type or "")
        tmdb_id = str(tmdb_id or "")
        if not media_type or not tmdb_id:
            continue
        key = (media_type, tmdb_id)
        if key in results:
            continue
        mem_key = ("meta", media_type, tmdb_id)
        item = _MEM.get(mem_key)
        if item and item[0] > now:
            results[key] = item[1]
        else:
            missing.append(key)
    if not missing:
        return results
    try:
        db = _connect()
        by_type = {}
        for media_type, tmdb_id in missing:
            by_type.setdefault(media_type, []).append(tmdb_id)
        for media_type, ids in by_type.items():
            placeholders = ",".join("?" for _ in ids)
            rows = db.execute(
                "SELECT media_type, tmdb_id, payload, expires FROM meta_cache "
                "WHERE media_type=? AND tmdb_id IN ({0})".format(placeholders),
                [media_type] + ids,
            ).fetchall()
            for row_media_type, row_tmdb_id, payload, expires in rows:
                if int(expires) <= now:
                    continue
                data = json.loads(payload)
                key = (row_media_type, row_tmdb_id)
                results[key] = data
                _MEM[("meta", row_media_type, row_tmdb_id)] = (int(expires), data)
        db.close()
    except Exception:
        return results
    return results


def meta_set(media_type, tmdb_id, payload, ttl_seconds=604800):
    expires = int(time.time()) + int(ttl_seconds)
    _MEM[("meta", media_type, str(tmdb_id))] = (expires, payload)
    try:
        db = _connect()
        db.execute(
            "INSERT OR REPLACE INTO meta_cache VALUES (?, ?, ?, ?)",
            (media_type, str(tmdb_id), json.dumps(payload, ensure_ascii=False), expires),
        )
        db.commit()
        db.close()
    except Exception as exc:
        xbmc.log("[Xstream Pro Cache] meta write failed: {0}".format(exc), xbmc.LOGWARNING)


def meta_delete(media_type, tmdb_id):
    _MEM.pop(("meta", media_type, str(tmdb_id)), None)
    try:
        db = _connect()
        db.execute("DELETE FROM meta_cache WHERE media_type=? AND tmdb_id=?", (media_type, str(tmdb_id)))
        db.commit()
        db.close()
    except Exception:
        pass


def clean_expired():
    now = int(time.time())
    try:
        db = _connect()
        db.execute("DELETE FROM kv_cache WHERE expires <= ?", (now,))
        db.execute("DELETE FROM meta_cache WHERE expires <= ?", (now,))
        db.commit()
        db.close()
    except Exception:
        pass
