"""
Compatibility shim for the removed 'pipes' module.

The 'pipes' module was deprecated in Python 3.11 and removed in Python 3.13.
This package provides a compatibility layer for packages that still depend on it.

"""

import shlex
import subprocess
import sys
import warnings
from typing import IO

# Suppress deprecation warning since this is a compatibility shim
warnings.filterwarnings("ignore", category=DeprecationWarning, module="pipes")

__all__ = ["quote", "Template"]


def quote(s: str) -> str:
    """
    Return a shell-escaped version of the string *s*.

    This is a compatibility shim for pipes.quote(), which was removed in Python 3.13.
    It uses shlex.quote() which provides the same functionality.

    Args:
        s: String to escape for shell use.

    Returns:
        Shell-escaped string.

    Example:
        >>> quote("hello world")
        "'hello world'"
    """
    return shlex.quote(s)


class Template:
    """
    Compatibility class for pipes.Template.

    The Template class represents a shell pipeline template. Commands can be
    added to the pipeline using append() and prepend(), and the pipeline can
    be executed using open() or copy().

    Note: This implementation uses subprocess for pipeline execution, which
    is the recommended replacement for the deprecated pipes module.
    """

    def __init__(self):
        """Initialize a new pipeline template."""
        self.commands = []
        self.prepend_commands = []
        self.append_commands = []
        self._debug = False

    def reset(self) -> None:
        """Reset a pipeline template to its initial state."""
        self.commands = []
        self.prepend_commands = []
        self.append_commands = []
        self._debug = False

    def clone(self) -> "Template":
        """Return a new, initialized copy of the pipeline template."""
        new = Template()
        new.commands = self.commands[:]
        new.prepend_commands = self.prepend_commands[:]
        new.append_commands = self.append_commands[:]
        new._debug = self._debug
        return new

    def debug(self, flag: bool) -> None:
        """
        Turn debugging on or off.

        Args:
            flag: If True, enable debugging output.
        """
        self._debug = bool(flag)

    def append(self, cmd: str, kind: str) -> None:
        """
        Append a new action at the end.

        Args:
            cmd: Command string to append.
            kind: Type of command (typically 'f' for file operation, '-' for stdin/stdout).
        """
        self.append_commands.append((cmd, kind))

    def prepend(self, cmd: str, kind: str) -> None:
        """
        Add a new action at the beginning.

        Args:
            cmd: Command string to prepend.
            kind: Type of command (typically 'f' for file operation, '-' for stdin/stdout).
        """
        self.prepend_commands.insert(0, (cmd, kind))

    def _build_pipeline(self) -> list[str]:
        """Build the full command pipeline as a list of command strings."""
        all_commands = self.prepend_commands + self.commands + self.append_commands
        return [cmd for cmd, _ in all_commands]

    def open(self, file: str, mode: str) -> IO:
        """
        Open a file through the pipeline, or return standard input/output.

        This method executes the pipeline and returns a file-like object.
        If file is '-', returns stdin (for 'r' mode) or stdout (for 'w' mode).
        Otherwise, executes the pipeline with the file as input/output.

        Args:
            file: File path or '-' for stdin/stdout.
            mode: File mode ('r' for read, 'w' for write).

        Returns:
            File-like object (stdin/stdout or subprocess pipe).

        Note:
            For complex pipelines, consider using subprocess.Popen directly
            for better control and error handling.
        """
        # Validate mode first
        if mode not in ("r", "w", "rb", "wb"):
            raise ValueError(f"Invalid mode '{mode}'")

        if file == "-":
            if mode == "r":
                return sys.stdin
            elif mode == "w":
                return sys.stdout
            elif mode == "rb":
                return sys.stdin.buffer
            elif mode == "wb":
                return sys.stdout.buffer
            else:
                raise ValueError(f"Invalid mode '{mode}' for stdin/stdout")

        pipeline = self._build_pipeline()

        if not pipeline:
            # No pipeline commands, just open the file normally
            return open(file, mode)

        # For subprocess pipes, we need to handle text vs binary mode
        text_mode = mode in ("r", "w")

        if mode in ("r", "rb"):
            # Read from file through pipeline
            # Build shell command: cat file | cmd1 | cmd2 | ...
            cmd_parts = [f"cat {shlex.quote(file)}"]
            cmd_parts.extend(pipeline)
            shell_cmd = " | ".join(cmd_parts)

            if self._debug:
                print(f"Executing: {shell_cmd}", file=sys.stderr)

            proc = subprocess.Popen(
                shell_cmd,
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE if self._debug else subprocess.DEVNULL,
                text=text_mode,
                encoding="utf-8" if text_mode else None,
                errors="replace" if text_mode else None,
            )
            return proc.stdout  # type: ignore

        elif mode in ("w", "wb"):
            # Write to file through pipeline
            # Build shell command: cmd1 | cmd2 | ... > file
            shell_cmd = " | ".join(pipeline) + " > " + shlex.quote(file)

            if self._debug:
                print(f"Executing: {shell_cmd}", file=sys.stderr)

            proc = subprocess.Popen(
                shell_cmd,
                shell=True,
                stdin=subprocess.PIPE,
                stderr=subprocess.PIPE if self._debug else subprocess.DEVNULL,
                text=text_mode,
                encoding="utf-8" if text_mode else None,
                errors="replace" if text_mode else None,
            )
            return proc.stdin  # type: ignore
        else:
            raise ValueError(f"Invalid mode '{mode}'")

    def copy(self, infile: str, outfile: str) -> None:
        """
        Copy infile to outfile through the pipe.

        Executes the pipeline, reading from infile and writing to outfile.
        If infile is '-', reads from stdin. If outfile is '-', writes to stdout.

        Args:
            infile: Input file path or '-' for stdin.
            outfile: Output file path or '-' for stdout.

        Raises:
            subprocess.CalledProcessError: If the pipeline execution fails.
        """
        pipeline = self._build_pipeline()

        # Build the shell command
        parts = []

        if infile == "-":
            # Read from stdin
            if pipeline:
                parts.extend(pipeline)
            else:
                # No pipeline, just copy stdin to outfile
                if outfile == "-":
                    # stdin to stdout - just read and write
                    with sys.stdin as stdin, sys.stdout as stdout:
                        stdout.write(stdin.read())
                    return
                else:
                    # stdin to file
                    with sys.stdin as stdin, open(outfile, "wb") as f:
                        f.write(stdin.buffer.read())
                    return
        else:
            # Read from file
            parts.append(f"cat {shlex.quote(infile)}")
            parts.extend(pipeline)

        if outfile == "-":
            # Write to stdout
            shell_cmd = " | ".join(parts)
        else:
            # Write to file
            shell_cmd = " | ".join(parts) + " > " + shlex.quote(outfile)

        if self._debug:
            print(f"Executing: {shell_cmd}", file=sys.stderr)

        result = subprocess.run(
            shell_cmd,
            shell=True,
            check=True,
            stderr=subprocess.PIPE if self._debug else subprocess.DEVNULL,
        )

        if self._debug and result.stderr:
            print(result.stderr.decode("utf-8", errors="replace"), file=sys.stderr)
