"""
Spark Simplicity - Base Writer Utilities
========================================

Foundation utilities for all Spark DataFrame writers providing shared functionality
for temporary file management, distributed file operations, and cross-platform
compatibility. This module contains the core infrastructure used by all format-specific
writers to ensure consistent behavior and robust file handling.

Key Features:
    - **Temporary Directory Management**: Safe creation and cleanup of temporary directories
    - **Distributed File Operations**: Efficient handling of Spark's multi-file output
    - **Cross-Platform Compatibility**: Windows, Linux, and macOS file system support
    - **Atomic File Operations**: Safe file moving and renaming with error recovery
    - **Shared Storage Support**: NFS, HDFS, and cloud storage compatibility
    - **Resource Cleanup**: Automatic temporary resource cleanup with error handling

Core Functionality:
    - **Directory Creation**: Secure temporary directory creation with proper permissions
    - **File Renaming**: Intelligent file renaming for distributed Spark output
    - **Cleanup Operations**: Robust cleanup with retry logic for network filesystems
    - **Error Handling**: Comprehensive error recovery and logging

Usage:
    These utilities are used internally by all format-specific writers and are not
    intended for direct use by end users. They provide the foundation for reliable
    file operations across different storage systems and platforms.

    from .base_writer import _create_temp_directory, _rename_and_move_files, _cleanup_temp_directory
"""

import shutil
import uuid
from pathlib import Path
from typing import List, Union

from ...logger import get_logger

# Logger for writers
_writer_logger = get_logger("spark_simplicity.io.writers")


def _rename_and_move_files(
    part_files: List[Path], output_path: Path, file_type: str = "file"
) -> None:
    """
    Rename and move Spark part files to final destination with sequential numbering.

    Takes the distributed output files generated by Spark (typically named part-xxxxx)
    and renames them to a more user-friendly sequential format at the final destination.
    This operation is atomic per file and provides clear file identification for
    downstream processing systems that expect predictable file naming patterns.

    Args:
        part_files: List of Path objects pointing to Spark-generated part files
                   (e.g., part-00000-uuid.format, part-00001-uuid.format, etc.)
                   Files are automatically sorted before processing for consistent ordering.
        output_path: Base path template for the final output files. The stem (filename without
                    extension) becomes the prefix for numbered files. The directory structure
                    is created automatically if it doesn't exist.
        file_type: Format identifier used for logging and error messages. Also used as
                  default file extension if output_path doesn't specify one.
                  Examples: "CSV", "JSON", "Parquet"

    File Naming Pattern:
        Input files: part-00000-abc123.csv, part-00001-def456.csv
        Output files: output_1.csv, output_2.csv (sequential numbering starts at 1)

    Operation Characteristics:
        - Files are moved (not copied) for optimal performance and storage efficiency
        - Sequential numbering starts from 1 for human-friendly identification
        - Original file extensions are preserved when present in output_path
        - Operation is atomic per individual file with comprehensive error handling
        - Parent directories are created automatically with proper permissions
        - Files are processed in sorted order to ensure consistent output numbering

    Raises:
        RuntimeError: If no part files are provided for moving, or if any file move
                     operation fails due to permissions, disk space, or I/O errors.
                     The error includes specific details about which file failed.

    Example:
         part_files = [Path("tmp/part-00000.csv"), Path("tmp/part-00001.csv")]
         _rename_and_move_files(part_files, Path("output.csv"), "CSV")
        # Creates: output_1.csv, output_2.csv

    Note:
        This function is designed for distributed strategies where multiple output
        files are expected and desired. For single-file strategies, files should
        be moved directly to the target location without sequential renaming.
        The function provides detailed logging of the operation for monitoring.
    """
    if not part_files:
        raise RuntimeError(f"No {file_type} part files provided for moving")

    output_dir = output_path.parent
    file_stem = output_path.stem
    file_suffix = output_path.suffix or f".{file_type.lower()}"

    # Ensure output directory exists
    output_dir.mkdir(parents=True, exist_ok=True)

    # Sort part files and rename them sequentially
    for i, part_file in enumerate(sorted(part_files), 1):
        new_filename = f"{file_stem}_{i}{file_suffix}"
        new_path = output_dir / new_filename

        try:
            shutil.move(str(part_file), str(new_path))
        except OSError as e:
            raise RuntimeError(
                f"Failed to move {file_type} part file {part_file}: {str(e)}"
            ) from e

    _writer_logger.info(
        "Successfully created %d separate %s files: %s_1%s to %s_%d%s",
        len(part_files),
        file_type,
        file_stem,
        file_suffix,
        file_stem,
        len(part_files),
        file_suffix,
    )


def _create_temp_directory(shared_mount: Union[str, Path], prefix: str) -> Path:
    """
    Create a secure temporary directory for Spark distributed output operations.

    Creates a uniquely named temporary directory within the shared storage location
    accessible by all cluster nodes. This directory serves as the staging area for
    Spark's distributed write operations before files are moved to their final
    destination. Uses UUID-based naming to avoid conflicts between concurrent
    operations and different Spark applications.

    Args:
        shared_mount: Path to shared filesystem accessible by all cluster nodes
                     (driver and executors). Must be writable by Spark processes.
                     Examples: NFS mount, HDFS path, cloud storage mount point.
        prefix: Prefix for the temporary directory name to identify the operation type.
               Should indicate the format or operation (e.g., "spark_csv", "spark_parquet").

    Returns:
        Path object pointing to the created temporary directory with guaranteed uniqueness.

    Directory Structure:
        The created directory follows the pattern: _{prefix}_tmp_{uuid}
        Example: _spark_csv_tmp_a1b2c3d4e5f67890abcdef1234567890

    Raises:
        PermissionError: If shared_mount location is not writable by current process
        OSError: If temporary directory creation fails due to filesystem issues

    Security Considerations:
        - Directory name includes random UUID to prevent prediction or collision
        - Created with default permissions allowing cluster node access
        - Prefix with underscore makes directory less visible in listings
        - Parent directories are created with appropriate permissions if needed

    Note:
        The temporary directory must be explicitly cleaned up after use with
        _cleanup_temp_directory() to avoid resource leaks and storage consumption.
        Network filesystems may require additional time for directory visibility
        across all cluster nodes.
    """
    shared_mount = Path(shared_mount)
    tmp_dir = shared_mount / f"_{prefix}_tmp_{uuid.uuid4().hex}"
    tmp_dir.mkdir(parents=True, exist_ok=True)
    return tmp_dir


def _cleanup_temp_directory(tmp_dir: Path) -> None:
    """
    Safely clean up temporary directory and all its contents with error handling.

    Performs robust cleanup of temporary directories created for Spark operations,
    including all files and subdirectories. Uses error suppression to ensure cleanup
    completion even in adverse conditions, preventing temporary cleanup failures
    from interrupting the main data processing workflow.

    Args:
        tmp_dir: Path to the temporary directory to be removed completely.
                Must be a valid Path object (can be non-existent).

    Cleanup Process:
        1. Checks if directory exists before attempting removal
        2. Removes entire directory tree using shutil.rmtree
        3. Ignores all errors during cleanup to prevent workflow interruption
        4. Handles various edge cases (locked files, permission issues, network delays)

    Error Handling Strategy:
        - **Graceful Degradation**: All errors are silently handled to prevent interruption
        - **Network Filesystems**: Accommodates delays and consistency issues
        - **Permission Issues**: Ignores files that cannot be removed due to permissions
        - **Concurrent Access**: Handles conflicts with other processes accessing files
        - **Platform Compatibility**: Works across Windows, Linux, and macOS filesystems

    Network Filesystem Considerations:
        - **NFS**: Handles file lock releases and caching delays
        - **HDFS**: Manages distributed filesystem consistency
        - **Cloud Storage**: Accommodates eventual consistency models
        - **Windows Shares**: Manages file handle complexities

    Raises:
        No exceptions are raised - all errors are suppressed with ignore_errors=True.
        This design ensures that temporary cleanup failures don't interrupt the main
        data processing workflow, prioritizing data operation success over cleanup perfection.

    Usage Pattern:
        This function should always be called in a try-finally block or similar
        construct to ensure cleanup occurs even if the main operation fails:

         tmp_dir = _create_temp_directory(mount, "spark_csv")
         try:
        ...     # Perform Spark operations
        ...     pass
        ... finally:
        ...     _cleanup_temp_directory(tmp_dir)

    Note:
        Temporary directory cleanup is essential for preventing storage leaks
        in long-running Spark applications. While this function suppresses errors
        for operational robustness, monitoring systems should track storage usage
        to detect potential cleanup failures in network filesystem scenarios.
    """
    if tmp_dir.exists():
        shutil.rmtree(tmp_dir, ignore_errors=True)
