import logging
import os
import shutil
import subprocess
from enum import Enum
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)


class VideoFormat(str, Enum):
    """
    Supported video formats for conversion
    """

    mp4 = "mp4"
    mov = "mov"
    avi = "avi"
    wmv = "wmv"
    flv = "flv"
    webm = "webm"
    ogg = "ogg"
    mkv = "mkv"
    mpeg = "mpeg"
    mpg = "mpg"


class VideoConverter:
    """
    A class for converting videos between different formats with format-specific optimizations.

    This class provides methods to convert videos to different formats using ffmpeg,
    with automatic format detection and format-specific parameter optimization.
    """

    def __init__(self, default_quality: int = 23, default_timeout: int = 3600):
        """
        Initialize the VideoConverter.

        Args:
            default_quality: Default quality setting (lower = better quality, typically 18-28)
            default_timeout: Default timeout in seconds for conversion operations
        """
        self.default_quality = default_quality
        self.default_timeout = default_timeout
        self._ffmpeg_available = None  # Cache for ffmpeg availability check

    def _check_ffmpeg_available(self) -> bool:
        """
        Check if ffmpeg is available on the system.

        Returns:
            True if ffmpeg is available, False otherwise
        """
        if self._ffmpeg_available is None:
            # Check if ffmpeg is in PATH
            ffmpeg_path = shutil.which("ffmpeg")
            self._ffmpeg_available = ffmpeg_path is not None

            if not self._ffmpeg_available:
                logger.warning(
                    "ffmpeg not found in PATH. Video conversion will not work. "
                    "Please install ffmpeg:\n"
                    "  Windows: Download from https://ffmpeg.org/download.html\n"
                    "  macOS: brew install ffmpeg\n"
                    "  Ubuntu/Debian: sudo apt install ffmpeg\n"
                    "  CentOS/RHEL: sudo yum install ffmpeg"
                )

        return self._ffmpeg_available

    def is_ffmpeg_available(self) -> bool:
        """
        Check if ffmpeg is available on the system.

        Returns:
            True if ffmpeg is available, False otherwise
        """
        return self._check_ffmpeg_available()

    @staticmethod
    def get_video_format(file_path: str) -> Optional[VideoFormat]:
        """
        Detect video format from file extension.

        Args:
            file_path: Path to the video file

        Returns:
            VideoFormat enum value or None if format not supported
        """
        extension = Path(file_path).suffix.lower().lstrip(".")
        try:
            return VideoFormat(extension)
        except ValueError:
            return None

    @staticmethod
    def is_format_supported(file_path: str) -> bool:
        """
        Check if a video format is supported by this converter.

        Args:
            file_path: Path to the video file

        Returns:
            True if the format is supported, False otherwise
        """
        return VideoConverter.get_video_format(file_path) is not None

    @staticmethod
    def get_supported_formats() -> list[str]:
        """
        Get a list of all supported video format extensions.

        Returns:
            List of supported format extensions (e.g., ['mp4', 'mov', 'avi', ...])
        """
        return [format.value for format in VideoFormat]

    def detect_video_format_with_ffprobe(self, file_path: str) -> Optional[str]:
        """
        Use ffprobe to detect the actual video format of a file.
        This is more accurate than checking file extensions.

        Args:
            file_path: Path to the video file

        Returns:
            Format string (e.g., 'mp4', 'mov', 'avi') or None if detection fails
        """
        if not self._check_ffmpeg_available():
            return None

        try:
            # Use ffprobe to get format information
            cmd = ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", file_path]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

            if result.returncode == 0:
                import json

                data = json.loads(result.stdout)
                format_name = data.get("format", {}).get("format_name", "").lower()

                # Extract the primary format (first one if multiple)
                if "," in format_name:
                    format_name = format_name.split(",")[0]

                # Check if this format is in our supported formats
                if format_name in [f.value for f in VideoFormat]:
                    return format_name
                else:
                    logger.info(f"ffprobe detected format '{format_name}' for {file_path}, but it's not in our supported formats list")
                    return None
            else:
                logger.warning(f"ffprobe failed for {file_path}: {result.stderr}")
                return None

        except (subprocess.TimeoutExpired, subprocess.CalledProcessError, json.JSONDecodeError) as e:
            logger.warning(f"Error detecting format with ffprobe for {file_path}: {e}")
            return None

    def _get_ffmpeg_params(self, output_format: VideoFormat, quality: int) -> dict:
        """
        Get ffmpeg parameters optimized for specific output format.

        Args:
            output_format: Target video format
            quality: Quality setting (lower = better quality)

        Returns:
            Dictionary with video codec, audio codec, and additional parameters
        """
        # Default parameters - no codec-specific settings in defaults
        params = {
            "video_codec": "libx264",
            "audio_codec": "aac",
            "audio_bitrate": "128k",
            "pixel_format": "yuv420p",
            "additional_params": [],
        }

        if output_format == VideoFormat.mp4:
            # Ensure MP4 always uses H.264 with browser-compatible settings
            params.update(
                {
                    "video_codec": "libx264",
                    "audio_codec": "aac",
                    "audio_bitrate": "128k",
                    "pixel_format": "yuv420p",
                    "additional_params": ["-profile:v", "baseline", "-level", "3.0", "-movflags", "faststart"],
                }
            )
        elif output_format == VideoFormat.webm:
            params.update(
                {
                    "video_codec": "libvpx-vp9",
                    "audio_codec": "libopus",
                    "audio_bitrate": "96k",
                    "additional_params": ["-b:v", "0"],  # VP9 with CRF mode
                }
            )
        elif output_format == VideoFormat.ogg:
            params.update(
                {
                    "video_codec": "libtheora",
                    "audio_codec": "libvorbis",
                    "audio_bitrate": "96k",
                    "additional_params": [],  # No H.264-specific parameters for libtheora
                }
            )
        elif output_format == VideoFormat.mkv:
            params.update(
                {
                    "video_codec": "libx264",
                    "audio_codec": "aac",
                    "additional_params": ["-c:s", "copy", "-profile:v", "baseline", "-level", "3.0"],  # Copy subtitles if present
                }
            )
        elif output_format == VideoFormat.mov:
            params.update({"video_codec": "libx264", "audio_codec": "aac", "additional_params": ["-movflags", "faststart", "-profile:v", "baseline", "-level", "3.0"]})
        elif output_format == VideoFormat.avi:
            params.update({"video_codec": "libx264", "audio_codec": "mp3", "audio_bitrate": "128k", "additional_params": ["-profile:v", "baseline", "-level", "3.0"]})
        elif output_format == VideoFormat.wmv:
            params.update(
                {
                    "video_codec": "wmv2",
                    "audio_codec": "wmav2",
                    "audio_bitrate": "128k",
                    "additional_params": [],  # No H.264-specific parameters for wmv2
                }
            )
        elif output_format == VideoFormat.flv:
            params.update(
                {
                    "video_codec": "flv",
                    "audio_codec": "mp3",
                    "audio_bitrate": "128k",
                    "additional_params": [],  # No H.264-specific parameters for flv
                }
            )
        elif output_format == VideoFormat.mpeg:
            params.update(
                {
                    "video_codec": "mpeg2video",
                    "audio_codec": "mp2",
                    "audio_bitrate": "128k",
                    "additional_params": [],  # No H.264-specific parameters for mpeg2video
                }
            )
        elif output_format == VideoFormat.mpg:
            params.update(
                {
                    "video_codec": "mpeg2video",
                    "audio_codec": "mp2",
                    "audio_bitrate": "128k",
                    "additional_params": [],  # No H.264-specific parameters for mpeg2video
                }
            )

        return params

    def convert_video(
        self,
        input_path: str,
        output_path: str,
        quality: Optional[int] = None,
        timeout: Optional[int] = None,
        input_format: Optional[VideoFormat] = None,
        output_format: Optional[VideoFormat] = None,
        remove_audio: bool = False,
    ) -> str:
        """
        Convert a video to a different format.

        Args:
            input_path: Path to the input video
            output_path: Path to the output video
            quality: Quality of the output video (lower = better quality, typically 18-28)
            timeout: Timeout in seconds
            input_format: Input format (auto-detected if None)
            output_format: Output format (auto-detected if None)
            remove_audio: If True, removes audio from the output video (reduces file size)
        Returns:
            Path to the converted video file

        Raises:
            FileNotFoundError: If the input file does not exist
            FileExistsError: If the output file already exists
            ValueError: If input or output format is not supported
            subprocess.TimeoutExpired: If the conversion takes too long
            subprocess.CalledProcessError: If the conversion fails
        """

        # Check if ffmpeg is available
        if not self._check_ffmpeg_available():
            raise RuntimeError(
                "ffmpeg is not available on your system. "
                "Please install ffmpeg to use video conversion features:\n"
                "  Windows: Download from https://ffmpeg.org/download.html\n"
                "  macOS: brew install ffmpeg\n"
                "  Ubuntu/Debian: sudo apt install ffmpeg\n"
                "  CentOS/RHEL: sudo yum install ffmpeg"
            )

        # Use default values if not provided
        quality = quality if quality is not None else self.default_quality
        timeout = timeout if timeout is not None else self.default_timeout

        if not os.path.exists(input_path):
            logger.error(f"Input file {input_path} does not exist")
            raise FileNotFoundError(f"Input file {input_path} does not exist")

        if os.path.exists(output_path):
            logger.error(f"Output file {output_path} already exists")
            raise FileExistsError(f"Output file {output_path} already exists")

        # Auto-detect formats if not provided
        if input_format is None:
            input_format = self.get_video_format(input_path)
            if input_format is None:
                # Try ffprobe detection as a fallback
                detected_format = self.detect_video_format_with_ffprobe(input_path)
                if detected_format:
                    try:
                        input_format = VideoFormat(detected_format)
                        logger.info(f"ffprobe detected format '{detected_format}' for {input_path}")
                    except ValueError:
                        # Even ffprobe detected format is not in our enum
                        pass

                if input_format is None:
                    # Final fallback: convert unsupported format to MP4
                    logger.warning(f"Unsupported input format for file: {input_path}. Attempting to convert to MP4.")
                    # Generate MP4 output path
                    output_dir = Path(output_path).parent
                    output_filename = Path(output_path).stem
                    if output_format == VideoFormat.mp4:
                        # If output is already MP4, just change the input format to None to let ffmpeg auto-detect
                        input_format = None
                    else:
                        # Change output to MP4 format
                        output_format = VideoFormat.mp4
                        output_path = str(output_dir / f"{output_filename}.mp4")
                        logger.info(f"Changed output format to MP4: {output_path}")

        if output_format is None:
            output_format = self.get_video_format(output_path)
            if output_format is None:
                raise ValueError(f"Unsupported output format for file: {output_path}")

        logger.info(f"Converting from {input_format.value} to {output_format.value}")

        # Get format-specific parameters
        params = self._get_ffmpeg_params(output_format, quality)

        # Build ffmpeg command
        ffmpeg_cmd = ["ffmpeg", "-i", input_path]

        # Add video codec and quality
        if output_format == VideoFormat.webm:
            ffmpeg_cmd.extend(["-c:v", params["video_codec"], "-crf", str(quality)])
        else:
            ffmpeg_cmd.extend(["-c:v", params["video_codec"], "-preset", "fast", "-crf", str(quality)])

        # Add pixel format
        ffmpeg_cmd.extend(["-pix_fmt", params["pixel_format"]])

        # Handle audio based on remove_audio flag
        if remove_audio:
            ffmpeg_cmd.extend(["-an"])  # No audio
        else:
            # Add audio codec and bitrate
            ffmpeg_cmd.extend(["-c:a", params["audio_codec"], "-b:a", params["audio_bitrate"]])

        # Add format-specific parameters
        ffmpeg_cmd.extend(params["additional_params"])

        # Add output file
        ffmpeg_cmd.append(output_path)

        logger.debug(f"FFmpeg command: {' '.join(ffmpeg_cmd)}")

        try:
            result = subprocess.run(ffmpeg_cmd, capture_output=True, text=True, timeout=timeout)

            if result.returncode != 0:
                logger.error(f"Conversion failed: {result.stderr}")
                raise subprocess.CalledProcessError(result.returncode, ffmpeg_cmd, result.stderr)

            logger.info(f"Successfully converted {input_path} to {output_path}")
            return output_path

        except FileNotFoundError:
            # This should not happen since we check ffmpeg availability above
            raise RuntimeError("ffmpeg command not found. Please ensure ffmpeg is properly installed.")
        except subprocess.TimeoutExpired:
            logger.error(f"Conversion timed out after {timeout} seconds")
            raise
        except subprocess.CalledProcessError as e:
            logger.error(f"ffmpeg conversion failed with return code {e.returncode}")
            raise

    def convert_to_format(
        self,
        input_path: str,
        output_format: VideoFormat,
        quality: Optional[int] = None,
        timeout: Optional[int] = None,
        output_dir: Optional[str] = None,
        remove_audio: bool = False,
    ) -> str:
        """
        Convert a video to a specific format with automatic output path generation.

        Args:
            input_path: Path to the input video
            output_format: Target video format
            quality: Quality of the output video (lower = better quality, typically 18-28)
            timeout: Timeout in seconds
            output_dir: Output directory (uses input directory if None)
            remove_audio: If True, removes audio from the output video (reduces file size)
        Returns:
            Path to the converted video file

        Raises:
            FileNotFoundError: If the input file does not exist
            ValueError: If input or output format is not supported
            subprocess.TimeoutExpired: If the conversion takes too long
            subprocess.CalledProcessError: If the conversion fails
        """
        input_path_obj = Path(input_path)

        if output_dir is None:
            output_dir = input_path_obj.parent
        else:
            output_dir = Path(output_dir)

        # Generate output filename
        output_filename = input_path_obj.stem + "." + output_format.value
        output_path = output_dir / output_filename

        return self.convert_video(
            input_path=str(input_path), output_path=str(output_path), quality=quality, timeout=timeout, output_format=output_format, remove_audio=remove_audio
        )

    def convert_to_mp4(
        self,
        input_path: str,
        quality: Optional[int] = None,
        timeout: Optional[int] = None,
        output_dir: Optional[str] = None,
        output_filename: Optional[str] = None,
        remove_audio: bool = False,
    ) -> str:
        """
        Convert any video format to MP4 (default conversion method).

        Args:
            input_path: Path to the input video (any supported format)
            quality: Quality of the output video (lower = better quality, typically 18-28)
            timeout: Timeout in seconds
            output_dir: Output directory (uses input directory if None)
            output_filename: Custom output filename without extension (uses input name if None)
            remove_audio: If True, removes audio from the output video (reduces file size)

        Returns:
            Path to the converted MP4 file

        Raises:
            FileNotFoundError: If the input file does not exist
            ValueError: If input format is not supported
            subprocess.TimeoutExpired: If the conversion takes too long
            subprocess.CalledProcessError: If the conversion fails
        """
        input_path_obj = Path(input_path)

        # Generate output filename
        if output_filename is None:
            output_filename = input_path_obj.stem

        if output_dir is None:
            output_dir = input_path_obj.parent
        else:
            output_dir = Path(output_dir)

        output_path = output_dir / f"{output_filename}_converted.mp4"

        # Check if input is already MP4
        input_format = self.get_video_format(input_path)
        if input_format == VideoFormat.mp4:
            if remove_audio:
                return self.remove_audio_from_video(input_path=input_path, output_path=output_path, timeout=timeout)
            logger.info(f"Input file {input_path} is already MP4 format")
            return input_path

        # Handle unsupported formats by attempting conversion anyway
        if input_format is None:
            logger.warning(f"Unsupported input format for file: {input_path}. Attempting to convert to MP4 using ffmpeg auto-detection.")

        logger.info(f"Converting {input_path} to MP4 format with H.264 codec for browser compatibility")

        return self.convert_video(input_path=input_path, output_path=str(output_path), quality=quality, timeout=timeout, output_format=VideoFormat.mp4, remove_audio=remove_audio)

    def batch_convert_to_mp4(
        self,
        input_files: list[str],
        quality: Optional[int] = None,
        timeout: Optional[int] = None,
        output_dir: Optional[str] = None,
        remove_audio: bool = False,
    ) -> list[str]:
        """
        Convert multiple video files to MP4 format.

        Args:
            input_files: List of input video file paths
            quality: Quality of the output videos (lower = better quality, typically 18-28)
            timeout: Timeout in seconds per conversion
            output_dir: Output directory (uses input directory if None)
            remove_audio: If True, removes audio from the output videos (reduces file size)
        Returns:
            List of paths to the converted MP4 files

        Raises:
            FileNotFoundError: If any input file does not exist
            ValueError: If any input format is not supported
            subprocess.TimeoutExpired: If any conversion takes too long
            subprocess.CalledProcessError: If any conversion fails
        """
        converted_files = []

        for input_file in input_files:
            try:
                # Try the standard conversion first
                output_path = self.convert_to_mp4(input_path=input_file, quality=quality, timeout=timeout, output_dir=output_dir, remove_audio=remove_audio)
                converted_files.append(output_path)
                logger.info(f"Successfully converted: {input_file} -> {output_path}")
            except ValueError as e:
                # If format is not supported, try the fallback method
                if "Unsupported input format" in str(e):
                    logger.warning(f"Standard conversion failed for {input_file}, trying fallback method...")
                    try:
                        output_path = self.convert_any_to_mp4(input_path=input_file, quality=quality, timeout=timeout, output_dir=output_dir, remove_audio=remove_audio)
                        converted_files.append(output_path)
                        logger.info(f"Successfully converted (fallback): {input_file} -> {output_path}")
                    except Exception as fallback_e:
                        logger.error(f"Fallback conversion also failed for {input_file}: {fallback_e}")
                        raise
                else:
                    logger.error(f"Failed to convert {input_file}: {e}")
                    raise
            except Exception as e:
                logger.error(f"Failed to convert {input_file}: {e}")
                raise

        return converted_files

    def convert_any_to_mp4(
        self,
        input_path: str,
        quality: Optional[int] = None,
        timeout: Optional[int] = None,
        output_dir: Optional[str] = None,
        output_filename: Optional[str] = None,
        remove_audio: bool = False,
    ) -> str:
        """
        Convert any video file to MP4 format, even if the input format is not in the VideoFormat enum.

        This method attempts to convert any video file to MP4 using ffmpeg's auto-detection,
        regardless of whether the input format is officially supported by this class.

        Args:
            input_path: Path to the input video (any format that ffmpeg can read)
            quality: Quality of the output video (lower = better quality, typically 18-28)
            timeout: Timeout in seconds
            output_dir: Output directory (uses input directory if None)
            output_filename: Custom output filename without extension (uses input name if None)
            remove_audio: If True, removes audio from the output video (reduces file size)

        Returns:
            Path to the converted MP4 file

        Raises:
            FileNotFoundError: If the input file does not exist
            subprocess.TimeoutExpired: If the conversion takes too long
            subprocess.CalledProcessError: If the conversion fails
        """
        input_path_obj = Path(input_path)

        # Determine output directory
        if output_dir is None:
            output_dir = input_path_obj.parent
        else:
            output_dir = Path(output_dir)

        # Generate output filename
        if output_filename is None:
            output_filename = input_path_obj.stem
        output_path = output_dir / f"{output_filename}_converted.mp4"

        logger.info(f"Converting {input_path} to MP4 format with H.264 codec (input format auto-detected)")

        # Use convert_video with input_format=None to let ffmpeg auto-detect
        return self.convert_video(
            input_path=input_path,
            output_path=str(output_path),
            quality=quality,
            timeout=timeout,
            input_format=None,  # Let ffmpeg auto-detect
            output_format=VideoFormat.mp4,
            remove_audio=remove_audio,
        )

    def remove_audio_from_video(self, input_path: str, output_path: Optional[str] = None, timeout: Optional[int] = None) -> str:
        """
        Remove audio from a video file while keeping the video unchanged.

        This method is useful for reducing file size when audio is not needed.
        The output format will be the same as the input format.

        Args:
            input_path: Path to the input video file
            output_path: Path for the output video (auto-generated if None)
            timeout: Timeout in seconds for the operation

        Returns:
            Path to the output video file with audio removed

        Raises:
            FileNotFoundError: If the input file does not exist
            FileExistsError: If the output file already exists
            ValueError: If input format is not supported
            subprocess.TimeoutExpired: If the operation takes too long
            subprocess.CalledProcessError: If the operation fails
        """
        input_path_obj = Path(input_path)

        if not os.path.exists(input_path):
            raise FileNotFoundError(f"Input file {input_path} does not exist")

        # Auto-generate output path if not provided
        if output_path is None:
            output_path = str(input_path_obj.parent / f"{input_path_obj.stem}_no_audio{input_path_obj.suffix}")

        if os.path.exists(output_path):
            raise FileExistsError(f"Output file {output_path} already exists")

        # Get input format
        input_format = self.get_video_format(input_path)
        if input_format is None:
            logger.warning(f"Unsupported input format for file: {input_path}. Attempting to remove audio using ffmpeg auto-detection.")
            # Continue with None format to let ffmpeg auto-detect

        format_info = f" (format: {input_format.value})" if input_format else " (format: auto-detected)"
        logger.info(f"Removing audio from {input_path}{format_info}")

        # Build ffmpeg command to copy video without audio
        ffmpeg_cmd = [
            "ffmpeg",
            "-i",
            input_path,
            "-c:v",
            "copy",  # Copy video stream without re-encoding
            "-an",  # No audio
            output_path,
        ]

        logger.debug(f"FFmpeg command: {' '.join(ffmpeg_cmd)}")

        try:
            result = subprocess.run(ffmpeg_cmd, capture_output=True, text=True, timeout=timeout or self.default_timeout)

            if result.returncode != 0:
                logger.error(f"Audio removal failed: {result.stderr}")
                raise subprocess.CalledProcessError(result.returncode, ffmpeg_cmd, result.stderr)

            logger.info(f"Successfully removed audio from {input_path} -> {output_path}")
            return output_path

        except subprocess.TimeoutExpired:
            logger.error(f"Audio removal timed out after {timeout or self.default_timeout} seconds")
            raise
        except subprocess.CalledProcessError as e:
            logger.error(f"ffmpeg audio removal failed with return code {e.returncode}")
            raise

    def is_web_compatible_video(self, video_path: str) -> bool:
        """
        Check if a video is compatible with web browsers.

        Web browsers require specific container formats AND codecs for HTML5 video playback:
        - MP4 container with H.264 video codec (widest browser support)
        - WebM container with VP8/VP9 video codec

        Args:
            video_path: Path to the video file

        Returns:
            True if video is web-compatible, False otherwise
        """
        try:
            # Get both format and stream info
            cmd = ["ffprobe", "-v", "quiet", "-print_format", "json", "-show_format", "-show_streams", video_path]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)

            if result.returncode != 0:
                logger.warning(f"ffprobe failed for {video_path}: {result.stderr}")
                return False

            import json

            data = json.loads(result.stdout)

            # Check container format first - browsers only support specific containers
            format_name = data.get("format", {}).get("format_name", "").lower()
            # ffprobe may return multiple formats separated by comma (e.g., "mov,mp4,m4a,3gp,3g2,mj2")
            format_names = [f.strip() for f in format_name.split(",")]

            # Web-compatible container formats
            mp4_formats = {"mov", "mp4", "m4a", "m4v"}
            webm_formats = {"webm", "matroska"}  # matroska is the underlying format for webm

            is_mp4_container = bool(mp4_formats & set(format_names))
            is_webm_container = bool(webm_formats & set(format_names))

            if not is_mp4_container and not is_webm_container:
                logger.debug(f"Video {video_path} has non-web-compatible container format: {format_name}")
                return False

            # Find video stream and check codec
            for stream in data.get("streams", []):
                if stream.get("codec_type") == "video":
                    codec = stream.get("codec_name", "")
                    pixel_format = stream.get("pix_fmt", "")

                    # MP4 container requires H.264 codec with compatible pixel format
                    if is_mp4_container:
                        if codec == "h264" and pixel_format in ["yuv420p", "yuv422p", "yuv444p"]:
                            logger.debug(f"Video {video_path} is web-compatible (MP4/H.264, {pixel_format})")
                            return True
                        else:
                            logger.debug(f"Video {video_path} has MP4 container but incompatible codec/pixel format (codec: {codec}, pixel_format: {pixel_format})")
                            return False

                    # WebM container requires VP8 or VP9 codec
                    if is_webm_container:
                        if codec in ["vp8", "vp9"]:
                            logger.debug(f"Video {video_path} is web-compatible (WebM/{codec})")
                            return True
                        else:
                            logger.debug(f"Video {video_path} has WebM container but incompatible codec: {codec}")
                            return False

            logger.warning(f"No video stream found in {video_path}")
            return False

        except (subprocess.TimeoutExpired, json.JSONDecodeError, Exception) as e:
            logger.warning(f"Error checking web compatibility for {video_path}: {e}")
            return False

    def convert_to_web_compatible(
        self,
        input_path: str,
        output_path: Optional[str] = None,
        quality: Optional[int] = None,
        timeout: Optional[int] = None,
    ) -> str:
        """
        Convert a video to web-compatible H.264 format.

        This method ensures the output video can be played in web browsers by converting
        to H.264 codec with web-compatible pixel format.

        Args:
            input_path: Path to input video
            output_path: Path for output video (auto-generated if None)
            quality: Quality setting for conversion (default: 23)
            timeout: Timeout in seconds for conversion

        Returns:
            Path to web-compatible video

        Raises:
            FileNotFoundError: If the input file does not exist
            ValueError: If the video is already web-compatible
            subprocess.TimeoutExpired: If the conversion takes too long
            subprocess.CalledProcessError: If the conversion fails
        """
        if not os.path.exists(input_path):
            raise FileNotFoundError(f"Input file {input_path} does not exist")

        # Check if already web-compatible
        if self.is_web_compatible_video(input_path):
            logger.info(f"Video {input_path} is already web-compatible")
            return input_path

        # Generate output path if not provided
        if output_path is None:
            input_path_obj = Path(input_path)
            output_path = str(input_path_obj.parent / f"{input_path_obj.stem}_web_compatible.mp4")

        # Use default quality if not provided
        quality = quality if quality is not None else self.default_quality

        logger.info(f"Converting {input_path} to web-compatible H.264 format")

        # Convert to H.264 MP4 with web-optimized settings
        converted_path = self.convert_video(
            input_path=input_path,
            output_path=output_path,
            quality=quality,
            timeout=timeout,
            output_format=VideoFormat.mp4,
            remove_audio=False,
        )

        logger.info(f"Successfully converted to web-compatible format: {converted_path}")
        return converted_path

    def ensure_web_compatibility(
        self,
        video_path: str,
        output_path: Optional[str] = None,
        quality: Optional[int] = None,
        timeout: Optional[int] = None,
    ) -> str:
        """
        Ensure a video is web-compatible, converting if necessary.

        This is a convenience method that checks compatibility and converts only if needed.

        Args:
            video_path: Path to the video file
            output_path: Path for output video (auto-generated if None)
            quality: Quality setting for conversion (default: 23)
            timeout: Timeout in seconds for conversion

        Returns:
            Path to web-compatible video (original path if already compatible)
        """
        if self.is_web_compatible_video(video_path):
            return video_path

        return self.convert_to_web_compatible(
            input_path=video_path,
            output_path=output_path,
            quality=quality,
            timeout=timeout,
        )
