"""
Local File IO Module

Utilities for reading and processing local FLAC files using the same API as remote modules

License: BSD 3-Clause
"""

from pathlib import Path
from typing import Dict, Optional, Union, Any

from pydub import AudioSegment

from cocina.printer import Printer
from soundhub_utils.utils import flac

# Setup logging
printer = Printer()

def extract_flac_header(src: str) -> Dict:
    """
    Extract FLAC header metadata from a local file.
    
    Args:
        src: Local file path to FLAC file
        
    Returns:
        Dict containing duration, sample_rate, channels, total_samples
    """
    src_path = Path(src)
    
    if not src_path.exists():
        raise FileNotFoundError(f"Local file not found: {src}")
    
    if not src_path.is_file():
        raise ValueError(f"Path is not a file: {src}")
    
    try:
        # Read first 4KB for header parsing
        with open(src_path, 'rb') as f:
            header_data = f.read(4096)
        
        # Use flac utility to parse header
        return flac.extract_header(header_data)
        
    except Exception as e:
        printer.error(e, f"Error parsing FLAC header from {src}")
        raise


def read(
    src: str,
    dest: Optional[str] = None,
    start_byte: Optional[int] = None,
    end_byte: Optional[int] = None,
    create_parents: bool = True,
    overwrite: bool = True,
    client: Optional[Any] = None
) -> Union[str, bytes]:
    """
    Read a local file.
    
    Args:
        src: Local file path
        dest: Optional destination path (if None, return file bytes)
        start_byte: Start byte position for partial reads
        end_byte: End byte position for partial reads
        create_parents: Create parent directories when writing files
        overwrite: Overwrite existing destination file
        client: Unused for local files (maintains API compatibility)
        
    Returns:
        str (destination file path) if dest is provided, otherwise file bytes
    """
    src_path = Path(src)
    
    if not src_path.exists():
        raise FileNotFoundError(f"Local file not found: {src}")
    
    if not src_path.is_file():
        raise ValueError(f"Path is not a file: {src}")
    
    try:
        # Read file (optionally with byte range)
        with open(src_path, 'rb') as f:
            if start_byte is not None:
                f.seek(start_byte)
            
            if start_byte is not None and end_byte is not None:
                data = f.read(end_byte - start_byte + 1)
            elif end_byte is not None:
                data = f.read(end_byte + 1)
            else:
                data = f.read()
        
        # If no destination specified, return the data
        if dest is None:
            return data
        
        # Setup destination path
        dest_path = Path(dest)
        if create_parents:
            dest_path.parent.mkdir(parents=True, exist_ok=True)
        
        if dest_path.exists() and not overwrite:
            raise FileExistsError(f"Destination file {dest} already exists and overwrite=False")
        
        # Write to destination
        with open(dest_path, 'wb') as f:
            f.write(data)
        
        printer.message(f"Copied {src} to {dest_path}")
        return str(dest_path)
        
    except Exception as e:
        printer.error(e, f"Error reading local file {src}")
        raise


def read_partial_flac(
    src: str,
    dest: str,
    start_time: float = None,
    end_time: Optional[float] = None,
    duration: Optional[float] = None,
    create_parents: bool = True,
    overwrite: bool = True
) -> str:
    """
    Read a part of a local FLAC file.
    
    Args:
        src: Local path to FLAC file
        dest: Local file destination
        start_time: Start time in seconds (default: 0)
        end_time: End time in seconds (if None, use start_time + duration)
        duration: Duration in seconds (ignored if end_time is provided)
        create_parents: Create parent directories if needed
        overwrite: Overwrite existing destination file
        
    Returns:
        Path to the saved file (dest)
    """
    # Validate inputs
    if start_time is None:
        start_time = 0.0
    
    if end_time is None:
        if duration is None:
            raise ValueError("Either end_time or duration must be provided")
        end_time = start_time + duration
    
    if start_time < 0:
        raise ValueError("start_time must be >= 0")
    
    if end_time <= start_time:
        raise ValueError("end_time must be > start_time")
    
    src_path = Path(src)
    if not src_path.exists():
        raise FileNotFoundError(f"Local file not found: {src}")
    
    if not src_path.is_file():
        raise ValueError(f"Path is not a file: {src}")
    
    # Setup destination path
    dest_path = Path(dest)
    if create_parents:
        dest_path.parent.mkdir(parents=True, exist_ok=True)
    
    if dest_path.exists() and not overwrite:
        raise FileExistsError(f"Destination file {dest} already exists and overwrite=False")
    
    printer.message(f"Reading FLAC partial: {src} -> {dest}, time range: {start_time:.1f}-{end_time:.1f}s")
    
    try:
        # Get file metadata
        header_info = extract_flac_header(src)
        total_duration = header_info['duration']
        
        # Validate time range
        if start_time >= total_duration:
            raise ValueError(f"start_time {start_time} >= file duration {total_duration}")
        
        end_time = min(end_time, total_duration)
        
        printer.message(f"File info: duration={total_duration:.1f}s")
        
        # Use flac utility to extract time range directly from local file
        return flac.extract_time_range(
            src_path=str(src_path),
            dest=dest,
            start_time=start_time,
            end_time=end_time,
            create_parents=create_parents,
            overwrite=overwrite
        )
    
    except Exception as e:
        printer.error(e, f"Error processing local file {src}")
        raise


#
# INTERNAL
#

def _get_file_size(file_path: str) -> int:
    """Get file size for a local file."""
    return Path(file_path).stat().st_size