"""
VideoDownloader - Download videos from YouTube, Instagram, TikTok, Twitter/X and more.
Uses yt-dlp under the hood for maximum compatibility.
"""

import asyncio
import os
import re
import tempfile
from typing import Dict, Optional
import logging

logger = logging.getLogger("zehnex.downloader")


class VideoInfo:
    """Holds metadata about a downloaded/queried video."""

    def __init__(self, data: Dict):
        self.title: str = data.get("title", "Unknown")
        self.duration: int = data.get("duration", 0)
        self.uploader: str = data.get("uploader", "Unknown")
        self.view_count: int = data.get("view_count", 0)
        self.like_count: int = data.get("like_count", 0)
        self.thumbnail: Optional[str] = data.get("thumbnail")
        self.url: str = data.get("webpage_url", "")
        self.ext: str = data.get("ext", "mp4")
        self.filesize: Optional[int] = data.get("filesize")
        self.description: str = data.get("description", "")[:300]

    @property
    def duration_str(self) -> str:
        if not self.duration:
            return "N/A"
        m, s = divmod(self.duration, 60)
        h, m = divmod(m, 60)
        if h:
            return f"{h}:{m:02d}:{s:02d}"
        return f"{m}:{s:02d}"

    @property
    def filesize_str(self) -> str:
        if not self.filesize:
            return "N/A"
        mb = self.filesize / 1024 / 1024
        if mb >= 1024:
            return f"{mb/1024:.1f} GB"
        return f"{mb:.1f} MB"

    def __str__(self):
        return (
            f"🎬 <b>{self.title}</b>\n"
            f"👤 {self.uploader}\n"
            f"⏱ {self.duration_str}\n"
            f"👁 {self.view_count:,} ko'rishlar\n"
            f"📦 {self.filesize_str}"
        )


class VideoDownloader:
    """
    Download videos from YouTube, TikTok, Instagram, Twitter/X, Facebook and 1000+ sites.

    Example:
        dl = VideoDownloader(output_dir="/tmp/zehnex_downloads")

        @bot.command("video")
        async def video_cmd(ctx):
            url = ctx.args[0] if ctx.args else None
            if not url:
                await ctx.reply("URL yuboring!")
                return

            await ctx.upload_video()
            info = await dl.get_info(url)
            await ctx.reply(str(info))

            path = await dl.download(url, quality="best")
            await ctx.send_video(path, caption=info.title)
            dl.cleanup(path)
    """

    SUPPORTED_SITES = [
        "youtube.com", "youtu.be",
        "tiktok.com", "instagram.com",
        "twitter.com", "x.com",
        "facebook.com", "fb.watch",
        "vimeo.com", "dailymotion.com",
        "reddit.com", "twitch.tv",
    ]

    def __init__(
        self,
        output_dir: Optional[str] = None,
        max_filesize: str = "50M",
        cookies_file: Optional[str] = None,
    ):
        self.output_dir = output_dir or tempfile.gettempdir()
        self.max_filesize = max_filesize
        self.cookies_file = cookies_file
        os.makedirs(self.output_dir, exist_ok=True)

    def _build_opts(self, quality: str, audio_only: bool, output_path: str) -> Dict:
        opts = {
            "outtmpl": output_path,
            "noplaylist": True,
            "no_warnings": True,
            "quiet": True,
            "merge_output_format": "mp4",
        }

        if self.cookies_file:
            opts["cookiefile"] = self.cookies_file

        if audio_only:
            opts["format"] = "bestaudio/best"
            opts["postprocessors"] = [{
                "key": "FFmpegExtractAudio",
                "preferredcodec": "mp3",
                "preferredquality": "192",
            }]
        else:
            quality_map = {
                "best": "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best",
                "720p": "bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720]",
                "480p": "bestvideo[height<=480][ext=mp4]+bestaudio[ext=m4a]/best[height<=480]",
                "360p": "bestvideo[height<=360][ext=mp4]+bestaudio[ext=m4a]/best[height<=360]",
            }
            opts["format"] = quality_map.get(quality, quality_map["best"])

        return opts

    async def get_info(self, url: str) -> VideoInfo:
        """Get video metadata without downloading."""
        try:
            import yt_dlp
        except ImportError:
            raise ImportError("yt-dlp o'rnatilmagan. pip install yt-dlp")

        def _fetch():
            opts = {"quiet": True, "no_warnings": True, "skip_download": True}
            if self.cookies_file:
                opts["cookiefile"] = self.cookies_file
            with yt_dlp.YoutubeDL(opts) as ydl:
                info = ydl.extract_info(url, download=False)
                return info

        loop = asyncio.get_event_loop()
        info = await loop.run_in_executor(None, _fetch)
        return VideoInfo(info or {})

    async def download(
        self,
        url: str,
        quality: str = "best",
        audio_only: bool = False,
        filename: Optional[str] = None,
    ) -> str:
        """
        Download video. Returns path to downloaded file.

        Args:
            url: Video URL
            quality: "best", "720p", "480p", "360p"
            audio_only: If True, extracts MP3 audio
            filename: Custom output filename (without extension)

        Returns:
            Full path to downloaded file
        """
        try:
            import yt_dlp
        except ImportError:
            raise ImportError("yt-dlp o'rnatilmagan. pip install yt-dlp")

        if not filename:
            import uuid
            filename = str(uuid.uuid4())

        ext = "mp3" if audio_only else "mp4"
        output_path = os.path.join(self.output_dir, f"{filename}.%(ext)s")
        final_path = os.path.join(self.output_dir, f"{filename}.{ext}")

        opts = self._build_opts(quality, audio_only, output_path)

        def _download():
            with yt_dlp.YoutubeDL(opts) as ydl:
                ydl.download([url])

        loop = asyncio.get_event_loop()
        await loop.run_in_executor(None, _download)

        # Find actual downloaded file
        for f in os.listdir(self.output_dir):
            if f.startswith(filename):
                return os.path.join(self.output_dir, f)

        return final_path

    def cleanup(self, *paths: str):
        """Delete downloaded files to free space."""
        for path in paths:
            try:
                if path and os.path.exists(path):
                    os.remove(path)
                    logger.info(f"Cleaned up: {path}")
            except Exception as e:
                logger.warning(f"Cleanup failed for {path}: {e}")

    @staticmethod
    def is_supported(url: str) -> bool:
        """Check if URL is from a supported site."""
        url_lower = url.lower()
        supported = VideoDownloader.SUPPORTED_SITES
        return any(site in url_lower for site in supported)

    @staticmethod
    def extract_url(text: str) -> Optional[str]:
        """Extract first URL from text."""
        pattern = r'https?://[^\s]+'
        match = re.search(pattern, text)
        return match.group(0) if match else None
