"""
Roundtrip tests for Issue #140: md-explode and md-implode compatibility.

Tests bidirectional functionality to ensure explode→implode and implode→explode
maintain content fidelity and proper structure reconstruction.
"""

import pytest
import tempfile
import shutil
import subprocess
from pathlib import Path
from textwrap import dedent


class TestExplodeImplodeRoundtrip:
    """Test explode→implode roundtrip functionality."""

    def setup_method(self):
        """Set up temporary directory for each test."""
        self.temp_dir = Path(tempfile.mkdtemp())

    def teardown_method(self):
        """Clean up temporary directory after each test."""
        if self.temp_dir.exists():
            shutil.rmtree(self.temp_dir)

    def run_markitect_command(self, args, check=True):
        """Helper to run markitect commands."""
        cmd = ["python", "-m", "markitect.cli"] + args
        result = subprocess.run(
            cmd,
            cwd="/home/worsch/markitect_project",
            capture_output=True,
            text=True
        )
        if check and result.returncode != 0:
            pytest.fail(f"Command failed: {' '.join(args)}\nStdout: {result.stdout}\nStderr: {result.stderr}")
        return result

    def test_simple_hierarchical_roundtrip(self):
        """Test basic hierarchical structure roundtrip."""

        # Create initial markdown file
        original_content = dedent("""
            # Book Title

            This is the introduction to the book.

            ## Chapter 1: Getting Started

            This chapter covers the basics.

            ### Section 1.1: Overview

            Overview content here.

            ### Section 1.2: Setup

            Setup instructions here.

            ## Chapter 2: Advanced Topics

            Advanced content goes here.

            # Conclusion

            Final thoughts and summary.
        """).strip()

        original_file = self.temp_dir / "book.md"
        original_file.write_text(original_content)

        # Step 1: Explode markdown to directory
        exploded_dir = self.temp_dir / "book_exploded"
        result = self.run_markitect_command([
            "md-explode", str(original_file),
            "--output-dir", str(exploded_dir)
        ])
        assert result.returncode == 0
        assert exploded_dir.exists()

        # Verify exploded structure exists
        assert (exploded_dir / "book_title").exists()
        assert (exploded_dir / "book_title" / "index.md").exists()
        assert (exploded_dir / "book_title" / "chapter_1_getting_started").exists()
        assert (exploded_dir / "book_title" / "chapter_1_getting_started" / "index.md").exists()
        assert (exploded_dir / "book_title" / "chapter_1_getting_started" / "section_1_1_overview.md").exists()

        # Step 2: Implode directory back to markdown
        reconstructed_file = self.temp_dir / "reconstructed.md"
        result = self.run_markitect_command([
            "md-implode", str(exploded_dir),
            "--output", str(reconstructed_file)
        ])
        assert result.returncode == 0
        assert reconstructed_file.exists()

        # Step 3: Compare original and reconstructed content
        reconstructed_content = reconstructed_file.read_text().strip()

        # Verify key structural elements are preserved
        assert "# Book Title" in reconstructed_content
        assert "## Chapter 1: Getting Started" in reconstructed_content
        assert "### Section 1.1: Overview" in reconstructed_content
        assert "### Section 1.2: Setup" in reconstructed_content
        assert "## Chapter 2: Advanced Topics" in reconstructed_content
        assert "# Conclusion" in reconstructed_content

        # Verify content is preserved
        assert "This is the introduction to the book." in reconstructed_content
        assert "This chapter covers the basics." in reconstructed_content
        assert "Overview content here." in reconstructed_content
        assert "Setup instructions here." in reconstructed_content
        assert "Advanced content goes here." in reconstructed_content
        assert "Final thoughts and summary." in reconstructed_content

    def test_complex_structure_with_front_matter_roundtrip(self):
        """Test roundtrip with front matter and complex structure."""

        original_content = dedent("""
            ---
            title: "Complex Document"
            author: "Test Author"
            date: "2024-10-07"
            tags: [documentation, test]
            ---

            # Complex Document

            This document has front matter.

            ## Part 1: Fundamentals

            ### Chapter 1: Basics

            Basic content with **bold** and *italic* text.

            #### Section 1.1: Details

            Detailed information here.

            ##### Subsection 1.1.1: Specifics

            Very specific content.

            ### Chapter 2: Intermediate

            Intermediate level content.

            ## Part 2: Advanced

            Advanced topics discussion.

            ## Appendix

            Reference material and additional information.
        """).strip()

        original_file = self.temp_dir / "complex.md"
        original_file.write_text(original_content)

        # Explode to directory
        exploded_dir = self.temp_dir / "complex_exploded"
        result = self.run_markitect_command([
            "md-explode", str(original_file),
            "--output-dir", str(exploded_dir)
        ])
        assert result.returncode == 0

        # Implode back to markdown
        reconstructed_file = self.temp_dir / "complex_reconstructed.md"
        result = self.run_markitect_command([
            "md-implode", str(exploded_dir),
            "--output", str(reconstructed_file),
            "--preserve-front-matter"
        ])
        assert result.returncode == 0

        reconstructed_content = reconstructed_file.read_text()

        # Verify front matter is preserved
        assert "title: \"Complex Document\"" in reconstructed_content
        assert "author: \"Test Author\"" in reconstructed_content
        assert "tags: [documentation, test]" in reconstructed_content

        # Verify hierarchical structure
        assert "# Complex Document" in reconstructed_content
        assert "## Part 1: Fundamentals" in reconstructed_content
        assert "### Chapter 1: Basics" in reconstructed_content
        assert "#### Section 1.1: Details" in reconstructed_content
        assert "##### Subsection 1.1.1: Specifics" in reconstructed_content

        # Verify formatting is preserved
        assert "**bold**" in reconstructed_content
        assert "*italic*" in reconstructed_content

    def test_minimal_document_roundtrip(self):
        """Test roundtrip with minimal document structure."""

        original_content = dedent("""
            # Simple Document

            Just a simple document with minimal content.

            ## One Section

            Some content in the section.
        """).strip()

        original_file = self.temp_dir / "simple.md"
        original_file.write_text(original_content)

        # Explode and implode
        exploded_dir = self.temp_dir / "simple_exploded"
        self.run_markitect_command(["md-explode", str(original_file), "--output-dir", str(exploded_dir)])

        reconstructed_file = self.temp_dir / "simple_reconstructed.md"
        self.run_markitect_command(["md-implode", str(exploded_dir), "--output", str(reconstructed_file)])

        reconstructed_content = reconstructed_file.read_text().strip()

        # Verify structure and content preservation
        assert "# Simple Document" in reconstructed_content
        assert "## One Section" in reconstructed_content
        assert "Just a simple document with minimal content." in reconstructed_content
        assert "Some content in the section." in reconstructed_content

    def test_empty_sections_roundtrip(self):
        """Test roundtrip handling of empty sections."""

        original_content = dedent("""
            # Document with Empty Sections

            Introduction content.

            ## Empty Chapter

            ## Chapter with Content

            This chapter has actual content.

            ### Empty Subsection

            ### Subsection with Content

            Content in subsection.
        """).strip()

        original_file = self.temp_dir / "empty_sections.md"
        original_file.write_text(original_content)

        exploded_dir = self.temp_dir / "empty_exploded"
        self.run_markitect_command(["md-explode", str(original_file), "--output-dir", str(exploded_dir)])

        reconstructed_file = self.temp_dir / "empty_reconstructed.md"
        self.run_markitect_command(["md-implode", str(exploded_dir), "--output", str(reconstructed_file)])

        reconstructed_content = reconstructed_file.read_text()

        # Verify all sections are preserved, even empty ones
        assert "# Document with Empty Sections" in reconstructed_content
        assert "## Empty Chapter" in reconstructed_content
        assert "## Chapter with Content" in reconstructed_content
        assert "### Empty Subsection" in reconstructed_content
        assert "### Subsection with Content" in reconstructed_content


class TestImplodeExplodeRoundtrip:
    """Test implode→explode roundtrip functionality."""

    def setup_method(self):
        """Set up temporary directory for each test."""
        self.temp_dir = Path(tempfile.mkdtemp())

    def teardown_method(self):
        """Clean up temporary directory after each test."""
        if self.temp_dir.exists():
            shutil.rmtree(self.temp_dir)

    def run_markitect_command(self, args, check=True):
        """Helper to run markitect commands."""
        cmd = ["python", "-m", "markitect.cli"] + args
        result = subprocess.run(
            cmd,
            cwd="/home/worsch/markitect_project",
            capture_output=True,
            text=True
        )
        if check and result.returncode != 0:
            pytest.fail(f"Command failed: {' '.join(args)}\nStdout: {result.stdout}\nStderr: {result.stderr}")
        return result

    def create_sample_directory_structure(self):
        """Create a sample directory structure to test with."""

        # Create directory structure
        base_dir = self.temp_dir / "sample_project"
        base_dir.mkdir()

        # Root content
        (base_dir / "introduction.md").write_text(dedent("""
            # Sample Project

            This is a sample project for testing roundtrip functionality.
        """).strip())

        # Chapter 1 structure
        chapter1_dir = base_dir / "chapter_1_basics"
        chapter1_dir.mkdir()
        (chapter1_dir / "index.md").write_text(dedent("""
            ## Chapter 1: Basics

            This chapter covers the fundamental concepts.
        """).strip())

        (chapter1_dir / "section_1_1_overview.md").write_text(dedent("""
            ### Section 1.1: Overview

            Overview of the basic concepts.
        """).strip())

        (chapter1_dir / "section_1_2_details.md").write_text(dedent("""
            ### Section 1.2: Details

            Detailed explanation of concepts.
        """).strip())

        # Chapter 2 structure
        chapter2_dir = base_dir / "chapter_2_advanced"
        chapter2_dir.mkdir()
        (chapter2_dir / "index.md").write_text(dedent("""
            ## Chapter 2: Advanced

            Advanced topics and techniques.
        """).strip())

        # Nested subsection
        subsection_dir = chapter2_dir / "subsection_2_1_algorithms"
        subsection_dir.mkdir()
        (subsection_dir / "index.md").write_text(dedent("""
            ### Subsection 2.1: Algorithms

            Discussion of algorithms.
        """).strip())

        (subsection_dir / "part_2_1_1_sorting.md").write_text(dedent("""
            #### Part 2.1.1: Sorting

            Sorting algorithm implementations.
        """).strip())

        # Conclusion
        (base_dir / "conclusion.md").write_text(dedent("""
            # Conclusion

            Summary and final thoughts.
        """).strip())

        return base_dir

    def test_directory_to_markdown_to_directory_roundtrip(self):
        """Test directory→markdown→directory roundtrip."""

        # Create original directory structure
        original_dir = self.create_sample_directory_structure()

        # Step 1: Implode directory to markdown
        markdown_file = self.temp_dir / "imploded.md"
        result = self.run_markitect_command([
            "md-implode", str(original_dir),
            "--output", str(markdown_file)
        ])
        assert result.returncode == 0
        assert markdown_file.exists()

        # Verify markdown content structure
        markdown_content = markdown_file.read_text()
        assert "# Sample Project" in markdown_content
        assert "## Chapter 1: Basics" in markdown_content
        assert "### Section 1.1: Overview" in markdown_content
        assert "## Chapter 2: Advanced" in markdown_content
        assert "### Subsection 2.1: Algorithms" in markdown_content
        assert "#### Part 2.1.1: Sorting" in markdown_content
        assert "# Conclusion" in markdown_content

        # Step 2: Explode markdown back to directory
        reconstructed_dir = self.temp_dir / "reconstructed_project"
        result = self.run_markitect_command([
            "md-explode", str(markdown_file),
            "--output-dir", str(reconstructed_dir)
        ])
        assert result.returncode == 0
        assert reconstructed_dir.exists()

        # Step 3: Verify directory structure is reconstructed
        # Check for key files and directories (explode creates a directory named after the first h1)
        assert (reconstructed_dir / "sample_project").exists()
        assert (reconstructed_dir / "sample_project" / "index.md").exists()
        assert (reconstructed_dir / "sample_project" / "chapter_1_basics.md").exists()
        assert (reconstructed_dir / "sample_project" / "chapter_2_advanced").exists()
        assert (reconstructed_dir / "sample_project" / "chapter_2_advanced" / "index.md").exists()
        assert (reconstructed_dir / "conclusion.md").exists()

        # Verify content preservation
        intro_content = (reconstructed_dir / "sample_project" / "index.md").read_text()
        assert "# Sample Project" in intro_content
        assert "This is a sample project for testing" in intro_content

    def test_nested_structure_roundtrip(self):
        """Test deeply nested structure roundtrip."""

        # Create deeply nested structure
        base_dir = self.temp_dir / "deep_structure"
        base_dir.mkdir()

        # Create 5-level deep structure
        current_dir = base_dir
        for level in range(1, 6):
            content = f"{'#' * level} Level {level}\n\nContent at level {level}."

            if level == 1:
                # Root level file
                (current_dir / f"level_{level}.md").write_text(content)
            else:
                # Create directory and index
                level_dir = current_dir / f"level_{level}_section"
                level_dir.mkdir()
                (level_dir / "index.md").write_text(content)
                current_dir = level_dir

        # Implode to markdown
        markdown_file = self.temp_dir / "deep_structure.md"
        self.run_markitect_command([
            "md-implode", str(base_dir),
            "--output", str(markdown_file)
        ])

        # Explode back to directory
        reconstructed_dir = self.temp_dir / "deep_reconstructed"
        self.run_markitect_command([
            "md-explode", str(markdown_file),
            "--output-dir", str(reconstructed_dir)
        ])

        # Verify deep structure is preserved (explode creates directory named after first h1)
        assert (reconstructed_dir / "level_1").exists()
        assert (reconstructed_dir / "level_1" / "index.md").exists()
        assert (reconstructed_dir / "level_1" / "level_2").exists()
        assert (reconstructed_dir / "level_1" / "level_2" / "level_3").exists()
        assert (reconstructed_dir / "level_1" / "level_2" / "level_3" / "level_4").exists()

        # Verify content at different levels
        level_1_content = (reconstructed_dir / "level_1" / "index.md").read_text()
        assert "# Level 1" in level_1_content
        assert "Content at level 1." in level_1_content


class TestRoundtripContentFidelity:
    """Test content fidelity across roundtrip operations."""

    def setup_method(self):
        """Set up temporary directory for each test."""
        self.temp_dir = Path(tempfile.mkdtemp())

    def teardown_method(self):
        """Clean up temporary directory after each test."""
        if self.temp_dir.exists():
            shutil.rmtree(self.temp_dir)

    def run_markitect_command(self, args, check=True):
        """Helper to run markitect commands."""
        cmd = ["python", "-m", "markitect.cli"] + args
        result = subprocess.run(
            cmd,
            cwd="/home/worsch/markitect_project",
            capture_output=True,
            text=True
        )
        if check and result.returncode != 0:
            pytest.fail(f"Command failed: {' '.join(args)}\nStdout: {result.stdout}\nStderr: {result.stderr}")
        return result

    def test_markdown_formatting_preservation(self):
        """Test that markdown formatting is preserved through roundtrips."""

        original_content = dedent("""
            # Formatting Test Document

            This document tests various **markdown** *formatting* elements.

            ## Code Examples

            Here's some `inline code` and a code block:

            ```python
            def hello_world():
                print("Hello, World!")
            ```

            ## Lists and Links

            Bullet list:
            - Item 1
            - Item 2
            - Item 3

            Numbered list:
            1. First item
            2. Second item
            3. Third item

            Link example: [Markitect](https://github.com/example/markitect)

            ## Tables

            | Column 1 | Column 2 | Column 3 |
            |----------|----------|----------|
            | Value A  | Value B  | Value C  |
            | Value D  | Value E  | Value F  |

            ## Quotes and Special Characters

            > This is a blockquote
            > with multiple lines

            Special characters: & < > " '
        """).strip()

        original_file = self.temp_dir / "formatting_test.md"
        original_file.write_text(original_content)

        # Full roundtrip: explode → implode
        exploded_dir = self.temp_dir / "formatting_exploded"
        self.run_markitect_command(["md-explode", str(original_file), "--output-dir", str(exploded_dir)])

        reconstructed_file = self.temp_dir / "formatting_reconstructed.md"
        self.run_markitect_command(["md-implode", str(exploded_dir), "--output", str(reconstructed_file)])

        reconstructed_content = reconstructed_file.read_text()

        # Verify formatting elements are preserved
        assert "**markdown**" in reconstructed_content
        assert "*formatting*" in reconstructed_content
        assert "`inline code`" in reconstructed_content
        assert "```python" in reconstructed_content
        assert "def hello_world():" in reconstructed_content
        assert "- Item 1" in reconstructed_content
        assert "1. First item" in reconstructed_content
        assert "[Markitect]" in reconstructed_content
        assert "| Column 1 |" in reconstructed_content
        assert "> This is a blockquote" in reconstructed_content
        assert "Special characters: & < > " in reconstructed_content

    def test_whitespace_and_spacing_preservation(self):
        """Test preservation of whitespace and spacing patterns."""

        original_content = dedent("""
            # Spacing Test


            This paragraph has extra blank lines above.

            ## Section with Spacing

            Content here.



            Multiple blank lines above this paragraph.

            ### Subsection

            Normal spacing here.

            ## Another Section

            Final content.
        """).strip()

        original_file = self.temp_dir / "spacing_test.md"
        original_file.write_text(original_content)

        # Roundtrip test
        exploded_dir = self.temp_dir / "spacing_exploded"
        self.run_markitect_command(["md-explode", str(original_file), "--output-dir", str(exploded_dir)])

        reconstructed_file = self.temp_dir / "spacing_reconstructed.md"
        self.run_markitect_command(["md-implode", str(exploded_dir), "--output", str(reconstructed_file)])

        reconstructed_content = reconstructed_file.read_text()

        # Verify key content is preserved (exact spacing may vary due to processing)
        assert "# Spacing Test" in reconstructed_content
        assert "This paragraph has extra blank lines above." in reconstructed_content
        assert "Multiple blank lines above this paragraph." in reconstructed_content
        assert "## Section with Spacing" in reconstructed_content
        assert "### Subsection" in reconstructed_content
        assert "## Another Section" in reconstructed_content

    def test_unicode_and_special_characters_roundtrip(self):
        """Test handling of unicode and special characters."""

        original_content = dedent("""
            # Unicode Test Document 🚀

            This document contains various unicode characters and symbols.

            ## Emoji Section 😀

            Various emoji: 🎉 📚 💻 ✅ ❌ 🔥 ⭐ 🌟

            ## International Characters

            - Français: café, naïve, résumé
            - Deutsch: Größe, Weiß, Straße
            - 日本語: こんにちは、ありがとう
            - Español: niño, señor, corazón
            - Русский: привет, спасибо

            ## Mathematical Symbols

            - Greek letters: α β γ δ ε ζ η θ
            - Math symbols: ∑ ∫ ∞ ≈ ≠ ± √ π
            - Arrows: → ← ↑ ↓ ↔ ⇒ ⇐

            ## Special Characters

            Quotes: " " ' ' „ "
            Punctuation: … – — • ‡ § ¶
        """).strip()

        original_file = self.temp_dir / "unicode_test.md"
        original_file.write_text(original_content, encoding='utf-8')

        # Roundtrip test
        exploded_dir = self.temp_dir / "unicode_exploded"
        self.run_markitect_command(["md-explode", str(original_file), "--output-dir", str(exploded_dir)])

        reconstructed_file = self.temp_dir / "unicode_reconstructed.md"
        self.run_markitect_command(["md-implode", str(exploded_dir), "--output", str(reconstructed_file)])

        reconstructed_content = reconstructed_file.read_text(encoding='utf-8')

        # Verify unicode characters are preserved
        assert "🚀" in reconstructed_content
        assert "😀" in reconstructed_content
        assert "café" in reconstructed_content
        assert "こんにちは" in reconstructed_content
        assert "α β γ" in reconstructed_content
        assert "∑ ∫ ∞" in reconstructed_content
        assert "→ ←" in reconstructed_content
        assert '"' in reconstructed_content  # Smart quote character


class TestRoundtripErrorHandling:
    """Test error handling and edge cases in roundtrip operations."""

    def setup_method(self):
        """Set up temporary directory for each test."""
        self.temp_dir = Path(tempfile.mkdtemp())

    def teardown_method(self):
        """Clean up temporary directory after each test."""
        if self.temp_dir.exists():
            shutil.rmtree(self.temp_dir)

    def run_markitect_command(self, args, check=False):
        """Helper to run markitect commands."""
        cmd = ["python", "-m", "markitect.cli"] + args
        result = subprocess.run(
            cmd,
            cwd="/home/worsch/markitect_project",
            capture_output=True,
            text=True
        )
        return result

    def test_malformed_markdown_handling(self):
        """Test handling of malformed or problematic markdown."""

        # Create markdown with potential issues
        problematic_content = dedent("""
            # Document with Issues

            ## Section with # Hash in Title

            Content here.

            ### Section/With\\Special:Characters?

            More content.

            ## Section with "Quotes" and 'Apostrophes'

            Final content.
        """).strip()

        original_file = self.temp_dir / "problematic.md"
        original_file.write_text(problematic_content)

        # Test explode (should handle gracefully)
        exploded_dir = self.temp_dir / "problematic_exploded"
        result = self.run_markitect_command(["md-explode", str(original_file), "--output-dir", str(exploded_dir)])

        # Should succeed or fail gracefully
        if result.returncode == 0:
            # If explode succeeded, test implode
            reconstructed_file = self.temp_dir / "problematic_reconstructed.md"
            result = self.run_markitect_command(["md-implode", str(exploded_dir), "--output", str(reconstructed_file)])

            if result.returncode == 0:
                # Verify basic structure is preserved
                reconstructed_content = reconstructed_file.read_text()
                assert "# Document with Issues" in reconstructed_content

    def test_empty_files_and_directories(self):
        """Test handling of empty files and directories."""

        # Create structure with empty elements
        base_dir = self.temp_dir / "empty_test"
        base_dir.mkdir()

        # Empty markdown file
        (base_dir / "empty.md").write_text("")

        # File with only whitespace
        (base_dir / "whitespace.md").write_text("   \n\n   \n")

        # Valid file
        (base_dir / "valid.md").write_text("# Valid Content\n\nSome actual content.")

        # Empty directory
        (base_dir / "empty_dir").mkdir()

        # Test implode→explode roundtrip
        markdown_file = self.temp_dir / "empty_test.md"
        result = self.run_markitect_command(["md-implode", str(base_dir), "--output", str(markdown_file)])

        if result.returncode == 0:
            # Test explode back
            reconstructed_dir = self.temp_dir / "empty_reconstructed"
            result = self.run_markitect_command(["md-explode", str(markdown_file), "--output-dir", str(reconstructed_dir)])

            # Should handle empty content gracefully
            assert result.returncode == 0 or "no content" in result.stderr.lower()


if __name__ == "__main__":
    pytest.main([__file__, "-v"])