#!/usr/bin/env python3
"""
Audio Converter Module for iPod Nano Transfer Tool
Handles converting audio files to iPod-compatible formats
"""

import os
import logging
import subprocess
from typing import Optional, Dict, Any
from pathlib import Path

from track_info import TrackInfo

# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

CONVERSION_TIMEOUT = 300  # 5 minutes max per track


class AudioConverter:
    """Class to handle audio conversion to iPod-compatible formats"""
    
    def __init__(self, output_dir: str = "converted"):
        """
        Initialize the audio converter
        
        Args:
            output_dir: Directory to save converted files
        """
        self.output_dir = output_dir
        os.makedirs(output_dir, exist_ok=True)
        
        # Check if ffmpeg is installed
        try:
            subprocess.run(["ffmpeg", "-version"], capture_output=True, check=True)
        except (subprocess.SubprocessError, FileNotFoundError):
            logger.error("FFmpeg is not installed or not in PATH. Please install FFmpeg.")
            raise RuntimeError("FFmpeg is required but not found")
    
    def convert_to_ipod_format(self, 
                              track_info: TrackInfo, 
                              input_file: str,
                               format: str = "mp3", 
                              audio_bitrate: int = 256,
                              overwrite: bool = False) -> str:
        """
        Convert audio file to iPod-compatible format
        
        Args:
            track_info: TrackInfo object with track metadata
            input_file: Path to input audio file
            format: Output format (m4a, mp3)
            audio_bitrate: Audio bitrate in kbps
            overwrite: Whether to overwrite existing files
            
        Returns:
            Path to converted file
        """
        logger.info(f"Converting {input_file} to iPod-compatible {format}")
        
        # Create output directory structure
        if track_info.playlist_title:
            artist_dir = os.path.join(self.output_dir, self._sanitize_filename(track_info.artist))
            album_dir = os.path.join(artist_dir, self._sanitize_filename(track_info.playlist_title))
            os.makedirs(album_dir, exist_ok=True)
            
            if track_info.track_number:
                filename = f"{track_info.track_number:02d} - {self._sanitize_filename(track_info.title)}.{format}"
            else:
                filename = f"{self._sanitize_filename(track_info.title)}.{format}"
                
            output_file = os.path.join(album_dir, filename)
        else:
            artist_dir = os.path.join(self.output_dir, self._sanitize_filename(track_info.artist))
            album_dir = os.path.join(artist_dir, self._sanitize_filename(track_info.album or track_info.artist))
            os.makedirs(album_dir, exist_ok=True)
            
            filename = f"{self._sanitize_filename(track_info.title)}.{format}"
            output_file = os.path.join(album_dir, filename)
        
        # Check if file already exists — but validate it's not empty/corrupt
        if os.path.exists(output_file) and not overwrite:
            file_size = os.path.getsize(output_file)
            if file_size > 1024:  # Minimum reasonable size for audio
                logger.info(f"File already exists: {output_file} ({file_size} bytes)")
                return output_file
            else:
                logger.warning(f"Existing file is too small ({file_size} bytes), re-converting")
                os.remove(output_file)
        
        if format == "m4a":
            # iPod Nano 7 AAC requirements:
            #   - AAC-LC profile only (HE-AAC causes playback issues/crashes)
            #   - 44100 Hz sample rate (firmware expects this exactly)
            #   - Max 160 kbps for stereo (higher may cause truncation/crackle)
            #   - +global_header: required for iPod AAC decoder init
            #   - +genpts: fix timestamp gaps from web sources
            m4a_bitrate = min(audio_bitrate, 160)
            if audio_bitrate > 160:
                logger.warning(
                    "Bitrate %d kbps exceeds iPod Nano 7 max (160 kbps), clamping to 160",
                    audio_bitrate,
                )
            cmd = [
                "ffmpeg", "-y", "-fflags", "+genpts", "-i", input_file,
                "-c:a", "aac",
                "-b:a", f"{m4a_bitrate}k",
                "-ar", "44100",
                "-ac", "2",
                "-flags", "+global_header",
            ]
            # Try libfdk_aac first (best compatibility)
            fdkaac_cmd = cmd + [
                "-map", "0:a",
                "-map", "0:v?",
                "-c:v", "copy",
                "-map", "0:t?",
                "-c:a", "libfdk_aac",
                "-profile:a", "aac_low",
                "-movflags", "+faststart",
                "-map_metadata", "0",
                "-loglevel", "warning",
                output_file,
            ]
            try:
                test = subprocess.run(
                    ["ffmpeg", "-hide_banner", "-encoders"],
                    capture_output=True, text=True, timeout=5
                )
                if "libfdk_aac" in test.stdout:
                    cmd = fdkaac_cmd
                else:
                    cmd += [
                        "-map", "0:a",
                        "-map", "0:v?",
                        "-c:v", "copy",
                        "-map", "0:t?",
                        "-profile:a", "aac_low",
                        "-movflags", "+faststart",
                        "-map_metadata", "0",
                        "-loglevel", "warning",
                        output_file,
                    ]
            except Exception:
                cmd += [
                    "-map", "0:a",
                    "-map", "0:v?",
                    "-c:v", "copy",
                    "-map", "0:t?",
                    "-profile:a", "aac_low",
                    "-movflags", "+faststart",
                    "-map_metadata", "0",
                    "-loglevel", "warning",
                    output_file,
                ]
        else:
            # MP3 for iPod Nano 7G: reliable, no special decoder flags needed.
            # libmp3lame is mature and produces streams safe for hardware decoders.
            # No +global_header — that flag is AAC-only and confuses MP3 playback.
            cmd = [
                "ffmpeg", "-y", "-i", input_file,
                "-c:a", "libmp3lame",
                "-b:a", f"{audio_bitrate}k",
                "-ar", "44100",
                "-ac", "2",
                "-bitexact",
                "-map", "0:a",
                "-map", "0:t?",
                "-id3v2_version", "3",
                "-map_metadata", "0",
                "-loglevel", "warning",
                output_file,
            ]
        
        logger.debug(f"Running: {' '.join(cmd)}")
        
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                timeout=CONVERSION_TIMEOUT,
                text=True
            )
            
            if result.returncode != 0:
                stderr = result.stderr.strip()
                logger.error(f"FFmpeg failed: {stderr}")
                raise RuntimeError(f"FFmpeg conversion failed: {stderr}")
            
            if not os.path.exists(output_file):
                raise RuntimeError("FFmpeg completed but output file not found")
            
            file_size = os.path.getsize(output_file)
            if file_size < 1024:
                os.remove(output_file)
                raise RuntimeError(f"FFmpeg produced empty/corrupt file ({file_size} bytes)")
            
            logger.info(f"Conversion complete: {output_file} ({file_size} bytes)")
            return output_file
            
        except subprocess.TimeoutExpired:
            logger.error(f"FFmpeg timed out after {CONVERSION_TIMEOUT}s on {input_file}")
            raise RuntimeError(f"Conversion timed out after {CONVERSION_TIMEOUT}s")
    
    def convert_video_for_ipod(self,
                              input_file: str,
                              output_file: Optional[str] = None,
                              resolution: str = "640x480",
                              video_bitrate: int = 1500,
                              audio_bitrate: int = 256,
                              overwrite: bool = False) -> str:
        """
        Convert video file to iPod-compatible format
        
        Args:
            input_file: Path to input video file
            output_file: Path to output file (optional)
            resolution: Video resolution (max 640x480 for iPod Nano)
            video_bitrate: Video bitrate in kbps
            audio_bitrate: Audio bitrate in kbps
            overwrite: Whether to overwrite existing files
            
        Returns:
            Path to converted file
        """
        logger.info(f"Converting video {input_file} to iPod-compatible format")
        
        if not output_file:
            input_path = Path(input_file)
            output_file = os.path.join(self.output_dir, f"{input_path.stem}_ipod.mp4")
        
        # Check if file already exists
        if os.path.exists(output_file) and not overwrite:
            logger.info(f"File already exists: {output_file}")
            return output_file
        
        # Build ffmpeg command
        cmd = [
            "ffmpeg", "-y", "-i", input_file,
            "-c:v", "h264",
            "-b:v", f"{video_bitrate}k",
            "-c:a", "aac",
            "-b:a", f"{audio_bitrate}k",
            "-vf", f"scale={resolution}",
            "-pix_fmt", "yuv420p",
            "-map_metadata", "0",
            "-loglevel", "error",
            output_file,
        ]
        
        logger.debug(f"Running: {' '.join(cmd)}")
        
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                timeout=CONVERSION_TIMEOUT * 3,  # Video takes longer
                text=True
            )
            
            if result.returncode != 0:
                stderr = result.stderr.strip()
                logger.error(f"FFmpeg failed: {stderr}")
                raise RuntimeError(f"FFmpeg video conversion failed: {stderr}")
            
            if not os.path.exists(output_file):
                raise RuntimeError("FFmpeg completed but output file not found")
            
            logger.info(f"Video conversion complete: {output_file}")
            return output_file
            
        except subprocess.TimeoutExpired:
            logger.error(f"FFmpeg timed out after {CONVERSION_TIMEOUT * 3}s on {input_file}")
            raise RuntimeError(f"Video conversion timed out")
    
    def _sanitize_filename(self, filename: str) -> str:
        """
        Sanitize filename to be compatible with file systems
        
        Args:
            filename: Input filename
            
        Returns:
            Sanitized filename
        """
        # Replace invalid characters with underscore
        invalid_chars = '<>:"/\\|?*'
        for char in invalid_chars:
            filename = filename.replace(char, '_')
        
        # Limit length
        if len(filename) > 100:
            filename = filename[:97] + '...'
            
        return filename.strip()


if __name__ == "__main__":
    # Example usage
    converter = AudioConverter()
