"""
Unified IO Module

Provides a unified interface for reading/writing files across different platforms:
- AWS S3 (s3://)
- Google Cloud Storage (gs://)
- HTTP/HTTPS URLs (http://, https://)
- Local files (file paths without URI scheme)

Automatically detects the appropriate backend based on URI scheme.

License: BSD 3-Clause
"""

from typing import Dict, Optional, Any, Union
from urllib.parse import urlparse
from cocina.printer import Printer
from soundhub_utils.io import aws, gcs, url, local
printer = Printer()


def _platform_handler(uri: str, platform_name: Optional[str] = None):
    """
    Determine the appropriate platform handler based on URI or explicit platform name.
    
    Args:
        uri: The URI to read from
        platform_name: Optional explicit platform ('aws', 'gcs', 'url', 'local')
        
    Returns:
        The appropriate platform module (aws, gcs, url, or local)
        
    Raises:
        ValueError: If URI scheme is not supported or platform_name is invalid
    """
    if platform_name:
        platform_map = {
            'aws': aws,
            'gcs': gcs, 
            'url': url,
            'local': local
        }
        if platform_name not in platform_map:
            raise ValueError(f"Unsupported platform: {platform_name}. Must be one of: {list(platform_map.keys())}")
        return platform_map[platform_name]
    
    # Auto-detect from URI scheme
    parsed = urlparse(uri)
    scheme = parsed.scheme.lower()
    
    if scheme == 's3':
        return aws
    elif scheme == 'gs':
        return gcs
    elif scheme in ['http', 'https']:
        return url
    elif scheme == '' or scheme == 'file':
        # No scheme or file:// scheme -> local file
        return local
    else:
        raise ValueError(f"Unsupported URI scheme: {scheme}. Supported schemes: s3, gs, http, https, file, or no scheme for local files")


def extract_flac_header(
    src: str, 
    platform_name: Optional[str] = None
) -> Dict:
    """
    Extract FLAC header metadata from a remote file.
    
    Automatically detects the appropriate platform (aws, gcs, url) based on URI scheme,
    or uses explicit platform_name if provided.
    
    Args:
        src: URI of the FLAC file (s3://, gs://, http://, https://, file://, or local path)
        platform_name: Optional explicit platform ('aws', 'gcs', 'url', 'local')
        
    Returns:
        Dictionary containing FLAC metadata including sample_rate, channels, 
        total_samples, duration, etc.
        
    Raises:
        ValueError: If URI scheme is not supported
        Exception: Platform-specific errors (network, authentication, etc.)
    """
    handler = _platform_handler(src, platform_name)
    printer.message(f"Using {handler.__name__} handler for extract_flac_header: {src}")
    return handler.extract_flac_header(src)


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,
    platform_name: Optional[str] = None
) -> Union[str, Any]:
    """
    Read a file from remote storage. 
    
    Automatically detects the appropriate platform (aws, gcs, url) based on URI scheme,
    or uses explicit platform_name if provided.
    
    Args:
        src: URI of the file to read (s3://, gs://, http://, https://, file://, or local path)
        dest: Optional local destination path. If None, returns response object
        start_byte: Optional start byte for partial reads (HTTP Range request)
        end_byte: Optional end byte for partial reads (HTTP Range request)
        create_parents: Create parent directories if they don't exist
        overwrite: Overwrite destination file if it exists
        client: Platform-specific client object
        platform_name: Optional explicit platform ('aws', 'gcs', 'url', 'local')
        
    Returns:
        If dest is provided: path to the saved file (str)
        If dest is None: response object from the platform
        
    Raises:
        ValueError: If URI scheme is not supported
        Exception: Platform-specific errors (network, authentication, etc.)
    """
    handler = _platform_handler(src, platform_name)
    printer.message(f"Using {handler.__name__} handler for read: {src}")
    
    return handler.read(
        src=src,
        dest=dest,
        start_byte=start_byte,
        end_byte=end_byte,
        create_parents=create_parents,
        overwrite=overwrite,
        client=client
    )


def read_flac(
    src: str,
    dest: Optional[str] = None,
    start_byte: Optional[int] = None,
    end_byte: Optional[int] = None,
    start_time: float = None,
    end_time: Optional[float] = None,
    duration: Optional[float] = None,
    create_parents: bool = True,
    overwrite: bool = True,
    client: Optional[Any] = None,
    platform_name: Optional[str] = None
) -> Union[str, Any]:
    """
    FIX ME: convience wrapper for `read` or `read_partial_flac` based on arguments.
    NOTE: read_partial_flac is flac specific. read can generically read anything.  that said only 
    use this for flac files. if you are wanting a generic read - explictly use read.
    """
    if (start_time or end_time or duration):
        return read_partial_flac(        
            src=src,
            dest=dest,
            start_time=start_time,
            end_time=end_time,
            duration=duration,
            create_parents=create_parents,
            overwrite=overwrite
        )
    else:
        return read(
            src=src,
            dest=dest,
            start_byte=start_byte,
            end_byte=end_byte,
            create_parents=create_parents,
            overwrite=overwrite,
            client=client
        )


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,
    platform_name: Optional[str] = None
) -> str:
    """
    Download a specific time range from a remote FLAC file.
    
    Automatically detects the appropriate platform (aws, gcs, url) based on URI scheme,
    or uses explicit platform_name if provided.
    
    Args:
        src: URI of the FLAC file (s3://, gs://, http://, https://, file://, or local path)
        dest: Local destination path for the downloaded chunk
        start_time: Start time in seconds
        end_time: End time in seconds (optional)
        duration: Duration in seconds (optional)
        create_parents: Create parent directories if they don't exist
        overwrite: Overwrite destination file if it exists
        platform_name: Optional explicit platform ('aws', 'gcs', 'url', 'local')
        
    Returns:
        Path to the downloaded FLAC file
        
    Raises:
        ValueError: If URI scheme is not supported or invalid time parameters
        Exception: Platform-specific errors (network, authentication, etc.)
    """
    handler = _platform_handler(src, platform_name)
    printer.message(f"Using {handler.__name__} handler for read_partial_flac: {src}")
    
    return handler.read_partial_flac(
        src=src,
        dest=dest,
        start_time=start_time,
        end_time=end_time,
        duration=duration,
        create_parents=create_parents,
        overwrite=overwrite
    )