import os
import asyncio
import aiohttp
import pymysql
import hashlib
from datetime import datetime
from aiogram import Bot, Dispatcher, types, F
from aiogram.filters import CommandStart, Command
from aiogram.types import FSInputFile, InlineKeyboardMarkup, InlineKeyboardButton
import shutil
import json
import re

# ========== SOZLAMALAR ==========
BOT_TOKEN = "8330989431:AAGyw91D_Ezb9hOEvWJtruWRuYISw-SI3XA"
DOWNLOAD_PATH = "/var/www/html/YUKLABOT/downloads"
SETTINGS_PATH = "/var/www/html/YUKLABOT/settings.json"

# RapidAPI
RAPIDAPI_KEY = "8cb4b71d34msh250b077876ffd2fp1d1b45jsn4058fd865425"
RAPIDAPI_HOST = "social-media-video-downloader.p.rapidapi.com"

# MySQL
DB_CONFIG = {
    'host': 'localhost',
    'user': 'yuklabot',
    'password': 'sanjarbek2208S@!',
    'database': 'yuklabot',
    'charset': 'utf8mb4',
    'cursorclass': pymysql.cursors.DictCursor
}

ADMIN_IDS = [6404701185]

# ========== RAPIDAPI DOWNLOADER ==========
class RapidAPIDownloader:
    def __init__(self):
        self.headers = {
            "x-rapidapi-key": RAPIDAPI_KEY,
            "x-rapidapi-host": RAPIDAPI_HOST
        }
        self.base_url = "https://social-media-video-downloader.p.rapidapi.com"
    
    def get_file_path(self, user_id: int, ext: str = "mp4") -> str:
        user_folder = os.path.join(DOWNLOAD_PATH, str(user_id))
        os.makedirs(user_folder, exist_ok=True)
        filename = hashlib.md5(str(datetime.now().timestamp()).encode()).hexdigest()[:12]
        return os.path.join(user_folder, f"{filename}.{ext}")
    
    def extract_video_id(self, url: str, platform: str) -> str:
        """URL dan video ID olish"""
        if platform == "youtube":
            # youtube.com/watch?v=ID yoki youtu.be/ID
            match = re.search(r'(?:v=|youtu\.be/|shorts/)([a-zA-Z0-9_-]{11})', url)
            return match.group(1) if match else None
        elif platform == "instagram":
            # instagram.com/p/ID/ yoki /reel/ID/
            match = re.search(r'/(?:p|reel|reels)/([A-Za-z0-9_-]+)', url)
            return match.group(1) if match else None
        elif platform == "tiktok":
            # tiktok.com/@user/video/ID
            match = re.search(r'/video/(\d+)', url)
            if match:
                return match.group(1)
            # vm.tiktok.com/ID
            return url
        elif platform == "facebook":
            return url
        return None
    
    async def download_youtube(self, url: str, user_id: int) -> dict:
        """YouTube dan yuklab olish"""
        result = {"success": False, "files": [], "error": None}
        
        video_id = self.extract_video_id(url, "youtube")
        if not video_id:
            result["error"] = "YouTube video ID topilmadi"
            return result
        
        try:
            async with aiohttp.ClientSession() as session:
                api_url = f"{self.base_url}/youtube/v3/video/details"
                params = {
                    "videoId": video_id,
                    "renderableFormats": "720p,480p,360p",
                    "urlAccess": "proxied",
                    "getTranscript": "false"
                }
                
                async with session.get(api_url, headers=self.headers, params=params) as resp:
                    if resp.status != 200:
                        result["error"] = f"API xatolik: {resp.status}"
                        return result
                    
                    data = await resp.json()
                
                # Video URL olish - contents[0]["videos"] strukturasi
                video_url = None
                
                if "contents" in data and len(data["contents"]) > 0:
                    content = data["contents"][0]
                    
                    if "videos" in content:
                        # 360p combined (itag 18) - audio bilan
                        for v in content["videos"]:
                            meta = v.get("metadata", {})
                            if meta.get("itag") == 18 and v.get("url"):
                                video_url = v.get("url")
                                break
                        
                        # Yoki 720p/480p mp4
                        if not video_url:
                            for label in ["720p", "480p", "360p"]:
                                for v in content["videos"]:
                                    if label in v.get("label", "") and "mp4" in v.get("metadata", {}).get("mime_type", "") and v.get("url"):
                                        video_url = v.get("url")
                                        break
                                if video_url:
                                    break
                
                if video_url:
                    file_path = self.get_file_path(user_id, "mp4")
                    
                    timeout = aiohttp.ClientTimeout(total=300)
                    async with aiohttp.ClientSession(timeout=timeout) as dl_session:
                        async with dl_session.get(video_url) as video_resp:
                            if video_resp.status == 200:
                                with open(file_path, 'wb') as f:
                                    async for chunk in video_resp.content.iter_chunked(1024*1024):
                                        f.write(chunk)
                                
                                size = os.path.getsize(file_path)
                                if size > 1000:
                                    result["success"] = True
                                    result["files"].append({"path": file_path, "type": "video", "size": size})
                                else:
                                    result["error"] = "Fayl juda kichik"
                            else:
                                result["error"] = f"Yuklab bo'lmadi: {video_resp.status}"
                else:
                    result["error"] = "Video URL topilmadi"
                    
        except Exception as e:
            result["error"] = str(e)[:100]
        
        return result
    
    async def download_instagram(self, url: str, user_id: int) -> dict:
        """Instagram dan yuklab olish"""
        result = {"success": False, "files": [], "error": None}
        
        try:
            async with aiohttp.ClientSession() as session:
                api_url = f"{self.base_url}/instagram/v4/post/details"
                params = {"url": url}
                
                async with session.get(api_url, headers=self.headers, params=params) as resp:
                    if resp.status != 200:
                        result["error"] = f"API xatolik: {resp.status}"
                        return result
                    
                    data = await resp.json()
                
                # Media URL olish
                media_url = None
                media_type = "video"
                
                if "video_versions" in data:
                    for v in data["video_versions"]:
                        if v.get("url"):
                            media_url = v["url"]
                            break
                elif "image_versions" in data:
                    for img in data.get("image_versions", {}).get("items", []):
                        if img.get("url"):
                            media_url = img["url"]
                            media_type = "image"
                            break
                elif "media" in data:
                    media = data["media"]
                    if media.get("video_url"):
                        media_url = media["video_url"]
                    elif media.get("image_url"):
                        media_url = media["image_url"]
                        media_type = "image"
                
                if media_url:
                    ext = "mp4" if media_type == "video" else "jpg"
                    file_path = self.get_file_path(user_id, ext)
                    
                    async with session.get(media_url) as media_resp:
                        if media_resp.status == 200:
                            content = await media_resp.read()
                            with open(file_path, 'wb') as f:
                                f.write(content)
                            
                            size = os.path.getsize(file_path)
                            if size > 1000:
                                result["success"] = True
                                result["files"].append({"path": file_path, "type": media_type, "size": size})
                else:
                    result["error"] = "Media URL topilmadi"
                    
        except Exception as e:
            result["error"] = str(e)[:100]
        
        return result
    
    async def download_tiktok(self, url: str, user_id: int) -> dict:
        """TikTok dan yuklab olish"""
        result = {"success": False, "files": [], "error": None}
        
        try:
            async with aiohttp.ClientSession() as session:
                api_url = f"{self.base_url}/tiktok/v3/video/details"
                params = {"url": url}
                
                async with session.get(api_url, headers=self.headers, params=params) as resp:
                    if resp.status != 200:
                        result["error"] = f"API xatolik: {resp.status}"
                        return result
                    
                    data = await resp.json()
                
                # Video URL olish
                video_url = None
                
                if "video" in data:
                    video_url = data["video"].get("noWatermark") or data["video"].get("playAddr")
                elif "playAddr" in data:
                    video_url = data["playAddr"]
                elif "downloadAddr" in data:
                    video_url = data["downloadAddr"]
                
                if video_url:
                    file_path = self.get_file_path(user_id, "mp4")
                    
                    async with session.get(video_url) as video_resp:
                        if video_resp.status == 200:
                            content = await video_resp.read()
                            with open(file_path, 'wb') as f:
                                f.write(content)
                            
                            size = os.path.getsize(file_path)
                            if size > 1000:
                                result["success"] = True
                                result["files"].append({"path": file_path, "type": "video", "size": size})
                else:
                    result["error"] = "Video URL topilmadi"
                    
        except Exception as e:
            result["error"] = str(e)[:100]
        
        return result
    
    async def download_facebook(self, url: str, user_id: int) -> dict:
        """Facebook dan yuklab olish"""
        result = {"success": False, "files": [], "error": None}
        
        try:
            async with aiohttp.ClientSession() as session:
                api_url = f"{self.base_url}/facebook/v3/video/details"
                params = {"url": url}
                
                async with session.get(api_url, headers=self.headers, params=params) as resp:
                    if resp.status != 200:
                        result["error"] = f"API xatolik: {resp.status}"
                        return result
                    
                    data = await resp.json()
                
                video_url = data.get("hd") or data.get("sd") or data.get("video_url")
                
                if video_url:
                    file_path = self.get_file_path(user_id, "mp4")
                    
                    async with session.get(video_url) as video_resp:
                        if video_resp.status == 200:
                            content = await video_resp.read()
                            with open(file_path, 'wb') as f:
                                f.write(content)
                            
                            size = os.path.getsize(file_path)
                            if size > 1000:
                                result["success"] = True
                                result["files"].append({"path": file_path, "type": "video", "size": size})
                else:
                    result["error"] = "Video URL topilmadi"
                    
        except Exception as e:
            result["error"] = str(e)[:100]
        
        return result
    
    async def download(self, url: str, user_id: int) -> dict:
        """Universal yuklab olish"""
        url_lower = url.lower()
        
        if "youtube.com" in url_lower or "youtu.be" in url_lower:
            return await self.download_youtube(url, user_id)
        elif "instagram.com" in url_lower:
            return await self.download_instagram(url, user_id)
        elif "tiktok.com" in url_lower:
            return await self.download_tiktok(url, user_id)
        elif "facebook.com" in url_lower or "fb.watch" in url_lower:
            return await self.download_facebook(url, user_id)
        else:
            return {"success": False, "files": [], "error": "Platforma qo'llab-quvvatlanmaydi"}

# ========== DATABASE ==========
def get_db():
    return pymysql.connect(**DB_CONFIG)

def init_database():
    conn = get_db()
    cursor = conn.cursor()
    
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id BIGINT UNIQUE NOT NULL,
            username VARCHAR(255),
            first_name VARCHAR(255),
            last_name VARCHAR(255),
            language_code VARCHAR(10),
            is_banned TINYINT DEFAULT 0,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            last_active DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            INDEX idx_user_id (user_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    ''')
    
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS downloads (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id BIGINT NOT NULL,
            url TEXT,
            platform VARCHAR(50),
            status VARCHAR(20),
            file_size BIGINT DEFAULT 0,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            INDEX idx_user_id (user_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    ''')
    
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS channels (
            id INT AUTO_INCREMENT PRIMARY KEY,
            channel_id VARCHAR(100) UNIQUE NOT NULL,
            channel_name VARCHAR(255),
            channel_url VARCHAR(255),
            is_active TINYINT DEFAULT 1,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
    ''')
    
    conn.commit()
    conn.close()

def add_user(user: types.User):
    conn = get_db()
    cursor = conn.cursor()
    try:
        cursor.execute('''
            INSERT INTO users (user_id, username, first_name, last_name, language_code)
            VALUES (%s, %s, %s, %s, %s)
            ON DUPLICATE KEY UPDATE
                username = VALUES(username),
                first_name = VALUES(first_name),
                last_name = VALUES(last_name),
                last_active = NOW()
        ''', (user.id, user.username, user.first_name, user.last_name, user.language_code))
        conn.commit()
    finally:
        conn.close()

def is_user_banned(user_id: int) -> bool:
    conn = get_db()
    cursor = conn.cursor()
    try:
        cursor.execute('SELECT is_banned FROM users WHERE user_id = %s', (user_id,))
        result = cursor.fetchone()
        return result and result['is_banned'] == 1
    finally:
        conn.close()

def add_download(user_id: int, url: str, platform: str, status: str, file_size: int = 0):
    conn = get_db()
    cursor = conn.cursor()
    try:
        cursor.execute('''
            INSERT INTO downloads (user_id, url, platform, status, file_size)
            VALUES (%s, %s, %s, %s, %s)
        ''', (user_id, url, platform, status, file_size))
        conn.commit()
    finally:
        conn.close()

def get_active_channels():
    conn = get_db()
    cursor = conn.cursor()
    try:
        cursor.execute('SELECT channel_id, channel_name, channel_url FROM channels WHERE is_active = 1')
        return cursor.fetchall()
    finally:
        conn.close()

def get_user_count():
    conn = get_db()
    cursor = conn.cursor()
    try:
        cursor.execute('SELECT COUNT(*) as c FROM users')
        return cursor.fetchone()['c']
    finally:
        conn.close()

def get_today_users():
    conn = get_db()
    cursor = conn.cursor()
    try:
        cursor.execute("SELECT COUNT(*) as c FROM users WHERE DATE(created_at) = CURDATE()")
        return cursor.fetchone()['c']
    finally:
        conn.close()

def get_download_count():
    conn = get_db()
    cursor = conn.cursor()
    try:
        cursor.execute('SELECT COUNT(*) as c FROM downloads')
        return cursor.fetchone()['c']
    finally:
        conn.close()

def get_all_users():
    conn = get_db()
    cursor = conn.cursor()
    try:
        cursor.execute('SELECT user_id FROM users WHERE is_banned = 0')
        return [row['user_id'] for row in cursor.fetchall()]
    finally:
        conn.close()

# ========== SETTINGS ==========
def load_settings():
    default = {
        "force_subscribe": False,
        "welcome_message": "🎬 <b>Universal Video Yuklovchi Bot</b>\n\n📥 Havolani yuboring!\n\n✅ Instagram\n✅ TikTok\n✅ YouTube\n✅ Facebook",
        "subscribe_message": "⚠️ Kanallarga obuna bo'ling:",
        "subscribed_message": "✅ Rahmat!"
    }
    if os.path.exists(SETTINGS_PATH):
        try:
            with open(SETTINGS_PATH, 'r') as f:
                default.update(json.load(f))
        except:
            pass
    return default

SETTINGS = load_settings()

# ========== BOT ==========
bot = Bot(token=BOT_TOKEN)
dp = Dispatcher()
downloader = RapidAPIDownloader()

def detect_platform(url: str):
    url = url.lower()
    if "instagram.com" in url:
        return "instagram", "📸 Instagram"
    elif "tiktok.com" in url:
        return "tiktok", "🎵 TikTok"
    elif "youtube.com" in url or "youtu.be" in url:
        return "youtube", "🎬 YouTube"
    elif "facebook.com" in url or "fb.watch" in url:
        return "facebook", "📘 Facebook"
    return "other", "🌐 Video"

async def check_subscription(user_id: int) -> tuple:
    if not SETTINGS.get("force_subscribe", False):
        return True, []
    
    channels = get_active_channels()
    if not channels:
        return True, []
    
    not_subscribed = []
    for ch in channels:
        try:
            member = await bot.get_chat_member(chat_id=ch['channel_id'], user_id=user_id)
            if member.status in ['left', 'kicked']:
                not_subscribed.append((ch['channel_name'], ch['channel_url']))
        except:
            pass
    
    return len(not_subscribed) == 0, not_subscribed

def get_subscribe_keyboard(channels):
    buttons = [[InlineKeyboardButton(text=f"📢 {n}", url=u)] for n, u in channels]
    buttons.append([InlineKeyboardButton(text="✅ Tekshirish", callback_data="check_sub")])
    return InlineKeyboardMarkup(inline_keyboard=buttons)

def cleanup(user_id: int):
    folder = os.path.join(DOWNLOAD_PATH, str(user_id))
    if os.path.exists(folder):
        shutil.rmtree(folder)

def format_size(size):
    if size < 1024:
        return f"{size} B"
    elif size < 1024 * 1024:
        return f"{size // 1024} KB"
    return f"{size // (1024 * 1024)} MB"

def is_admin(user_id: int) -> bool:
    return user_id in ADMIN_IDS

# ========== HANDLERS ==========
@dp.message(CommandStart())
async def start_handler(message: types.Message):
    add_user(message.from_user)
    
    if is_user_banned(message.from_user.id):
        return await message.answer("🚫 Bloklangansiz")
    
    is_sub, not_sub = await check_subscription(message.from_user.id)
    if not is_sub:
        return await message.answer(SETTINGS["subscribe_message"], reply_markup=get_subscribe_keyboard(not_sub), parse_mode="HTML")
    
    await message.answer(SETTINGS["welcome_message"], parse_mode="HTML")

@dp.callback_query(F.data == "check_sub")
async def check_sub_callback(callback: types.CallbackQuery):
    is_sub, _ = await check_subscription(callback.from_user.id)
    if is_sub:
        await callback.message.edit_text(SETTINGS["subscribed_message"], parse_mode="HTML")
    else:
        await callback.answer("❌ Obuna bo'lmadingiz!", show_alert=True)

@dp.message(Command("stats"))
async def stats_handler(message: types.Message):
    if not is_admin(message.from_user.id):
        return
    
    await message.answer(f"""
📊 <b>Statistika</b>

👥 Jami: <b>{get_user_count()}</b>
📅 Bugun: <b>{get_today_users()}</b>
📥 Yuklashlar: <b>{get_download_count()}</b>
""", parse_mode="HTML")

@dp.message(Command("broadcast"))
async def broadcast_cmd(message: types.Message):
    if not is_admin(message.from_user.id):
        return
    await message.answer("📢 Xabarni reply qilib /send yuboring")

@dp.message(Command("send"))
async def send_broadcast(message: types.Message):
    if not is_admin(message.from_user.id):
        return
    if not message.reply_to_message:
        return await message.answer("❌ Reply qiling!")
    
    users = get_all_users()
    sent, failed = 0, 0
    status = await message.answer(f"📤 0/{len(users)}")
    
    for uid in users:
        try:
            await message.reply_to_message.copy_to(uid)
            sent += 1
        except:
            failed += 1
        if (sent + failed) % 50 == 0:
            await status.edit_text(f"📤 {sent + failed}/{len(users)}")
        await asyncio.sleep(0.05)
    
    await status.edit_text(f"✅ Yuborildi: {sent}\n❌ Xatolik: {failed}")

@dp.message(Command("ban"))
async def ban_cmd(message: types.Message):
    if not is_admin(message.from_user.id):
        return
    args = message.text.split()
    if len(args) < 2:
        return await message.answer("❌ /ban <user_id>")
    try:
        uid = int(args[1])
        conn = get_db()
        cursor = conn.cursor()
        cursor.execute('UPDATE users SET is_banned = 1 WHERE user_id = %s', (uid,))
        conn.commit()
        conn.close()
        await message.answer(f"🚫 {uid} bloklandi")
    except:
        await message.answer("❌ Xatolik")

@dp.message(Command("unban"))
async def unban_cmd(message: types.Message):
    if not is_admin(message.from_user.id):
        return
    args = message.text.split()
    if len(args) < 2:
        return await message.answer("❌ /unban <user_id>")
    try:
        uid = int(args[1])
        conn = get_db()
        cursor = conn.cursor()
        cursor.execute('UPDATE users SET is_banned = 0 WHERE user_id = %s', (uid,))
        conn.commit()
        conn.close()
        await message.answer(f"✅ {uid} blokdan chiqdi")
    except:
        await message.answer("❌ Xatolik")

@dp.message(F.text)
async def url_handler(message: types.Message):
    add_user(message.from_user)
    
    if is_user_banned(message.from_user.id):
        return await message.answer("🚫 Bloklangansiz")
    
    is_sub, not_sub = await check_subscription(message.from_user.id)
    if not is_sub:
        return await message.answer(SETTINGS["subscribe_message"], reply_markup=get_subscribe_keyboard(not_sub), parse_mode="HTML")
    
    url = message.text.strip()
    
    supported = ["instagram.com", "tiktok.com", "youtube.com", "youtu.be", "facebook.com", "fb.watch"]
    if not any(s in url.lower() for s in supported):
        return
    
    platform, platform_name = detect_platform(url)
    status_msg = await message.answer(f"⏳ {platform_name} yuklanmoqda...")
    
    try:
        result = await downloader.download(url, message.from_user.id)
        
        total_size = sum(f.get("size", 0) for f in result.get("files", []))
        add_download(message.from_user.id, url, platform, "success" if result["success"] else "failed", total_size)
        
        if not result["success"]:
            error_msg = result.get('error', 'Yuklanmadi')
            return await status_msg.edit_text(f"❌ {error_msg}")
        
        await status_msg.edit_text(f"📤 {platform_name} yuborilmoqda...")
        
        for f in result["files"]:
            try:
                if f["size"] > 50 * 1024 * 1024:
                    await message.answer(f"⚠️ Fayl katta ({format_size(f['size'])})")
                    continue
                
                file = FSInputFile(f["path"])
                caption = f"{platform_name} | {format_size(f['size'])}"
                
                if f["type"] == "video":
                    await message.answer_video(video=file, caption=caption)
                else:
                    await message.answer_photo(photo=file, caption=caption)
            except Exception as e:
                print(f"Send error: {e}")
        
        await status_msg.delete()
        
    except Exception as e:
        error_str = str(e)[:100]
        await status_msg.edit_text(f"❌ Xatolik: {error_str}")
    finally:
        cleanup(message.from_user.id)

async def main():
    init_database()
    os.makedirs(DOWNLOAD_PATH, exist_ok=True)
    
    print("=" * 50)
    print("✅ YuklaBot RapidAPI bilan ishga tushdi!")
    print(f"👥 Adminlar: {ADMIN_IDS}")
    print("🗄️ MySQL | 🌐 RapidAPI")
    print("=" * 50)
    
    await dp.start_polling(bot)

if __name__ == "__main__":
    asyncio.run(main())