import logging
import os
from pathlib import Path
from datetime import datetime

import yt_dlp

from backend.core.config import settings

logger = logging.getLogger(__name__)


class VideoDownloader:
    """Download videos from YouTube, TikTok, and Instagram using yt-dlp + pytubefix."""

    def __init__(self):
        self.base_dir = settings.download_path

    def download(
        self,
        url: str,
        platform: str = "unknown",
        quality: str = "best",
        video_id: str = "",
    ) -> dict:
        """
        Download a video and return info about the downloaded file.
        For YouTube: uses pytubefix with OAuth (bypasses bot detection).
        For TikTok/Instagram: uses yt-dlp.
        """
        # YouTube: use pytubefix (yt-dlp is blocked on datacenter IPs)
        if platform == "youtube":
            return self._download_youtube(url, video_id)

        # TikTok, Instagram, etc: use yt-dlp
        return self._download_ytdlp(url, platform, quality, video_id)

    def _download_youtube(self, url: str, video_id: str) -> dict:
        """Download YouTube video using pytubefix with OAuth."""
        from backend.services.youtube_auth import yt_auth_manager

        platform_dir = self.base_dir / "youtube"
        date_str = datetime.now().strftime("%Y%m%d")
        filename_base = f"{date_str}_{video_id}" if video_id else date_str

        return yt_auth_manager.download_video(url, platform_dir, filename_base)

    def _download_ytdlp(
        self, url: str, platform: str, quality: str, video_id: str
    ) -> dict:
        """Download video using yt-dlp (for TikTok, Instagram, etc)."""
        platform_dir = self.base_dir / platform
        platform_dir.mkdir(parents=True, exist_ok=True)

        date_str = datetime.now().strftime("%Y%m%d")
        filename_base = f"{date_str}_{video_id}" if video_id else date_str

        output_template = str(platform_dir / f"{filename_base}_%(title).50s.%(ext)s")

        format_spec = self._get_format_spec(quality)

        ydl_opts = {
            "format": format_spec,
            "outtmpl": output_template,
            "noplaylist": True,
            "no_warnings": True,
            "quiet": True,
            "merge_output_format": "mp4",
            "postprocessors": [
                {
                    "key": "FFmpegVideoConvertor",
                    "preferedformat": "mp4",
                }
            ],
        }

        if platform == "tiktok":
            ydl_opts["http_headers"] = {
                "User-Agent": (
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
                    "AppleWebKit/537.36 (KHTML, like Gecko) "
                    "Chrome/120.0.0.0 Safari/537.36"
                )
            }

        try:
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                info = ydl.extract_info(url, download=True)
                filename = ydl.prepare_filename(info)

                if not filename.endswith(".mp4"):
                    mp4_path = Path(filename).with_suffix(".mp4")
                    if mp4_path.exists():
                        filename = str(mp4_path)

                file_path = Path(filename)
                file_size = file_path.stat().st_size if file_path.exists() else 0

                return {
                    "success": True,
                    "path": str(file_path),
                    "filename": file_path.name,
                    "size": file_size,
                    "size_formatted": self._format_size(file_size),
                    "duration": info.get("duration", 0),
                    "title": info.get("title", ""),
                    "error": None,
                }
        except Exception as e:
            return {
                "success": False,
                "path": "",
                "filename": "",
                "size": 0,
                "size_formatted": "",
                "duration": 0,
                "title": "",
                "error": str(e),
            }

    def get_video_info(self, url: str) -> dict | None:
        """Get video info without downloading."""
        ydl_opts = {
            "quiet": True,
            "no_warnings": True,
            "noplaylist": True,
        }
        cookies_path = Path(settings.data_path) / "youtube_cookies.txt"
        if cookies_path.exists() and "youtube" in url:
            ydl_opts["cookiefile"] = str(cookies_path)
        try:
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                info = ydl.extract_info(url, download=False)
                return {
                    "title": info.get("title", ""),
                    "duration": info.get("duration", 0),
                    "thumbnail": info.get("thumbnail", ""),
                    "formats": [
                        {
                            "format_id": f.get("format_id"),
                            "ext": f.get("ext"),
                            "quality": f.get("format_note", ""),
                            "filesize": f.get("filesize", 0),
                        }
                        for f in info.get("formats", [])
                        if f.get("vcodec") != "none"
                    ][:10],
                }
        except Exception:
            return None

    def _get_format_spec(self, quality: str) -> str:
        quality_map = {
            "best": "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best",
            "1080": "bestvideo[height<=1080][ext=mp4]+bestaudio[ext=m4a]/best[height<=1080]",
            "720": "bestvideo[height<=720][ext=mp4]+bestaudio[ext=m4a]/best[height<=720]",
            "480": "bestvideo[height<=480][ext=mp4]+bestaudio[ext=m4a]/best[height<=480]",
            "audio": "bestaudio[ext=m4a]/bestaudio",
        }
        return quality_map.get(quality, quality_map["best"])

    @staticmethod
    def _format_size(size_bytes: int) -> str:
        if size_bytes == 0:
            return "0 B"
        units = ["B", "KB", "MB", "GB"]
        i = 0
        size = float(size_bytes)
        while size >= 1024 and i < len(units) - 1:
            size /= 1024
            i += 1
        return f"{size:.1f} {units[i]}"

    def list_downloads(self, platform: str = "") -> list[dict]:
        """List all downloaded files."""
        files = []
        search_dir = self.base_dir / platform if platform else self.base_dir

        if not search_dir.exists():
            return files

        for file_path in search_dir.rglob("*.mp4"):
            stat = file_path.stat()
            files.append({
                "filename": file_path.name,
                "path": str(file_path),
                "platform": file_path.parent.name,
                "size": stat.st_size,
                "size_formatted": self._format_size(stat.st_size),
                "downloaded_at": datetime.fromtimestamp(stat.st_mtime).isoformat(),
            })

        files.sort(key=lambda x: x["downloaded_at"], reverse=True)
        return files

    def delete_download(self, file_path: str) -> bool:
        """Delete a downloaded file."""
        path = Path(file_path)
        if path.exists() and str(path).startswith(str(self.base_dir)):
            os.remove(path)
            return True
        return False
