"""YouTube metadata extraction service using curl.

This service extracts metadata from YouTube URLs using curl commands to avoid
the YouTube API dependency. It extracts title, channel, description, and video length.
"""

import asyncio
import json
import logging
import re
import subprocess
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Optional, Protocol, runtime_checkable
from urllib.parse import urlparse, parse_qs

from ..base.services import BaseService
from ..database.models import YouTubeVideo
from ..database.connection import get_session

logger = logging.getLogger(__name__)


@runtime_checkable
class YouTubeMetadataExtractor(Protocol):
    """Protocol for YouTube metadata extraction."""

    async def extract_metadata(self, url: str) -> Dict[str, Any]:
        """Extract metadata from YouTube URL."""
        ...


class CurlYouTubeExtractor:
    """YouTube metadata extractor using curl and yt-dlp."""

    def __init__(self, cache_dir: Optional[Path] = None):
        self.cache_dir = cache_dir or Path("/tmp/trax_youtube_cache")
        self.cache_dir.mkdir(parents=True, exist_ok=True)

    async def extract_metadata(self, url: str) -> Dict[str, Any]:
        """Extract metadata from YouTube URL using yt-dlp."""
        try:
            # Use yt-dlp to extract metadata without downloading
            cmd = [
                "yt-dlp",
                "--dump-json",
                "--no-download",
                "--no-playlist",
                url
            ]
            
            logger.info(f"Extracting metadata from: {url}")
            
            # Run the command asynchronously
            process = await asyncio.create_subprocess_exec(
                *cmd,
                stdout=asyncio.subprocess.PIPE,
                stderr=asyncio.subprocess.PIPE
            )
            
            stdout, stderr = await process.communicate()
            
            if process.returncode != 0:
                error_msg = stderr.decode() if stderr else "Unknown error"
                logger.error(f"yt-dlp failed: {error_msg}")
                raise Exception(f"Failed to extract metadata: {error_msg}")
            
            # Parse the JSON output
            metadata = json.loads(stdout.decode())
            
            # Extract YouTube ID from URL
            youtube_id = self._extract_youtube_id(url)
            
            # Convert to our format
            return {
                "youtube_id": youtube_id,
                "title": metadata.get("title", ""),
                "channel": metadata.get("uploader", ""),
                "description": metadata.get("description", ""),
                "duration_seconds": metadata.get("duration", 0),
                "url": url,
                "metadata_extracted_at": datetime.now(timezone.utc),
                "raw_metadata": metadata  # Store full metadata for debugging
            }
            
        except Exception as e:
            logger.error(f"Error extracting metadata from {url}: {e}")
            raise

    def _extract_youtube_id(self, url: str) -> str:
        """Extract YouTube video ID from URL."""
        # Handle various YouTube URL formats
        patterns = [
            r"(?:youtube\.com/watch\?v=|youtu\.be/|youtube\.com/embed/)([a-zA-Z0-9_-]{11})",
            r"youtube\.com/v/([a-zA-Z0-9_-]{11})",
        ]
        
        for pattern in patterns:
            match = re.search(pattern, url)
            if match:
                return match.group(1)
        
        raise ValueError(f"Could not extract YouTube ID from URL: {url}")


class YouTubeMetadataService(BaseService):
    """YouTube metadata extraction service."""

    def __init__(self, config: Optional[Dict[str, Any]] = None):
        super().__init__("youtube_metadata", config)
        self.extractor = CurlYouTubeExtractor()
        self._session = None

    async def _initialize_impl(self) -> None:
        """Initialize the service."""
        logger.info("Initializing YouTube metadata service")
        # Service is ready immediately since it uses external tools

    async def _shutdown_impl(self) -> None:
        """Shutdown the service."""
        logger.info("Shutting down YouTube metadata service")
        if self._session:
            await self._session.close()

    async def extract_and_store_metadata(self, url: str) -> YouTubeVideo:
        """Extract metadata from YouTube URL and store in database."""
        try:
            # Extract metadata
            metadata = await self.extractor.extract_metadata(url)
            
            # Store in database using synchronous session
            from ..database.connection import get_db_session
            
            with get_db_session() as session:
                # Check if video already exists
                existing_video = session.query(YouTubeVideo).filter(
                    YouTubeVideo.youtube_id == metadata["youtube_id"]
                ).first()
                
                if existing_video:
                    # Update existing video
                    for key, value in metadata.items():
                        if key != "raw_metadata" and hasattr(existing_video, key):
                            setattr(existing_video, key, value)
                    video = existing_video
                    logger.info(f"Updated existing video: {metadata['youtube_id']}")
                else:
                    # Create new video
                    video = YouTubeVideo(**{k: v for k, v in metadata.items() if k != "raw_metadata"})
                    session.add(video)
                    logger.info(f"Created new video: {metadata['youtube_id']}")
                
                session.commit()
                session.refresh(video)
                
                return video
                
        except Exception as e:
            logger.error(f"Error in extract_and_store_metadata: {e}")
            raise

    async def get_video_metadata(self, youtube_id: str) -> Optional[YouTubeVideo]:
        """Get video metadata from database."""
        try:
            from ..database.connection import get_db_session
            
            with get_db_session() as session:
                video = session.query(YouTubeVideo).filter(
                    YouTubeVideo.youtube_id == youtube_id
                ).first()
                return video
        except Exception as e:
            logger.error(f"Error getting video metadata: {e}")
            return None

    async def list_videos(self, limit: int = 100) -> list[YouTubeVideo]:
        """List videos from database."""
        try:
            from ..database.connection import get_db_session
            
            with get_db_session() as session:
                videos = session.query(YouTubeVideo).order_by(
                    YouTubeVideo.created_at.desc()
                ).limit(limit).all()
                return videos
        except Exception as e:
            logger.error(f"Error listing videos: {e}")
            return []

    def get_health_status(self) -> Dict[str, Any]:
        """Get service health status."""
        base_status = super().get_health_status()
        
        # Check if yt-dlp is available
        try:
            result = subprocess.run(
                ["yt-dlp", "--version"],
                capture_output=True,
                text=True,
                timeout=5
            )
            yt_dlp_available = result.returncode == 0
        except Exception:
            yt_dlp_available = False
        
        base_status.update({
            "yt_dlp_available": yt_dlp_available,
            "cache_dir": str(self.extractor.cache_dir),
        })
        
        return base_status
