"""
YouTube OAuth manager using pytubefix.
Handles device-code OAuth flow and caches tokens for yt-dlp/pytubefix downloads.
User authenticates ONCE via the web UI, then downloads work forever.
"""

import json
import logging
import threading
import time
from pathlib import Path

from pytubefix import YouTube
from pytubefix.exceptions import BotDetection

from backend.core.config import settings

logger = logging.getLogger(__name__)

TOKEN_FILE = Path(settings.data_path) / "youtube_oauth.json"
AUTH_STATE_FILE = Path(settings.data_path) / "youtube_auth_state.json"


class YouTubeAuthManager:
    """Manages YouTube OAuth authentication via pytubefix device code flow."""

    def __init__(self):
        self._auth_pending = False
        self._auth_code = None
        self._auth_url = None
        self._auth_thread = None
        self._auth_error = None
        self._auth_completed = False
        self._lock = threading.Lock()

    @property
    def is_authenticated(self) -> bool:
        """Check if we have a cached OAuth token."""
        return TOKEN_FILE.exists() and TOKEN_FILE.stat().st_size > 10

    def get_status(self) -> dict:
        """Get current auth status."""
        with self._lock:
            return {
                "authenticated": self.is_authenticated,
                "pending": self._auth_pending,
                "code": self._auth_code,
                "url": self._auth_url,
                "error": self._auth_error,
                "completed": self._auth_completed,
            }

    def start_auth(self) -> dict:
        """Start the OAuth device code flow. Returns the code for the user."""
        with self._lock:
            if self._auth_pending:
                return {
                    "status": "pending",
                    "code": self._auth_code,
                    "url": self._auth_url,
                }

            self._auth_pending = True
            self._auth_code = None
            self._auth_url = None
            self._auth_error = None
            self._auth_completed = False

        # Run OAuth in background thread
        self._auth_thread = threading.Thread(target=self._do_auth, daemon=True)
        self._auth_thread.start()

        # Wait up to 10 seconds for the code to be generated
        for _ in range(20):
            time.sleep(0.5)
            with self._lock:
                if self._auth_code:
                    return {
                        "status": "pending",
                        "code": self._auth_code,
                        "url": self._auth_url,
                    }
                if self._auth_error:
                    return {"status": "error", "error": self._auth_error}

        return {"status": "error", "error": "Timeout gerando código de autenticação"}

    def _custom_oauth_verifier(self, verification_url: str, user_code: str):
        """Called by pytubefix when it needs the user to authenticate."""
        with self._lock:
            self._auth_code = user_code
            self._auth_url = verification_url

        logger.info(f"YouTube OAuth: code={user_code}, url={verification_url}")

        # Wait for up to 5 minutes for the user to complete auth
        for _ in range(300):
            time.sleep(1)
            # pytubefix will poll Google's servers automatically
            # We just need to keep the thread alive

    def _do_auth(self):
        """Background thread that handles the OAuth flow."""
        try:
            yt = YouTube(
                "https://www.youtube.com/watch?v=dQw4w9WgXcQ",  # any valid video
                client="WEB",
                use_oauth=True,
                allow_oauth_cache=True,
                token_file=str(TOKEN_FILE),
                oauth_verifier=self._custom_oauth_verifier,
            )
            # If we get here, auth succeeded
            title = yt.title
            logger.info(f"YouTube OAuth successful! Test video: {title}")
            with self._lock:
                self._auth_completed = True
                self._auth_pending = False
                self._auth_code = None
        except Exception as e:
            error_msg = str(e)
            # "Precondition Required" means user hasn't completed auth yet - that's OK
            if "428" not in error_msg:
                logger.error(f"YouTube OAuth error: {e}")
                with self._lock:
                    self._auth_error = error_msg
            with self._lock:
                self._auth_pending = False

    def download_video(self, url: str, output_dir: Path, filename_base: str) -> dict:
        """Download a YouTube video using pytubefix with OAuth."""
        try:
            output_dir.mkdir(parents=True, exist_ok=True)

            yt_opts = {
                "client": "WEB",
                "use_oauth": self.is_authenticated,
                "allow_oauth_cache": True,
                "token_file": str(TOKEN_FILE),
            }

            yt = YouTube(url, **yt_opts)

            # Try progressive stream first (video + audio in one file)
            stream = (
                yt.streams.filter(progressive=True, file_extension="mp4")
                .order_by("resolution")
                .desc()
                .first()
            )

            if not stream:
                # Fallback to any available stream
                stream = yt.streams.filter(file_extension="mp4").first()

            if not stream:
                return {
                    "success": False,
                    "error": "Nenhum stream disponível para este vídeo",
                }

            filename = f"{filename_base}.mp4"
            filepath = stream.download(output_path=str(output_dir), filename=filename)

            file_path = Path(filepath)
            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": yt.length or 0,
                "title": yt.title or "",
                "error": None,
            }

        except BotDetection:
            return {
                "success": False,
                "error": "YouTube bloqueou o download. Conecte sua conta em Configurações > YouTube Auth.",
            }
        except Exception as e:
            logger.error(f"pytubefix download error: {e}")
            return {
                "success": False,
                "error": f"Erro no download: {str(e)[:200]}",
            }

    @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 revoke(self):
        """Remove cached OAuth tokens."""
        if TOKEN_FILE.exists():
            TOKEN_FILE.unlink()
        with self._lock:
            self._auth_completed = False
            self._auth_pending = False
            self._auth_code = None


# Singleton
yt_auth_manager = YouTubeAuthManager()
