"""Tests for pipes.Template class."""

import os
import subprocess
import tempfile

import pytest

import pipes


class TestTemplateBasic:
    """Test basic Template class functionality."""

    def test_template_init(self):
        """Test Template initialization."""
        t = pipes.Template()
        assert t.commands == []
        assert t.prepend_commands == []
        assert t.append_commands == []
        assert t._debug is False

    def test_template_reset(self):
        """Test Template reset."""
        t = pipes.Template()
        t.append("echo test", "f")
        t.prepend("cat", "f")
        t.debug(True)
        t.reset()
        assert t.commands == []
        assert t.prepend_commands == []
        assert t.append_commands == []
        assert t._debug is False

    def test_template_clone(self):
        """Test Template cloning."""
        t = pipes.Template()
        t.append("echo test", "f")
        t.prepend("cat", "f")
        t.debug(True)

        cloned = t.clone()
        assert cloned is not t
        assert cloned.commands == t.commands
        assert cloned.prepend_commands == t.prepend_commands
        assert cloned.append_commands == t.append_commands
        assert cloned._debug == t._debug

        # Modifying clone shouldn't affect original
        cloned.append("new command", "f")
        assert len(cloned.append_commands) == 2
        assert len(t.append_commands) == 1

    def test_template_debug(self):
        """Test Template debug method."""
        t = pipes.Template()
        assert t._debug is False
        t.debug(True)
        assert t._debug is True
        t.debug(False)
        assert t._debug is False
        t.debug(1)  # Truthy value
        assert t._debug is True
        t.debug(0)  # Falsy value
        assert t._debug is False

    def test_template_append(self):
        """Test Template append method."""
        t = pipes.Template()
        t.append("echo hello", "f")
        assert len(t.append_commands) == 1
        assert t.append_commands[0] == ("echo hello", "f")

        t.append("echo world", "-")
        assert len(t.append_commands) == 2
        assert t.append_commands[1] == ("echo world", "-")

    def test_template_prepend(self):
        """Test Template prepend method."""
        t = pipes.Template()
        t.prepend("echo hello", "f")
        assert len(t.prepend_commands) == 1
        assert t.prepend_commands[0] == ("echo hello", "f")

        t.prepend("echo world", "-")
        assert len(t.prepend_commands) == 2
        # Prepended commands should be in reverse order
        assert t.prepend_commands[0] == ("echo world", "-")
        assert t.prepend_commands[1] == ("echo hello", "f")


class TestTemplateBuildPipeline:
    """Test Template pipeline building."""

    def test_build_pipeline_empty(self):
        """Test building an empty pipeline."""
        t = pipes.Template()
        pipeline = t._build_pipeline()
        assert pipeline == []

    def test_build_pipeline_with_commands(self):
        """Test building a pipeline with commands."""
        t = pipes.Template()
        t.prepend("first", "f")
        t.append("second", "f")
        t.append("third", "f")

        pipeline = t._build_pipeline()
        assert pipeline == ["first", "second", "third"]


class TestTemplateOpen:
    """Test Template open() method."""

    def test_open_stdin_read(self):
        """Test opening stdin for reading."""
        t = pipes.Template()
        result = t.open("-", "r")
        import sys

        assert result is sys.stdin

    def test_open_stdout_write(self):
        """Test opening stdout for writing."""
        t = pipes.Template()
        result = t.open("-", "w")
        import sys

        assert result is sys.stdout

    def test_open_invalid_mode_stdin(self):
        """Test opening stdin with invalid mode."""
        t = pipes.Template()
        with pytest.raises(ValueError, match="Invalid mode"):
            t.open("-", "x")

    def test_open_file_no_pipeline(self):
        """Test opening a file without a pipeline."""
        t = pipes.Template()
        with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
            f.write("test content")
            temp_path = f.name

        try:
            result = t.open(temp_path, "r")
            assert hasattr(result, "read")
            content = result.read()
            assert content == "test content"
            result.close()
        finally:
            os.unlink(temp_path)

    def test_open_file_with_pipeline_read(self):
        """Test opening a file through a pipeline for reading."""
        t = pipes.Template()
        t.append("tr '[:lower:]' '[:upper:]'", "f")

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as f:
            f.write("hello world")
            temp_path = f.name

        try:
            result = t.open(temp_path, "r")
            assert hasattr(result, "read")
            content = result.read().strip()
            assert content == "HELLO WORLD"
            if hasattr(result, "close"):
                result.close()
        finally:
            os.unlink(temp_path)

    def test_open_invalid_mode(self):
        """Test opening with invalid mode."""
        t = pipes.Template()
        with tempfile.NamedTemporaryFile(delete=False) as f:
            temp_path = f.name

        try:
            with pytest.raises(ValueError, match="Invalid mode"):
                t.open(temp_path, "x")
        finally:
            os.unlink(temp_path)


class TestTemplateCopy:
    """Test Template copy() method."""

    def test_copy_file_to_file_simple(self):
        """Test copying a file to another file without pipeline."""
        t = pipes.Template()

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as infile:
            infile.write("test content")
            inpath = infile.name

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as outfile:
            outpath = outfile.name

        try:
            t.copy(inpath, outpath)
            with open(outpath) as f:
                content = f.read()
            assert content == "test content"
        finally:
            os.unlink(inpath)
            os.unlink(outpath)

    def test_copy_file_to_file_with_pipeline(self):
        """Test copying a file through a pipeline."""
        t = pipes.Template()
        t.append("tr '[:lower:]' '[:upper:]'", "f")

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as infile:
            infile.write("hello world")
            inpath = infile.name

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as outfile:
            outpath = outfile.name

        try:
            t.copy(inpath, outpath)
            with open(outpath) as f:
                content = f.read().strip()
            assert content == "HELLO WORLD"
        finally:
            os.unlink(inpath)
            os.unlink(outpath)

    def test_copy_file_to_stdout(self):
        """Test copying a file to stdout."""
        t = pipes.Template()
        t.append("tr '[:lower:]' '[:upper:]'", "f")

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as infile:
            infile.write("hello")
            inpath = infile.name

        try:
            # Capture stdout
            result = subprocess.run(
                [
                    "python",
                    "-c",
                    f"import pipes; t = pipes.Template(); t.append('tr \\'[:lower:]\\' \\'[:upper:]\\'', 'f'); t.copy('{inpath}', '-')",
                ],
                capture_output=True,
                text=True,
            )
            assert result.stdout.strip() == "HELLO"
        finally:
            os.unlink(inpath)

    def test_copy_stdin_to_file(self):
        """Test copying stdin to a file."""
        with tempfile.NamedTemporaryFile(mode="w", delete=False) as outfile:
            outpath = outfile.name

        try:
            # Use subprocess to simulate stdin
            proc = subprocess.Popen(
                ["python", "-c", f"import pipes; t = pipes.Template(); t.copy('-', '{outpath}')"],
                stdin=subprocess.PIPE,
                text=True,
            )
            proc.communicate(input="test input")
            proc.wait()

            with open(outpath) as f:
                content = f.read()
            assert "test input" in content
        finally:
            os.unlink(outpath)

    def test_copy_with_multiple_commands(self):
        """Test copying through a pipeline with multiple commands."""
        t = pipes.Template()
        t.append("grep -v '^#'", "f")  # Remove comment lines
        t.append("tr '[:lower:]' '[:upper:]'", "f")
        t.append("head -n 2", "f")  # Take first 2 lines

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as infile:
            infile.write("# comment\nhello\nworld\n# another\n")
            inpath = infile.name

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as outfile:
            outpath = outfile.name

        try:
            t.copy(inpath, outpath)
            with open(outpath) as f:
                lines = f.read().strip().split("\n")
            # Should have HELLO and WORLD, no comments
            assert len(lines) == 2
            assert "HELLO" in lines or "WORLD" in lines
        finally:
            os.unlink(inpath)
            os.unlink(outpath)

    def test_copy_prepend_and_append(self):
        """Test copying with both prepended and appended commands."""
        t = pipes.Template()
        t.prepend("cat", "f")
        t.append("tr '[:lower:]' '[:upper:]'", "f")
        t.append("head -n 1", "f")

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as infile:
            infile.write("first line\nsecond line\n")
            inpath = infile.name

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as outfile:
            outpath = outfile.name

        try:
            t.copy(inpath, outpath)
            with open(outpath) as f:
                content = f.read().strip()
            assert content == "FIRST LINE"
        finally:
            os.unlink(inpath)
            os.unlink(outpath)

    def test_copy_empty_pipeline_stdin_to_stdout(self):
        """Test copying stdin to stdout with empty pipeline."""
        # This should work but we'll test via subprocess
        result = subprocess.run(
            ["python", "-c", "import pipes; t = pipes.Template(); t.copy('-', '-')"],
            input="test",
            capture_output=True,
            text=True,
        )
        assert result.stdout == "test"

    def test_copy_file_with_special_chars(self):
        """Test copying a file with special characters in path."""
        t = pipes.Template()

        # Create a file with spaces in the name
        with tempfile.TemporaryDirectory() as tmpdir:
            inpath = os.path.join(tmpdir, "file with spaces.txt")
            outpath = os.path.join(tmpdir, "output.txt")

            with open(inpath, "w") as f:
                f.write("test content")

            t.copy(inpath, outpath)

            with open(outpath) as f:
                content = f.read()
            assert content == "test content"


class TestTemplateErrorHandling:
    """Test Template error handling."""

    def test_copy_nonexistent_file(self):
        """Test copying a nonexistent file raises error."""
        t = pipes.Template()
        with tempfile.NamedTemporaryFile(delete=True) as outfile:
            outpath = outfile.name

        # File should be deleted now
        with pytest.raises(subprocess.CalledProcessError):
            t.copy("/nonexistent/file/path", outpath)

    def test_copy_invalid_command(self):
        """Test copying with an invalid command."""
        t = pipes.Template()
        t.append("nonexistent_command_xyz123", "f")

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as infile:
            infile.write("test")
            inpath = infile.name

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as outfile:
            outpath = outfile.name

        try:
            with pytest.raises(subprocess.CalledProcessError):
                t.copy(inpath, outpath)
        finally:
            os.unlink(inpath)
            os.unlink(outpath)


class TestTemplateDebug:
    """Test Template debug functionality."""

    def test_debug_output(self, capsys):
        """Test that debug mode produces output."""
        t = pipes.Template()
        t.debug(True)
        t.append("echo test", "f")

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as infile:
            infile.write("input")
            inpath = infile.name

        with tempfile.NamedTemporaryFile(mode="w", delete=False) as outfile:
            outpath = outfile.name

        try:
            t.copy(inpath, outpath)
            # Debug output goes to stderr
            # Note: This might not work perfectly due to subprocess, but we test the flag
            assert t._debug is True
        finally:
            os.unlink(inpath)
            os.unlink(outpath)
