"""
Shared IO Functions

Common functionality shared across different storage backends (AWS, GCS, URL)

License: BSD 3-Clause
"""

from pathlib import Path
from typing import Callable
from cocina.printer import Printer
from soundhub_utils.utils import flac

printer = Printer()


def download_and_extract_partial_flac(
    src: str,
    dest: str,
    start_time: float,
    end_time: float,
    duration: float,
    start_byte: int,
    end_byte: int,
    create_parents: bool,
    overwrite: bool,
    download_range_func: Callable[[str, int, int], bytes]
) -> str:
    """
    Common logic for downloading partial FLAC file using byte range and extracting time segment.

    This function encapsulates the shared logic across AWS, GCS, and URL implementations:
    1. Setup destination path
    2. Download byte range to memory
    3. Calculate padded start time
    4. Extract audio segment using flac utilities
    5. Export to destination

    Args:
        src: Source URI (s3://, gs://, or http(s)://)
        dest: Local destination path for the downloaded chunk
        start_time: Start time in seconds
        end_time: End time in seconds
        duration: Duration in seconds
        start_byte: Start byte for range request
        end_byte: End byte for range request
        create_parents: Create parent directories if they don't exist
        overwrite: Overwrite destination file if it exists
        download_range_func: Platform-specific function to download byte range
                            Should accept (src, start_byte, end_byte) and return bytes

    Returns:
        Path to the downloaded FLAC file (dest)

    Raises:
        FileExistsError: If destination exists and overwrite=False
        Exception: Platform-specific download or processing errors
    """
    # 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")

    try:
        # Download byte range to memory using platform-specific function
        audio_data = download_range_func(src, start_byte, end_byte)

        # Calculate the padded start time
        padding_time = duration * 0.25
        padded_start = max(0, start_time - padding_time)

        # Use flac utility to extract audio segment
        chunk = flac.extract_audio_segment(
            audio_data, start_time, end_time, use_range=True, padded_start=padded_start
        )

        # Export the chunk as FLAC
        chunk.export(str(dest_path), format="flac")

        return str(dest_path)

    except Exception as e:
        printer.error(e, f"Error processing {src}")
        raise


def validate_and_calculate_time_range(
    start_time: float,
    end_time: float,
    duration: float,
    total_duration: float
) -> tuple[float, float, float]:
    """
    Validate and calculate time range parameters for partial FLAC downloads.

    Args:
        start_time: Start time in seconds
        end_time: End time in seconds (may be None)
        duration: Duration in seconds (may be None)
        total_duration: Total duration of the audio file

    Returns:
        Tuple of (start_time, end_time, duration) with validated/calculated values

    Raises:
        ValueError: If time parameters are invalid or out of range
    """
    # Calculate end_time if not provided
    if end_time is None:
        if duration is None:
            raise ValueError("Must specify either end_time or duration")
        end_time = start_time + duration
    elif duration is None:
        duration = end_time - start_time

    # Validate time parameters
    if start_time < 0 or start_time >= total_duration:
        raise ValueError(f"start_time {start_time} is out of range [0, {total_duration})")

    if end_time > total_duration:
        printer.message(f"end_time {end_time} exceeds file duration {total_duration}, clipping")
        end_time = total_duration
        duration = end_time - start_time

    return start_time, end_time, duration
