# -*- coding: utf-8 -*-
import sys, urllib.parse, os, json, time, re, threading
import xbmc, xbmcgui, xbmcplugin, xbmcaddon, xbmcvfs, requests

# ═══════════════════════════════════════════════
# 🌟 الإعدادات والثوابت الأساسية
# ═══════════════════════════════════════════════
ADDON        = xbmcaddon.Addon()
ADDON_NAME   = ADDON.getAddonInfo('name')
SERVER_URL   = "http://beta-dexiptv.duckdns.org"
BASE_URL     = sys.argv[0]
ADDON_HANDLE = int(sys.argv[1])
FANART_IMAGE = os.path.join(ADDON.getAddonInfo('path'), 'fanart.jpg')
ICON_IMAGE   = os.path.join(ADDON.getAddonInfo('path'), 'icon.png')
PROFILE_DIR  = xbmcvfs.translatePath(ADDON.getAddonInfo('profile'))
HISTORY_FILE = os.path.join(PROFILE_DIR, 'history.json')
FAV_FILE     = os.path.join(PROFILE_DIR, 'favorites.json')

API_KEY      = ADDON.getSetting('api_key').strip()
AUTO_PLAY    = ADDON.getSetting('auto_play') == 'true'
IS_EN        = ADDON.getSetting('language') == 'en'

try: NOTIFY_TIME = int(ADDON.getSetting('notify_time'))
except: NOTIFY_TIME = 240

USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
_session = requests.Session()
_session.headers.update({'User-Agent': USER_AGENT})

_manifest_cache = {'data': None, 'ts': 0}

HIDDEN_CAT_NAMES = ['جديد المسلسلات', 'KDrama Abukarim', 'وثائقيات', 'Korean Movies']
NON_IPTV_IDS = {
    'dex_tmdb_movie_genres', 'dex_tmdb_series_genres',
    'dex_ai_recommend_movie', 'dex_ai_recommend_series',
    'dex_time_mood_movie', 'dex_time_mood_series',
    'dex_weekend_special', 'dex_universes_native',
    'dex_search_movie', 'dex_search_series', 'dex_search_channel'
}

# ═══════════════════════════════════════════════
# 🌟 قاموس النصوص
# ═══════════════════════════════════════════════
T = {
    'no_api':          ("الرجاء ادخال API CODE في إعدادات الإضافة",  "Please enter your API CODE in addon settings"),
    'favorites':       ("⭐ المفضلة",                                  "⭐ Favorites"),
    'history':         ("⏱️ متابعة المشاهدة",                         "⏱️ Continue Watching"),
    'search_vod':      ("🔍 بحث شامل (أفلام ومسلسلات)",               "🔍 Global Search (Movies & Series)"),
    'search_ai':       ("🤖 المارد الذكي (بحث AI)",                   "🤖 AI Genie (Smart Search)"),
    'section_empty':   ("القسم فارغ حاليا",                           "This section is currently empty"),
    'add_fav':         ("⭐ إضافة إلى المفضلة",                       "⭐ Add to Favorites"),
    'fav_added':       ("تمت الإضافة للمفضلة",                        "Added to favorites"),
    'fav_exists':      ("موجود بالفعل",                               "Already in favorites"),
    'fav_removed':     ("تمت الإزالة",                                "Removed"),
    'fav_empty':       ("المفضلة فارغة",                              "Favorites are empty"),
    'history_empty':   ("سجل المشاهدة فارغ",                          "Watch history is empty"),
    'remove_history':  ("❌ إزالة من السجل",                          "❌ Remove from history"),
    'clear_history':   ("🗑️ مسح كل سجل المشاهدة",                    "🗑️ Clear All Watch History"),
    'removed_history': ("تمت الإزالة من السجل",                       "Removed from history"),
    'no_link':         ("رابط غير متوفر",                             "Stream not available"),
    'not_available':   ("عذراً، العمل غير متوفر حالياً في سيرفرك",   "Sorry, this content is not available on your server"),
    'no_results':      ("لم يتم العثور على نتائج",                    "No results found"),
    'sort_current':    ("🔄 تغيير الترتيب (الحالي: {})",              "🔄 Change Sort (Current: {})"),
    'sort_default':    ("الافتراضي",                                   "Default"),
    'next_page':       ("⬇️ انتقال إلى صفحة {} ⬇️",                  "⬇️ Go to Page {} ⬇️"),
    'choose_sort':     ("اختر طريقة الترتيب",                         "Choose Sort Order"),
    'choose_quality':  ("اختر الجودة المطلوبة",                       "Select Video Quality"),
    'sort_options':    (["الافتراضي","جديد","احدث الحلقات","الأقدم","أ-ي","توب"],
                        ["Default","Latest","Latest Episode","Oldest","A-Z","Top"]),
    'search_kb':       ("اكتب كلمة البحث هنا...",                     "Type your search here..."),
    'preparing':       ("جاري تحضير الإضافة...",                      "Preparing Search Engine..."),
    'translating':     ("جاري ترجمة الكلمات بذكاء...",                "Translating keywords smartly..."),
    'searching_in':    ("نبحث عن: {} في قسم {}",                      "Searching for: {} in {}"),
    'auto_next':       ("⏭️ التشغيل التلقائي",                         "⏭️ Auto Play"),
    'auto_next_msg':   ("سيتم تشغيل {} تلقائياً",                     "Next up: {} will play automatically"),
    'ai_searching':    ("جاري البحث في السيرفر...",                   "Searching in server..."),
    'ep_label':        ("حلقة",                                        "Episode"),
}

def _(key, *args):
    pair = T.get(key, (key, key))
    text = pair[1] if IS_EN else pair[0]
    if args: text = text.format(*args)
    return text

# ═══════════════════════════════════════════════
# 🌟 الدوال المساعدة
# ═══════════════════════════════════════════════
def clean_name(text):
    if not text: return ""
    text = re.sub(r'[\U00010000-\U0010ffff]', '', str(text), flags=re.UNICODE)
    return text.strip()

def _is_non_iptv(cat_id):
    if cat_id in NON_IPTV_IDS: return True
    if cat_id.startswith('dex_dyn_'): return True
    return False

def get_int_id(string_id):
    try: return int(''.join(filter(str.isdigit, str(string_id))))
    except: return 1

def build_url(action, **kwargs):
    clean_kwargs = {k: v for k, v in kwargs.items() if v not in (None, '', 'None')}
    return "{0}?action={1}&{2}".format(BASE_URL, action, urllib.parse.urlencode(clean_kwargs))

def api(): return "{0}/{1}".format(SERVER_URL, API_KEY)
def dexcats_api(): return "{0}/dexcats/{1}".format(SERVER_URL, API_KEY)

def notify(msg, is_error=False):
    icon = xbmcgui.NOTIFICATION_ERROR if is_error else xbmcgui.NOTIFICATION_INFO
    xbmcgui.Dialog().notification(ADDON_NAME, clean_name(msg), icon, 3000)

def get_manifest():
    global _manifest_cache
    now = time.time()
    if _manifest_cache['data'] and (now - _manifest_cache['ts']) < 5:
        return _manifest_cache['data']
    try: 
        data = _session.get("{0}/manifest.json".format(api()), timeout=10).json()
        _manifest_cache = {'data': data, 'ts': now}
        return data
    except Exception: return _manifest_cache.get('data') or {}

def get_resume_info(item_id):
    try:
        if os.path.exists(HISTORY_FILE):
            with open(HISTORY_FILE, 'r', encoding='utf-8') as f:
                for h in json.load(f):
                    if str(h.get('id')) == str(item_id):
                        return float(h.get('resume_time', 0)), float(h.get('total_time', 0))
    except: pass
    return 0.0, 0.0

def force_upnext_time():
    if xbmc.getCondVisibility('System.HasAddon(service.upnext)'):
        try:
            upnext_addon = xbmcaddon.Addon('service.upnext')
            upnext_addon.setSetting('notify_time', str(NOTIFY_TIME))
            upnext_addon.setSetting('notification_time', str(NOTIFY_TIME))
            upnext_addon.setSetting('ip_time', str(NOTIFY_TIME))
        except: pass

# ═══════════════════════════════════════════════
# 🌟 القائمة الرئيسية المباشرة
# ═══════════════════════════════════════════════
def main_menu():
    if not API_KEY:
        notify(_('no_api'), is_error=True)
        ADDON.openSettings()
        return
        
    manifest = get_manifest()
    catalogs = manifest.get('catalogs', [])
    
    if not catalogs:
        notify(_('section_empty'), is_error=True)
        xbmcplugin.endOfDirectory(ADDON_HANDLE, cacheToDisc=False)
        return

    add_dir_item(_('history'),     build_url('history'))
    add_dir_item(_('favorites'),   build_url('favorites'))

    for cat in catalogs:
        cat_id   = str(cat.get('id', ''))
        cat_type = str(cat.get('type', '')).lower()
        cat_name = str(cat.get('name', ''))

        if 'search' in cat_id.lower() and _is_non_iptv(cat_id): continue
        if any(h.lower() in cat_name.lower() for h in HIDDEN_CAT_NAMES): continue

        has_genres = any(e.get('name') == 'genre' for e in cat.get('extra', []))
        action = 'genres' if has_genres else 'catalog'
        add_dir_item(cat_name, build_url(action, type=cat_type, id=cat_id))

    add_dir_item(_('search_vod'),  build_url('search', mode='vod'))
    add_dir_item(_('search_ai'),   build_url('search', mode='ai_all'))

    xbmcplugin.endOfDirectory(ADDON_HANDLE, cacheToDisc=False)

def add_dir_item(name, url, is_folder=True, icon=None, fanart=None):
    li = xbmcgui.ListItem(clean_name(name))
    li.setArt({'icon': icon or ICON_IMAGE, 'thumb': icon or ICON_IMAGE, 'poster': icon or ICON_IMAGE, 'fanart': fanart or FANART_IMAGE})
    xbmcplugin.addDirectoryItem(ADDON_HANDLE, url, li, isFolder=is_folder)

def show_genres(cat_type, cat_id):
    manifest = get_manifest()
    for cat in manifest.get('catalogs', []):
        if str(cat.get('id')) == str(cat_id):
            for ext in cat.get('extra', []):
                if ext.get('name') == 'genre':
                    for genre in ext.get('options', []):
                        add_dir_item(genre, build_url('catalog', type=cat_type, id=cat_id, genre=genre))
                    break
            break
    xbmcplugin.endOfDirectory(ADDON_HANDLE, cacheToDisc=False)

def show_catalog(cat_type, cat_id, genre=None, skip=0, sort=None):
    try:
        url = "{0}/catalog/{1}/{2}".format(api(), urllib.parse.quote(cat_type), urllib.parse.quote(cat_id))
        extras = []
        if genre and genre != 'None': extras.append("genre={0}".format(urllib.parse.quote(genre)))
        if sort and sort != 'None':   extras.append("sort={0}".format(urllib.parse.quote(sort)))
        if int(skip) > 0:             extras.append("skip={0}".format(skip))
        if extras: url += "/" + "/".join(extras)
        url += ".json"

        metas = _session.get(url, timeout=15).json().get('metas', [])

        if int(skip) == 0 and cat_type in ['movie', 'series']:
            sort_label = sort if sort and sort != 'None' else _('sort_default')
            li_sort = xbmcgui.ListItem(_('sort_current', sort_label))
            li_sort.setArt({'icon': 'DefaultFolder.png', 'thumb': 'DefaultFolder.png'})
            xbmcplugin.addDirectoryItem(ADDON_HANDLE, build_url('choose_sort', type=cat_type, id=cat_id, genre=genre), li_sort, isFolder=False)

        for meta in metas: add_meta_item(meta, cat_type)

        if len(metas) >= 100:
            page = (int(skip) // 100) + 2
            li_next = xbmcgui.ListItem(_('next_page', page))
            li_next.setArt({'icon': 'DefaultFolder.png', 'thumb': 'DefaultFolder.png'})
            xbmcplugin.addDirectoryItem(ADDON_HANDLE, build_url('catalog', type=cat_type, id=cat_id, genre=genre, skip=int(skip) + len(metas), sort=sort), li_next, isFolder=True)
    except Exception: pass

    xbmcplugin.setContent(ADDON_HANDLE, 'images' if cat_type in ['tv', 'channel', 'live'] else ('tvshows' if cat_type == 'series' else 'movies'))
    xbmcplugin.endOfDirectory(ADDON_HANDLE, cacheToDisc=False)
    xbmc.executebuiltin('Container.SetViewMode(500)')

def choose_sort(cat_type, cat_id, genre):
    options = _('sort_options')
    ret = xbmcgui.Dialog().select(_('choose_sort'), options)
    if ret >= 0:
        selected_sort = "" if ret == 0 else options[ret]
        url = build_url('catalog', type=cat_type, id=cat_id, genre=genre, skip=0, sort=selected_sort)
        xbmc.executebuiltin("Container.Update({0})".format(url))

def add_meta_item(meta, default_type, context='catalog'):
    name      = clean_name(meta.get('name', '..'))
    item_id   = str(meta.get('id', ''))
    item_type = str(meta.get('type', default_type))
    poster    = meta.get('poster') or ICON_IMAGE
    bg        = meta.get('background') or poster or FANART_IMAGE
    logo      = meta.get('logo') or ''

    year = meta.get('year') or meta.get('releaseInfo', '')
    rating = meta.get('imdbRating') or meta.get('rating', '')
    label = f"{name}  [{year}]  ⭐ {rating}" if year or rating else name

    li = xbmcgui.ListItem(label)
    li.setArt({'thumb': poster, 'poster': poster, 'fanart': bg, 'clearlogo': logo})
    li.setInfo('video', {'title': name, 'plot': meta.get('description', ''), 'year': int(year) if str(year).isdigit() else 0, 'rating': float(rating) if rating else 0.0, 'mediatype': 'movie' if item_type == 'movie' else 'tvshow' if item_type == 'series' else 'video'})

    if item_type != 'series':
        r_time, t_time = get_resume_info(item_id)
        if r_time > 10:
            li.setProperty('ResumeTime', str(r_time))
            if t_time > 0: li.setProperty('TotalTime', str(t_time))

    fav_cmd = build_url('manage_fav', action_type='add', id=item_id, type=item_type, name=name, poster=poster, bg=bg)
    li.addContextMenuItems([(_('add_fav'), 'RunPlugin({})'.format(fav_cmd))])

    # 🌟 التوجيه الذكي إلى TMDB Helper للذكاء الاصطناعي والتورنت
    if context == 'ai_search' or item_id.startswith('tmdb:') or item_id.startswith('tt'):
        tm_type  = 'tv' if item_type == 'series' else 'movie'
        clean_id = item_id.split(':')[-1]
        
        if clean_id.isdigit() or clean_id.startswith('tt'):
            # توجيه للتشغيل المباشر لفتح خيارات التورنت
            id_param = 'imdb_id' if clean_id.startswith('tt') else 'tmdb_id'
            url = "plugin://plugin.video.themoviedb.helper/?info=play&type={0}&{1}={2}".format(tm_type, id_param, clean_id)
            li.setProperty('IsPlayable', 'true') 
            xbmcplugin.addDirectoryItem(ADDON_HANDLE, url, li, isFolder=False)
        else:
            # إذا رجع المارد اسم الفيلم نصياً، نفتح بحث TMDB (يجب أن يكون isFolder=True)
            url = "plugin://plugin.video.themoviedb.helper/?info=search&type={0}&query={1}".format(tm_type, urllib.parse.quote(clean_id))
            li.setProperty('IsPlayable', 'false') 
            xbmcplugin.addDirectoryItem(ADDON_HANDLE, url, li, isFolder=True)
        return

    action = 'meta' if item_type == 'series' else 'play'
    is_folder = (item_type == 'series')
    if not is_folder: li.setProperty('IsPlayable', 'true')
    
    xbmcplugin.addDirectoryItem(ADDON_HANDLE, build_url(action, type=item_type, id=item_id, name=name, poster=poster, bg=bg), li, isFolder=is_folder)

def show_meta(item_type, item_id, name='', poster='', bg=''):
    try:
        url  = "{0}/meta/{1}/{2}.json".format(api(), urllib.parse.quote(item_type), urllib.parse.quote(item_id))
        meta = _session.get(url, timeout=15).json().get('meta', {})
        s_name  = clean_name(meta.get('name') or name or ('Series' if IS_EN else 'مسلسل'))
        s_poster = meta.get('poster') or poster or ICON_IMAGE
        s_bg     = meta.get('background') or bg or s_poster or FANART_IMAGE
        s_logo   = meta.get('logo') or ''

        for vid in meta.get('videos', []):
            ep_id    = str(vid.get('id', ''))
            ep_title = clean_name(vid.get('title') or "{0} {1}".format(_('ep_label'), vid.get('episode', '')))
            full_n   = "{0} - {1}".format(s_name, ep_title)
            ep_thumb = vid.get('thumbnail') or s_bg or s_poster

            li = xbmcgui.ListItem(ep_title)
            li.setArt({'thumb': ep_thumb, 'landscape': ep_thumb, 'poster': s_poster, 'fanart': s_bg, 'clearlogo': s_logo})
            li.setInfo('video', {'title': ep_title, 'season': vid.get('season'), 'episode': vid.get('episode'), 'mediatype': 'episode'})

            r_time, t_time = get_resume_info(ep_id)
            if r_time > 10:
                li.setProperty('ResumeTime', str(r_time))
                if t_time > 0: li.setProperty('TotalTime', str(t_time))

            li.setProperty('IsPlayable', 'true')
            xbmcplugin.addDirectoryItem(ADDON_HANDLE, build_url('play', type=item_type, id=ep_id, name=full_n, poster=s_poster, bg=s_bg, series_id=item_id, season=vid.get('season'), episode=vid.get('episode'), s_name=s_name), li, isFolder=False)
    except Exception: pass
    xbmcplugin.setContent(ADDON_HANDLE, 'episodes')
    xbmcplugin.endOfDirectory(ADDON_HANDLE, cacheToDisc=False)

# ═══════════════════════════════════════════════
# السجل والمفضلة
# ═══════════════════════════════════════════════
def update_history(item_id, item_type, name, poster, bg, resume_time=0, total_time=0, series_id='', season=0, episode=0, s_name=''):
    if not os.path.exists(PROFILE_DIR): os.makedirs(PROFILE_DIR)
    try:
        with open(HISTORY_FILE, 'r', encoding='utf-8') as f: history = json.load(f)
    except Exception: history = []
    history = [h for h in history if str(h.get('id')) != str(item_id)]
    history.insert(0, {'id': item_id, 'type': item_type, 'name': clean_name(name), 'poster': poster, 'bg': bg, 'resume_time': resume_time, 'total_time': total_time, 'series_id': series_id, 'season': season, 'episode': episode, 's_name': s_name})
    with open(HISTORY_FILE, 'w', encoding='utf-8') as f: json.dump(history[:40], f, ensure_ascii=False)

def remove_history_silent(item_id):
    try:
        with open(HISTORY_FILE, 'r', encoding='utf-8') as f: history = json.load(f)
        history = [h for h in history if str(h.get('id')) != str(item_id)]
        with open(HISTORY_FILE, 'w', encoding='utf-8') as f: json.dump(history, f, ensure_ascii=False)
    except Exception: pass

def remove_history(item_id):
    remove_history_silent(item_id)
    xbmc.executebuiltin('Container.Refresh')
    notify(_('removed_history'))

def show_history():
    try:
        with open(HISTORY_FILE, 'r', encoding='utf-8') as f: history = json.load(f)
    except Exception: history = []
    
    if not history: notify(_('history_empty'))
    else: add_dir_item(_('clear_history'), build_url('clear_history'), is_folder=False)

    for item in history:
        li = xbmcgui.ListItem(item.get('name'))
        li.setArt({'thumb': item.get('poster'), 'poster': item.get('poster'), 'fanart': item.get('bg')})
        media_type = 'episode' if item.get('type') == 'series' else 'movie'
        li.setInfo('video', {'title': item.get('name'), 'mediatype': media_type})

        r_time, t_time = float(item.get('resume_time', 0)), float(item.get('total_time', 0))
        if r_time > 10:
            li.setProperty('ResumeTime', str(r_time))
            if t_time > 0: li.setProperty('TotalTime', str(t_time))

        li.setProperty('IsPlayable', 'true')
        li.addContextMenuItems([(_('remove_history'), 'RunPlugin({})'.format(build_url('remove_history', id=item.get('id'))))])

        if str(item.get('id')).startswith('tmdb_'):
            url = build_url('resolve_tmdb', type=item.get('type'), id=item.get('series_id'), season=item.get('season'), episode=item.get('episode'), title=item.get('s_name'))
        else:
            url = build_url('play', type=item.get('type'), id=item.get('id'), name=item.get('name'), poster=item.get('poster'), bg=item.get('bg'), series_id=item.get('series_id', ''), season=item.get('season', 0), episode=item.get('episode', 0), s_name=item.get('s_name', ''))

        xbmcplugin.addDirectoryItem(ADDON_HANDLE, url, li, isFolder=False)

    xbmcplugin.setContent(ADDON_HANDLE, 'movies')
    xbmcplugin.endOfDirectory(ADDON_HANDLE, cacheToDisc=False)

def manage_fav(action_type, item_id, item_type='', name='', poster='', bg=''):
    if not os.path.exists(PROFILE_DIR): os.makedirs(PROFILE_DIR)
    try:
        with open(FAV_FILE, 'r', encoding='utf-8') as f: favs = json.load(f)
    except Exception: favs = []
    
    if action_type == 'add':
        if not any(f.get('id') == item_id for f in favs):
            favs.insert(0, {'id': item_id, 'type': item_type, 'name': clean_name(name), 'poster': poster, 'bg': bg})
            notify(_('fav_added'))
        else: notify(_('fav_exists'))
    else:
        favs = [f for f in favs if f.get('id') != item_id]
        notify(_('fav_removed'))
        xbmc.executebuiltin('Container.Refresh')
        
    with open(FAV_FILE, 'w', encoding='utf-8') as f: json.dump(favs, f, ensure_ascii=False)

def show_favorites():
    try:
        with open(FAV_FILE, 'r', encoding='utf-8') as f: favs = json.load(f)
    except Exception: favs = []
    if not favs: notify(_('fav_empty'))
    
    for item in favs:
        li = xbmcgui.ListItem(item.get('name'))
        li.setArt({'thumb': item.get('poster'), 'poster': item.get('poster'), 'fanart': item.get('bg')})
        li.setInfo('video', {'title': item.get('name')})
        li.addContextMenuItems([(_('fav_removed'), 'RunPlugin({})'.format(build_url('manage_fav', action_type='remove', id=item.get('id'))))])
        action = 'meta' if item.get('type') == 'series' else 'play'
        if action == 'play': li.setProperty('IsPlayable', 'true')
        xbmcplugin.addDirectoryItem(ADDON_HANDLE, build_url(action, type=item.get('type'), id=item.get('id'), name=item.get('name'), poster=item.get('poster'), bg=item.get('bg')), li, item.get('type') == 'series')
    xbmcplugin.endOfDirectory(ADDON_HANDLE, cacheToDisc=False)

# ═══════════════════════════════════════════════
# محرك التشغيل
# ═══════════════════════════════════════════════
def _monitor_playback(player, monitor, item_id, item_type, name, poster, bg, series_id, season, episode, s_name, next_info=None):
    last_time = total_time = 0
    notified = False
    has_upnext = xbmc.getCondVisibility('System.HasAddon(service.upnext)')

    while not monitor.abortRequested():
        try:
            if not player.isPlaying(): break
            last_time  = player.getTime()
            total_time = player.getTotalTime()
            if not has_upnext and next_info and AUTO_PLAY and total_time > 0 and not notified:
                if 0 < (total_time - last_time) <= NOTIFY_TIME:
                    notified = True
                    xbmcgui.Dialog().notification(_('auto_next'), _('auto_next_msg', next_info['name']), ICON_IMAGE, 5000, False)
        except: break
        monitor.waitForAbort(2)

    if total_time > 0:
        if (total_time - last_time) <= (NOTIFY_TIME + 10):
            remove_history_silent(item_id)
            if next_info: update_history(next_info['id'], item_type, next_info['name'], poster, bg, 0, 0, series_id, next_info['s'], next_info['e'], s_name)
        else:
            update_history(item_id, item_type, name, poster, bg, last_time, total_time, series_id, season, episode, s_name)

def play_stream(item_type, item_id, name='', poster='', bg='', series_id='', season=0, episode=0, s_name=''):
    try:
        force_upnext_time()
        url_api = "{0}/stream/{1}/{2}.json".format(api(), urllib.parse.quote(item_type), urllib.parse.quote(item_id))
        streams = _session.get(url_api, timeout=15).json().get('streams', [])
        if not streams: return notify(_('no_link'), True)

        if len(streams) > 1:
            options = [f"{s.get('name', '').replace('\\n', ' ')} - {s.get('title', '')}" for s in streams]
            ret = xbmcgui.Dialog().select(_('choose_quality'), options)
            if ret < 0: return xbmcplugin.setResolvedUrl(ADDON_HANDLE, False, xbmcgui.ListItem())
            stream_url = streams[ret]['url']
        else:
            stream_url = streams[0]['url']

        base_url = stream_url.split('|')[0].lower()
        if '|' not in stream_url: stream_url += "|User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36"

        li = xbmcgui.ListItem(path=stream_url)
        if '.m3u8' in base_url:
            li.setProperty('inputstream', 'inputstream.adaptive')
            li.setProperty('inputstream.adaptive.manifest_type', 'hls')
        elif '.ts' in base_url: li.setMimeType('video/mp2t')
        else: li.setMimeType('video/mp4')

        li.setProperty('IsPlayable', 'true')
        xbmcplugin.setResolvedUrl(ADDON_HANDLE, True, li)

        if item_type in ['movie', 'series']:
            monitor, player  = xbmc.Monitor(), xbmc.Player()
            next_info = None
            if item_type == 'series' and series_id:
                try:
                    vids = _session.get("{0}/meta/series/{1}.json".format(api(), urllib.parse.quote(series_id)), timeout=10).json().get('meta', {}).get('videos', [])
                    c_s, c_e = int(season), int(episode)
                    n_obj = next((v for v in vids if int(v.get('season', 0)) == c_s and int(v.get('episode', 0)) == c_e + 1), None)
                    if not n_obj: n_obj = next((v for v in vids if int(v.get('season', 0)) == c_s + 1 and int(v.get('episode', 0)) == 1), None)
                    if n_obj:
                        ep_t = clean_name(n_obj.get('title') or "{0} {1}".format(_('ep_label'), n_obj.get('episode', '')))
                        next_info = {'id': str(n_obj.get('id', '')), 'name': "{0} - {1}".format(s_name or name.split(' - ')[0], ep_t), 's': n_obj.get('season'), 'e': n_obj.get('episode')}
                except Exception: pass

            for _ in range(20):
                if player.isPlaying(): break
                xbmc.sleep(500)

            if player.isPlaying() and next_info and AUTO_PLAY:
                next_url = build_url('play', type='series', id=next_info['id'], name=next_info['name'], poster=poster, bg=bg, series_id=series_id, season=next_info['s'], episode=next_info['e'], s_name=s_name)
                playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
                next_li  = xbmcgui.ListItem(next_info['name'])
                next_li.setProperty('IsPlayable', 'true')
                playlist.add(next_url, next_li)

            _monitor_playback(player, monitor, item_id, item_type, name, poster, bg, series_id, season, episode, s_name, next_info)
    except Exception:
        xbmcplugin.setResolvedUrl(ADDON_HANDLE, False, xbmcgui.ListItem())

def resolve_tmdb(item_type, tmdb_id, season=1, episode=1, title=''):
    notify(_('ai_searching'))
    try:
        force_upnext_time()
        payload = { "apiKey": API_KEY, "tmdbId": tmdb_id, "inputTitle": urllib.parse.unquote(title), "type": item_type, "season": season, "episode": episode }
        res = _session.post("{0}/api/resolve_widget_stream".format(SERVER_URL), json=payload, timeout=15).json()

        if res and res.get('success') and res.get('data'):
            streams = res.get('data')
            if len(streams) > 0:
                if len(streams) > 1:
                    options = [f"{s.get('name', '').replace('\\n', ' ')} - {s.get('title', '')}" for s in streams]
                    ret = xbmcgui.Dialog().select(_('choose_quality'), options)
                    if ret < 0: return xbmcplugin.setResolvedUrl(ADDON_HANDLE, False, xbmcgui.ListItem())
                    stream_url = streams[ret]['url']
                else:
                    stream_url = streams[0]['url']
                    
                if '|' not in stream_url: stream_url += "|User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36"

                li = xbmcgui.ListItem(path=stream_url)
                base_url_check = stream_url.split('|')[0].lower()
                if '.m3u8' in base_url_check:
                    li.setProperty('inputstream', 'inputstream.adaptive')
                    li.setProperty('inputstream.adaptive.manifest_type', 'hls')
                elif '.ts' in base_url_check: li.setMimeType('video/mp2t')
                else: li.setMimeType('video/mp4')
                
                li.setProperty('IsPlayable', 'true')
                xbmcplugin.setResolvedUrl(ADDON_HANDLE, True, li)

                h_id   = "tmdb_{0}".format(tmdb_id) if item_type == 'movie' else "tmdb_{0}_S{1}E{2}".format(tmdb_id, season, episode)
                h_name = urllib.parse.unquote(title)
                if item_type == 'series': h_name += " - S{0}E{1}".format(season, episode)

                monitor, player = xbmc.Monitor(), xbmc.Player()
                has_upnext = xbmc.getCondVisibility('System.HasAddon(service.upnext)')
                next_info = None

                if item_type == 'series':
                    next_ep = int(episode) + 1
                    next_title_d = "{0} - S{1}E{2}".format(urllib.parse.unquote(title), season, next_ep)
                    next_info = {'id': "tmdb_{0}_S{1}E{2}".format(tmdb_id, season, next_ep), 'name': next_title_d, 's': int(season), 'e': next_ep}

                for _ in range(20):
                    if player.isPlaying(): break
                    xbmc.sleep(500)
                    
                if player.isPlaying() and next_info and AUTO_PLAY:
                    next_url = build_url('resolve_tmdb', type='series', id=tmdb_id, season=next_info['s'], episode=next_info['e'], title=title)
                    playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
                    next_li  = xbmcgui.ListItem(next_info['name'])
                    next_li.setProperty('IsPlayable', 'true')
                    playlist.add(next_url, next_li)

                _monitor_playback(player, monitor, h_id, item_type, h_name, ICON_IMAGE, FANART_IMAGE, tmdb_id, int(season), int(episode), title, next_info)
                return
    except Exception: pass
    notify(_('not_available'), True)
    xbmcplugin.setResolvedUrl(ADDON_HANDLE, False, xbmcgui.ListItem())

# ═══════════════════════════════════════════════
# الترجمة الذكية للبحث (TMDB + Google Translate)
# ═══════════════════════════════════════════════
def get_smart_arabic_titles(query):
    titles = []
    if not re.search(r'[a-zA-Z]', query): return titles
    try:
        tmdb_key = "8d6d91941230817f7807d643736e8a49"
        url = "https://api.themoviedb.org/3/search/multi?api_key={0}&language=ar-SA&query={1}".format(tmdb_key, urllib.parse.quote(query))
        res = _session.get(url, timeout=5).json()
        if res.get('results'):
            for item in res['results'][:5]:
                name = item.get('title') or item.get('name')
                if name and re.search(r'[\u0600-\u06FF]', name) and name not in titles:
                    titles.append(name)
    except: pass
    try:
        url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=ar&dt=t&q={0}".format(urllib.parse.quote(query))
        res = _session.get(url, timeout=5).json()
        translated = res[0][0][0]
        if translated and re.search(r'[\u0600-\u06FF]', translated) and translated not in titles:
            titles.append(translated)
    except: pass
    return titles

def get_english_translation(query):
    """🌟 ترجمة عكسية للحصول على الأسماء الإنجليزية الرسمية لنتائج دقيقة في TMDB Helper"""
    if not re.search(r'[\u0600-\u06FF]', query): return query
    try:
        url = "https://translate.googleapis.com/translate_a/single?client=gtx&sl=ar&tl=en&dt=t&q={0}".format(urllib.parse.quote(query))
        res = _session.get(url, timeout=5).json()
        return res[0][0][0]
    except: return query

# ═══════════════════════════════════════════════
# محرك البحث المدمج والذكي (Background Threading)
# ═══════════════════════════════════════════════
def fetch_data_bg(url, timeout, result_container):
    try:
        res = _session.get(url, timeout=timeout).json()
        result_container['data'] = res
    except Exception as e:
        result_container['error'] = str(e)

def global_search(mode, query=None):
    if not query:
        try:
            params = dict(urllib.parse.parse_qsl(sys.argv[2][1:]))
            query = params.get('search') or params.get('keyword') or params.get('query')
        except: pass

    if not query or query.strip() == "":
        kb = xbmc.Keyboard('', _('search_kb'))
        kb.doModal()
        if kb.isConfirmed(): query = kb.getText().strip()
        else: return

    if not query: return

    pDialog = xbmcgui.DialogProgress()
    pDialog.create(ADDON_NAME, _('preparing'))

    queries_to_run = []
    
    # 🌟 توجيه ذكي للمسارات مع ترجمة حسب النوع
    if mode.startswith('ai'):
        # للمارد الذكي: نرسل الترجمة الإنجليزية لضمان الحصول على أسماء إنجليزية من TMDB
        pDialog.update(5, 'جاري تحويل السؤال للصيغة الإنجليزية...')
        eng_query = get_english_translation(query)
        queries_to_run.append(eng_query)
        
        if mode == 'ai_all':
            targets, base_fn, ctx = [('movie', 'dex_ai_recommend_movie'), ('series', 'dex_ai_recommend_series')], dexcats_api, 'ai_search'
        elif mode == 'ai_movie':
            targets, base_fn, ctx = [('movie', 'dex_ai_recommend_movie')], dexcats_api, 'ai_search'
        else:
            targets, base_fn, ctx = [('series', 'dex_ai_recommend_series')], dexcats_api, 'ai_search'
    else:
        # للبحث العادي: نستخدم الترجمة الذكية المتعددة
        queries_to_run.append(query)
        if not IS_EN:
            pDialog.update(5, _('translating'))
            smart_titles = get_smart_arabic_titles(query)
            for st in smart_titles:
                if st not in queries_to_run: queries_to_run.append(st)
                
        if mode == 'vod':
            targets, base_fn, ctx = [('movie', 'dex_search_movie'), ('series', 'dex_search_series')], api, 'catalog'
        elif mode == 'channel':
            targets, base_fn, ctx = [('channel', 'dex_search_channel')], api, 'catalog'
        else: 
            targets, base_fn, ctx = [('movie', 'dex_search_movie'), ('series', 'dex_search_series'), ('channel', 'dex_search_channel')], api, 'catalog'

    found = False
    seen_ids = set()
    total_steps = len(targets) * len(queries_to_run)
    current_step = 0

    for q in queries_to_run:
        for t, cid in targets:
            if pDialog.iscanceled(): break

            current_step += 1
            progress = int((current_step / total_steps) * 100)
            pDialog.update(progress, _('searching_in', q, t))

            safe_query = urllib.parse.quote(q)
            url = "{0}/catalog/{1}/{2}/search={3}.json".format(base_fn(), urllib.parse.quote(t), urllib.parse.quote(cid), safe_query)

            result_container = {}
            fetch_thread = threading.Thread(target=fetch_data_bg, args=(url, 25, result_container))
            fetch_thread.start()

            while fetch_thread.is_alive():
                xbmc.sleep(100)
                if pDialog.iscanceled():
                    pDialog.close()
                    return

            metas = result_container.get('data', {}).get('metas', [])
            for m in metas:
                item_id = str(m.get('id', ''))
                if item_id not in seen_ids:
                    add_meta_item(m, t, context=ctx)
                    seen_ids.add(item_id)
                    found = True

    pDialog.close()
    if not found: notify(_('no_results'), is_error=True)

    xbmcplugin.setContent(ADDON_HANDLE, 'movies')
    xbmcplugin.endOfDirectory(ADDON_HANDLE, cacheToDisc=False)

# ═══════════════════════════════════════════════
# Router
# ═══════════════════════════════════════════════
def router(paramstring):
    p = dict(urllib.parse.parse_qsl(paramstring))
    a, t, i = p.get('action', ''), p.get('type', 'movie'), p.get('id', '')

    if   a == 'catalogs':     show_catalogs(t)
    elif a == 'genres':       show_genres(t, i)
    elif a == 'catalog':      show_catalog(t, i, p.get('genre'), p.get('skip', 0), p.get('sort'))
    elif a == 'choose_sort':  choose_sort(t, i, p.get('genre'))
    elif a == 'meta':         show_meta(t, i, p.get('name', ''), p.get('poster', ''), p.get('bg', ''))
    elif a == 'play':         play_stream(t, i, p.get('name', ''), p.get('poster', ''), p.get('bg', ''), p.get('series_id', ''), p.get('season', 0), p.get('episode', 0), p.get('s_name', ''))
    elif a == 'resolve_tmdb': resolve_tmdb(t, i, p.get('season', 1), p.get('episode', 1), p.get('title', ''))
    elif a == 'search':       global_search(p.get('mode'), p.get('query'))
    elif a == 'history':      show_history()
    elif a == 'remove_history': remove_history(i)
    elif a == 'clear_history': 
        if os.path.exists(HISTORY_FILE): os.remove(HISTORY_FILE)
        xbmc.executebuiltin('Container.Refresh')
    elif a == 'favorites':    show_favorites()
    elif a == 'manage_fav':   manage_fav(p.get('action_type'), i, t, p.get('name', ''), p.get('poster', ''), p.get('bg', ''))
    else: main_menu()

if __name__ == '__main__':
    router(sys.argv[2][1:])
