#!/usr/bin/env python3
"""
=============================================================
 Telegram Userbot Broadcaster Script (Telethon - MTProto)
 learn-lexicore.bytronex.com
=============================================================
"""

import os
import sys
import json
import random
import asyncio
import argparse
import requests
import re
from datetime import datetime
from typing import Optional, Tuple

# استيراد مكتبة Telethon
try:
    from telethon import TelegramClient
    from telethon.errors import SessionPasswordNeededError
except ImportError:
    print("[ERROR] Telethon library not found. Run: python3 -m pip install telethon requests --user")
    sys.exit(1)

# ── مسارات الملفات ──────────────────────────────────────
SCRIPT_DIR  = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE  = os.path.join(SCRIPT_DIR, "social_config.json")
PROGRESS_FILE = os.path.join(SCRIPT_DIR, "tg_userbot_progress.json")
LOG_FILE     = os.path.join(SCRIPT_DIR, "tg_userbot.log")
STATE_FILE    = os.path.join(SCRIPT_DIR, "tg_state.json")
SESSION_FILE = os.path.join(SCRIPT_DIR, "lexicore_userbot.session")

KEYWORDS_FILE = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "wp_auto_poster", "keywords.txt"))
OUTPUT_IMAGES_DIR = os.path.abspath(os.path.join(SCRIPT_DIR, "..", "output-images"))


def log(msg: str, level: str = "INFO"):
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"[{timestamp}] [{level}] {msg}"
    print(line)
    with open(LOG_FILE, "a", encoding="utf-8") as f:
        f.write(line + "\n")


def load_json(path: str) -> dict:
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def save_json(path: str, data: dict):
    with open(path, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)


def pick_next_keyword(keywords: list, progress: dict) -> Tuple[str, int]:
    last_index = progress.get("last_index", -1)
    next_index = (last_index + 1) % len(keywords)
    return keywords[next_index], next_index


def pick_random_image(keyword: str) -> Optional[str]:
    """يختار صورة متطابقة للتيليجرام من المجلد المحلي بناءً على الكلمة المفتاحية أو يختار عشوائياً"""
    if not os.path.exists(OUTPUT_IMAGES_DIR):
        log(f"Images folder not found: {OUTPUT_IMAGES_DIR}", "ERROR")
        return None
        
    keyword_lower = keyword.lower()
    book_match = re.search(r'book\s+(\d+)', keyword_lower)
    form_match = re.search(r'form\s+(\d+)', keyword_lower)
    quiz_match = re.search(r'quiz\s+([a-c])', keyword_lower)
    
    target_type = None
    target_num = None
    target_letter = None
    
    if quiz_match and book_match:
        target_type = "quiz"
        target_num = book_match.group(1)
        target_letter = quiz_match.group(1)
    elif form_match:
        target_type = "form"
        target_num = form_match.group(1)
    elif book_match:
        target_type = "book"
        target_num = book_match.group(1)
        
    categories = [d for d in os.listdir(OUTPUT_IMAGES_DIR) if os.path.isdir(os.path.join(OUTPUT_IMAGES_DIR, d))]
    
    if target_type == "quiz" and target_num and target_letter:
        cat_path = os.path.join(OUTPUT_IMAGES_DIR, "quizzes")
        if os.path.exists(cat_path):
            images = os.listdir(cat_path)
            for img in images:
                if f"book {target_num} quiz {target_letter}" in img.lower():
                    return os.path.join(cat_path, img)
    elif target_type == "form" and target_num:
        cat_path = os.path.join(OUTPUT_IMAGES_DIR, "alcpt")
        if os.path.exists(cat_path):
            images = os.listdir(cat_path)
            for img in images:
                if re.search(rf'form\s+{target_num}\b', img.lower()):
                    return os.path.join(cat_path, img)
    elif target_type == "book" and target_num:
        cat_path = os.path.join(OUTPUT_IMAGES_DIR, "books")
        if os.path.exists(cat_path):
            images = os.listdir(cat_path)
            for img in images:
                if re.search(rf'book\s+{target_num}\b', img.lower()):
                    return os.path.join(cat_path, img)
                    
    if not categories:
        return None
        
    # Fallback Priority: lexicore_app -> alcpt -> any
    if "lexicore_app" in categories:
        cat = "lexicore_app"
    elif "alcpt" in categories:
        cat = "alcpt"
    else:
        cat = random.choice(categories)
        
    cat_path = os.path.join(OUTPUT_IMAGES_DIR, cat)
    images = [f for f in os.listdir(cat_path) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
    
    if not images:
        # Final fallback to any folder with images
        for fallback_cat in categories:
            fallback_path = os.path.join(OUTPUT_IMAGES_DIR, fallback_cat)
            images = [f for f in os.listdir(fallback_path) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
            if images:
                cat_path = fallback_path
                break
        if not images:
            return None
        if not images:
            return None

    return os.path.join(cat_path, random.choice(images))


def generate_telegram_caption(cfg: dict, keyword: str) -> str:
    """يولد نص احترافي للبث في تيليجرام."""
    api_key = cfg["groq"]["api_key"]
    model   = cfg["groq"].get("model", "llama-3.3-70b-versatile")

    prompt = f"""You are an enthusiastic English teacher for military personnel (ALCPT/ALC).
Write a VERY SHORT, highly engaging broadcast message for a Telegram Group regarding: "{keyword}"

REQUIREMENTS:
- Language: Bilingual (English first, followed by Arabic translation). Both languages are mandatory.
- Tone: Exciting, helpful, encouraging
- Length: Max 2 short sentences per language.
- Include 1-2 emojis
- Include 3 hashtags (e.g., #ALCPT #LexiCore)
- End with this EXACT bilingual text for the CTA (preserve the emojis):

⬇️ The Best App to Learn & Pass ALCPT Exams ⬇️
⬇️ أقوى تطبيق لتعلم وتجاوز اختبارات ALCPT ⬇️
🍎 iOS (الأيفون): https://apps.apple.com/us/app/alcpt-cat-practice-lexicore/id6761208858
📱 Android (تحميل أندرويد): https://play.google.com/store/apps/details?id=com.bytronex.lexicore

OUTPUT:
Return ONLY the raw bilingual caption text. Do not use markdown wrappers.
"""

    url = "https://api.groq.com/openai/v1/chat/completions"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    payload = {
        "model": model,
        "messages": [
            {"role": "system", "content": "You output direct plain text only without meta-commentary."},
            {"role": "user", "content": prompt}
        ],
        "temperature": 0.7,
        "max_completion_tokens": 500
    }

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    resp.raise_for_status()

    raw = resp.json()
    return raw["choices"][0]["message"]["content"].strip()


async def setup_login(api_id, api_hash):
    """سكربت تفاعلي لتسجيل دخول الحساب لأول مرة."""
    print("\n--- Telegram Userbot Authentication ---")
    print("This will create a session file so the script can post on your behalf.")
    
    client = TelegramClient(os.path.join(SCRIPT_DIR, 'lexicore_userbot'), api_id, api_hash)
    await client.connect()
    
    if not await client.is_user_authorized():
        phone = input("Enter your Telegram Phone Number (with country code, e.g. +1234567890): ")
        await client.send_code_request(phone)
        try:
            code = input("Enter the code you received on Telegram: ")
            await client.sign_in(phone, code)
        except SessionPasswordNeededError:
            password = input("Two-Step Verification is enabled. Enter your password: ")
            await client.sign_in(password=password)
            
    me = await client.get_me()
    print(f"\n[SUCCESS] Logged in successfully as: {me.first_name} (@{me.username})")
    print("Session file 'lexicore_userbot.session' has been created.")
    print("You can now run the script normally via CRON.")
    await client.disconnect()


async def broadcast_via_userbot(api_id, api_hash, chat_ids, image_path, caption):
    """نرسل الصورة والنص إلى المحادثات كحساب شخصي."""
    client = TelegramClient(os.path.join(SCRIPT_DIR, 'lexicore_userbot'), api_id, api_hash)
    
    # Check if session exists without prompting
    await client.connect()
    if not await client.is_user_authorized():
        log("Userbot session is not authorized. Please run the script with --login first.", "ERROR")
        return 0
        
    success_count = 0
    
    # ── التأخير العشوائي عند البداية لمحاكاة السلوك البشري ──
    startup_delay = random.uniform(60, 900) # من دقيقة إلى 15 دقيقة
    log(f"Human Mimicry: Waiting {startup_delay/60:.1f} minutes before first post...")
    await asyncio.sleep(startup_delay)

    for chat in chat_ids:
        if not chat or "YOUR_" in chat or "GROUP_" in chat:
            continue
            
        try:
            # Cast to int if it's a numeric ID representing a chat/channel
            target = int(chat) if str(chat).lstrip('-').isdigit() else chat
            
            log(f"Sending message to {target}...")
            await client.send_file(
                entity=target,
                file=image_path,
                caption=caption
            )
            success_count += 1
            log(f"✅ Userbot sent message to {target}")
            
            # Anti-Spam Delay: Sleep 60-120 seconds between posts to mimic human behavior
            delay = random.uniform(60.0, 120.0)
            log(f"Waiting {delay:.1f}s before next group...")
            await asyncio.sleep(delay)
            
        except Exception as e:
            log(f"❌ Userbot failed to send to {chat}: {str(e)}", "ERROR")
            
    await client.disconnect()
    return success_count


def send_admin_notification(cfg: dict, message: str):
    """إرسال إشعارات عبر اليوزربوت (باسم @haarods)."""
    tg_u = cfg.get("telegram_userbot", {})
    if not tg_u.get("enabled"): return

    api_id = tg_u.get("api_id")
    api_hash = tg_u.get("api_hash")
    # نرسل الإشعار للحساب نفسه للتأكد ونكتفي به في اليوزربوت لتجنب التكرار في القناة العامة
    # targets = ["@alcpt_practice", "@haarods"]
    targets = ["@haarods"]
    
    if not api_id or not api_hash: return

    async def _send():
        # نستخدم نفس مسار الجلسة الموجود في المجلد الحالي
        session_path = os.path.join(SCRIPT_DIR, 'lexicore_userbot')
        client = TelegramClient(session_path, api_id, api_hash)
        await client.connect()
        if not await client.is_user_authorized():
            return
        for target in targets:
            try:
                await client.send_message(target, message, parse_mode='html')
            except:
                pass
        await client.disconnect()

    try:
        # بما أننا داخل loop أصلاً في main، يمكننا إضافته للمهام
        asyncio.ensure_future(_send())
    except:
        pass


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("--login", action="store_true", help="Interactive login to Telegram")
    parser.add_argument("--dry-run", action="store_true", help="Generate content without posting")
    args = parser.parse_args()

    if not os.path.exists(CONFIG_FILE):
        log("social_config.json not found", "ERROR")
        sys.exit(1)

    cfg = load_json(CONFIG_FILE)
    tg_cfg = cfg.get("telegram_userbot", {})
    
    if not tg_cfg.get("enabled"):
        log("telegram_userbot is disabled in config.", "WARNING")
        sys.exit(0)
        
    try:
        api_id = int(tg_cfg.get("api_id", 0))
    except ValueError:
        api_id = 0
        
    api_hash = tg_cfg.get("api_hash", "")
    
    if api_id == 0 or not api_hash or "YOUR_" in api_hash:
        print("[ERROR] You must set 'api_id' (Number) and 'api_hash' (String) in social_config.json")
        print("Get them from: https://my.telegram.org/auth")
        sys.exit(1)

    # 1. عملية تسجيل الدخول لأول مرة
    if args.login:
        loop = asyncio.get_event_loop()
        loop.run_until_complete(setup_login(api_id, api_hash))
        sys.exit(0)

    # 2. نظام الجدولة الذكي (للنشر 4 مرات عشوائية في اليوم)
    now = datetime.now()
    today_str = now.strftime("%Y-%m-%d")
    
    # تحميل الحالة الحالية
    state = load_json(STATE_FILE) if os.path.exists(STATE_FILE) else {
        "last_date": "",
        "daily_count": 0,
        "next_run_time": 0
    }
    
    # تصفير العداد إذا بدأ يوم جديد
    if state["last_date"] != today_str:
        state["last_date"] = today_str
        state["daily_count"] = 0
        state["next_run_time"] = 0
        save_json(STATE_FILE, state)

    # التحقق من الحد الأقصى للنشر (4 مرات)
    if state["daily_count"] >= 4:
        # log("Daily limit (4 posts) reached. Skipping.")
        return

    # التحقق مما إذا كان الوقت قد حان للنشر
    if now.timestamp() < state["next_run_time"]:
        # log(f"Next scheduled run is at {datetime.fromtimestamp(state['next_run_time'])}. Skipping.")
        return

    # 3. عملية النشر العادية (Cron Job)
    broadcast_groups = tg_cfg.get("broadcast_groups", [])
    valid_groups = [g for g in broadcast_groups if g and "YOUR_" not in g and "GROUP_" not in g]

    if not valid_groups and not args.dry_run:
        log("No valid broadcast_groups found in config. Add them first.", "ERROR")
        sys.exit(1)

    if not os.path.exists(KEYWORDS_FILE):
        log("keywords.txt not found", "ERROR")
        sys.exit(1)

    with open(KEYWORDS_FILE, "r", encoding="utf-8") as f:
        keywords = [l.strip() for l in f if l.strip()]

    progress = load_json(PROGRESS_FILE) if os.path.exists(PROGRESS_FILE) else {"last_index": -1, "broadcasts": []}
    
    keyword, index = pick_next_keyword(keywords, progress)
    log(f"Smart Broadcast [{state['daily_count']+1}/4] keyword: '{keyword}'")

    local_img_path = pick_random_image(keyword)
    if not local_img_path:
        log("No image could be assigned. Exiting.", "ERROR")
        sys.exit(1)
        
    log(f"Local Image for Userbot: {local_img_path}")

    try:
        caption = generate_telegram_caption(cfg, keyword)
    except Exception as e:
        log(f"Groq API Error: {e}", "ERROR")
        sys.exit(1)

    if args.dry_run:
        log("[DRY RUN] Would broadcast via Userbot to:")
        print(f"\n--- LOCAL IMAGE ---\n{local_img_path}\n")
        print(f"--- CAPTION ---\n{caption}\n")
        print(f"--- TARGETS ---\n{valid_groups}\n")
        return

    # Call Telethon to broadcast
    loop = asyncio.get_event_loop()
    success_count = loop.run_until_complete(broadcast_via_userbot(api_id, api_hash, valid_groups, local_img_path, caption))
    
    if success_count > 0:
        # تحديث الحالة للمرة القادمة
        state["daily_count"] += 1
        # جدولة الموعد القادم: بعد 4.5 إلى 7.5 ساعات
        interval = random.uniform(4.5 * 3600, 7.5 * 3600)
        state["next_run_time"] = datetime.now().timestamp() + interval
        save_json(STATE_FILE, state)

        progress["last_index"] = index
        progress["broadcasts"].append({
            "date": datetime.now().isoformat(),
            "keyword": keyword,
            "image": local_img_path,
            "success_count": success_count,
            "targets": valid_groups
        })
        save_json(PROGRESS_FILE, progress)
        log(f"Userbot sync complete! Reached {success_count} chats. Next run at {datetime.fromtimestamp(state['next_run_time'])}")
        
        # Admin Notification
        notif_msg = f"🚀 <b>تم النشر بنجاح (اليوزربوت)!</b>\n\n" \
                    f"📝 <b>الكلمة:</b> {keyword}\n" \
                    f"✅ <b>الترتيب اليومي:</b> {state['daily_count']}/4\n" \
                    f"⏰ <b>الموعد القادم:</b> {datetime.fromtimestamp(state['next_run_time']).strftime('%I:%M %p')}"
        send_admin_notification(cfg, notif_msg)
    else:
        log("Userbot Broadcast failed. No chats were reached.", "WARNING")

if __name__ == "__main__":
    main()
