"""
TDD8 Cycle 2: Frontmatter Commands Tests (RED Phase)
Issue #38 - MarkdownMatters CLI Implementation

This test file implements the RED phase tests for frontmatter command family:
- markitect frontmatter-get [key] [path] - Get specific frontmatter value
- markitect frontmatter-set key=value [path] - Set frontmatter value
- markitect frontmatter-keys [path] - List all frontmatter keys
- markitect frontmatter-stats [path] - Frontmatter statistics

Following TDD8 methodology, these tests MUST FAIL initially.
"""

import pytest
import tempfile
import os
from pathlib import Path
from click.testing import CliRunner

from markitect.matter_frontmatter.parser import FrontmatterParser
from markitect.matter_frontmatter.stats import FrontmatterStats
from markitect.matter_frontmatter.commands import frontmatter_get, frontmatter_set, frontmatter_keys, frontmatter_stats


class TestFrontmatterExtraction:
    """Test frontmatter extraction and parsing."""

    @pytest.fixture
    def test_files_dir(self):
        """Path to frontmatter test fixture files."""
        return Path(__file__).parent / "fixtures" / "frontmatter_test_files"

    @pytest.fixture
    def frontmatter_parser(self):
        """Frontmatter parser instance."""
        return FrontmatterParser()

    def test_frontmatter_parser_extracts_yaml_frontmatter(self, frontmatter_parser, test_files_dir):
        """Test that parser extracts YAML frontmatter correctly."""
        file_path = test_files_dir / "yaml_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        frontmatter = frontmatter_parser.extract_frontmatter(text)

        # Should extract all YAML frontmatter fields
        assert frontmatter["title"] == "YAML Frontmatter Test Document"
        assert frontmatter["author"] == "Test Author"
        assert str(frontmatter["date"]) == "2025-10-02"
        assert frontmatter["tags"] == ["yaml", "frontmatter", "testing"]
        assert frontmatter["version"] == 1.2
        assert frontmatter["published"] is True

        # Should handle nested objects
        assert frontmatter["nested"]["category"] == "documentation"
        assert frontmatter["nested"]["priority"] == "high"
        assert frontmatter["nested"]["metadata"]["creation_date"] == "2025-10-02"

    def test_frontmatter_parser_extracts_json_frontmatter(self, frontmatter_parser, test_files_dir):
        """Test that parser extracts JSON frontmatter correctly."""
        file_path = test_files_dir / "json_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        frontmatter = frontmatter_parser.extract_frontmatter(text)

        # Should extract all JSON frontmatter fields
        assert frontmatter["title"] == "JSON Frontmatter Test Document"
        assert frontmatter["author"] == "Test Author"
        assert frontmatter["tags"] == ["json", "frontmatter", "testing"]
        assert frontmatter["version"] == 2.1
        assert frontmatter["published"] is False

        # Should handle nested objects
        assert frontmatter["config"]["theme"] == "dark"
        assert frontmatter["config"]["language"] == "en"
        assert frontmatter["config"]["features"] == ["toc", "search", "navigation"]

    def test_frontmatter_parser_handles_no_frontmatter(self, frontmatter_parser, test_files_dir):
        """Test that parser handles documents without frontmatter."""
        file_path = test_files_dir / "no_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        frontmatter = frontmatter_parser.extract_frontmatter(text)

        # Should return empty dict for no frontmatter
        assert frontmatter == {}

    def test_frontmatter_parser_handles_empty_frontmatter(self, frontmatter_parser, test_files_dir):
        """Test that parser handles empty frontmatter blocks."""
        file_path = test_files_dir / "empty_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        frontmatter = frontmatter_parser.extract_frontmatter(text)

        # Should return empty dict for empty frontmatter
        assert frontmatter == {}

    def test_frontmatter_parser_get_nested_value(self, frontmatter_parser, test_files_dir):
        """Test getting nested values using dot notation."""
        file_path = test_files_dir / "yaml_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        frontmatter = frontmatter_parser.extract_frontmatter(text)

        # Should support dot notation for nested access
        value = frontmatter_parser.get_nested_value(frontmatter, "nested.category")
        assert value == "documentation"

        value = frontmatter_parser.get_nested_value(frontmatter, "nested.metadata.creation_date")
        assert value == "2025-10-02"

        # Should return None for non-existent keys
        value = frontmatter_parser.get_nested_value(frontmatter, "non.existent.key")
        assert value is None


class TestFrontmatterModification:
    """Test frontmatter modification operations."""

    @pytest.fixture
    def frontmatter_parser(self):
        """Frontmatter parser instance."""
        return FrontmatterParser()

    def test_frontmatter_set_simple_value(self, frontmatter_parser):
        """Test setting simple frontmatter values."""
        text = """---
title: "Original Title"
author: "Original Author"
---

# Content here"""

        new_text = frontmatter_parser.set_frontmatter_value(text, "title", "New Title")

        # Should update the title value
        frontmatter = frontmatter_parser.extract_frontmatter(new_text)
        assert frontmatter["title"] == "New Title"
        assert frontmatter["author"] == "Original Author"

    def test_frontmatter_set_new_value(self, frontmatter_parser):
        """Test adding new frontmatter values."""
        text = """---
title: "Original Title"
---

# Content here"""

        new_text = frontmatter_parser.set_frontmatter_value(text, "author", "New Author")

        # Should add the new field
        frontmatter = frontmatter_parser.extract_frontmatter(new_text)
        assert frontmatter["title"] == "Original Title"
        assert frontmatter["author"] == "New Author"

    def test_frontmatter_set_nested_value(self, frontmatter_parser):
        """Test setting nested frontmatter values using dot notation."""
        text = """---
title: "Test"
config:
  theme: "light"
---

# Content here"""

        new_text = frontmatter_parser.set_frontmatter_value(text, "config.theme", "dark")

        # Should update nested value
        frontmatter = frontmatter_parser.extract_frontmatter(new_text)
        assert frontmatter["config"]["theme"] == "dark"

    def test_frontmatter_add_to_empty_document(self, frontmatter_parser):
        """Test adding frontmatter to document without any."""
        text = """# Content Without Frontmatter

Just some content here."""

        new_text = frontmatter_parser.set_frontmatter_value(text, "title", "New Title")

        # Should add frontmatter block
        frontmatter = frontmatter_parser.extract_frontmatter(new_text)
        assert frontmatter["title"] == "New Title"

        # Should preserve content
        assert "# Content Without Frontmatter" in new_text


class TestFrontmatterKeys:
    """Test frontmatter key listing functionality."""

    @pytest.fixture
    def test_files_dir(self):
        """Path to frontmatter test fixture files."""
        return Path(__file__).parent / "fixtures" / "frontmatter_test_files"

    @pytest.fixture
    def frontmatter_parser(self):
        """Frontmatter parser instance."""
        return FrontmatterParser()

    def test_frontmatter_keys_yaml_document(self, frontmatter_parser, test_files_dir):
        """Test listing keys from YAML frontmatter."""
        file_path = test_files_dir / "yaml_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        keys = frontmatter_parser.get_frontmatter_keys(text)

        # Should return all top-level keys
        expected_keys = ["title", "author", "date", "tags", "version", "published", "description", "nested"]
        assert set(keys) == set(expected_keys)

    def test_frontmatter_keys_with_nested_option(self, frontmatter_parser, test_files_dir):
        """Test listing keys including nested keys with dot notation."""
        file_path = test_files_dir / "yaml_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        keys = frontmatter_parser.get_frontmatter_keys(text, include_nested=True)

        # Should include nested keys with dot notation
        assert "nested.category" in keys
        assert "nested.priority" in keys
        assert "nested.metadata.creation_date" in keys
        assert "nested.metadata.last_modified" in keys

    def test_frontmatter_keys_empty_document(self, frontmatter_parser, test_files_dir):
        """Test listing keys from document without frontmatter."""
        file_path = test_files_dir / "no_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        keys = frontmatter_parser.get_frontmatter_keys(text)

        # Should return empty list
        assert keys == []


class TestFrontmatterStatistics:
    """Test frontmatter statistics calculation."""

    @pytest.fixture
    def test_files_dir(self):
        """Path to frontmatter test fixture files."""
        return Path(__file__).parent / "fixtures" / "frontmatter_test_files"

    @pytest.fixture
    def frontmatter_parser(self):
        """Frontmatter parser instance."""
        return FrontmatterParser()

    def test_frontmatter_stats_yaml_document(self, frontmatter_parser, test_files_dir):
        """Test statistics calculation for YAML frontmatter."""
        file_path = test_files_dir / "yaml_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        stats = frontmatter_parser.calculate_frontmatter_stats(text)

        # Should count fields correctly
        assert stats.total_fields == 8  # Top-level fields
        assert stats.nested_fields == 5  # Nested fields (category, priority, creation_date, last_modified, metadata object)
        assert stats.format == "yaml"
        assert stats.has_frontmatter is True

        # Should categorize field types
        assert "string" in stats.field_types
        assert "array" in stats.field_types
        assert "number" in stats.field_types
        assert "boolean" in stats.field_types
        assert "object" in stats.field_types

    def test_frontmatter_stats_json_document(self, frontmatter_parser, test_files_dir):
        """Test statistics calculation for JSON frontmatter."""
        file_path = test_files_dir / "json_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        stats = frontmatter_parser.calculate_frontmatter_stats(text)

        # Should identify JSON format
        assert stats.format == "json"
        assert stats.has_frontmatter is True
        assert stats.total_fields > 0

    def test_frontmatter_stats_no_frontmatter(self, frontmatter_parser, test_files_dir):
        """Test statistics for document without frontmatter."""
        file_path = test_files_dir / "no_frontmatter.md"

        with open(file_path, 'r') as f:
            text = f.read()

        stats = frontmatter_parser.calculate_frontmatter_stats(text)

        # Should indicate no frontmatter
        assert stats.has_frontmatter is False
        assert stats.total_fields == 0
        assert stats.nested_fields == 0
        assert stats.format is None


class TestFrontmatterCLICommands:
    """Test CLI command integration."""

    @pytest.fixture
    def runner(self):
        """CLI test runner."""
        return CliRunner()

    @pytest.fixture
    def test_files_dir(self):
        """Path to frontmatter test fixture files."""
        return Path(__file__).parent / "fixtures" / "frontmatter_test_files"

    def test_frontmatter_get_command(self, runner, test_files_dir):
        """Test frontmatter-get CLI command."""
        file_path = test_files_dir / "yaml_frontmatter.md"

        # Test getting simple value
        result = runner.invoke(frontmatter_get, ['title', '--file', str(file_path)])
        assert result.exit_code == 0
        assert "YAML Frontmatter Test Document" in result.output

        # Test getting nested value
        result = runner.invoke(frontmatter_get, ['nested.category', '--file', str(file_path)])
        assert result.exit_code == 0
        assert "documentation" in result.output

    def test_frontmatter_keys_command(self, runner, test_files_dir):
        """Test frontmatter-keys CLI command."""
        file_path = test_files_dir / "yaml_frontmatter.md"

        result = runner.invoke(frontmatter_keys, ['--file', str(file_path)])
        assert result.exit_code == 0
        assert "title" in result.output
        assert "author" in result.output
        assert "tags" in result.output

    def test_frontmatter_stats_command(self, runner, test_files_dir):
        """Test frontmatter-stats CLI command."""
        file_path = test_files_dir / "yaml_frontmatter.md"

        result = runner.invoke(frontmatter_stats, ['--file', str(file_path)])
        assert result.exit_code == 0
        assert "total_fields" in result.output
        assert "format" in result.output

    def test_frontmatter_set_command(self, runner, test_files_dir):
        """Test frontmatter-set CLI command."""
        # Create temporary file for testing
        with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f:
            f.write("""---
title: "Original Title"
---

# Test Content""")
            temp_file = f.name

        try:
            result = runner.invoke(frontmatter_set, ['title=New Title', '--file', temp_file])
            assert result.exit_code == 0

            # Verify the change was made
            with open(temp_file, 'r') as f:
                content = f.read()
                assert "New Title" in content

        finally:
            os.unlink(temp_file)

    def test_frontmatter_commands_help_text(self, runner):
        """Test that help text is available for all frontmatter commands."""
        commands = [frontmatter_get, frontmatter_keys, frontmatter_stats, frontmatter_set]

        for command in commands:
            result = runner.invoke(command, ['--help'])
            assert result.exit_code == 0
            assert "frontmatter" in result.output.lower()


class TestFrontmatterStats:
    """Test FrontmatterStats data class."""

    def test_frontmatter_stats_creation(self):
        """Test FrontmatterStats object creation."""
        stats = FrontmatterStats(
            has_frontmatter=True,
            total_fields=5,
            nested_fields=2,
            format="yaml",
            field_types={"string": 3, "number": 1, "boolean": 1}
        )

        assert stats.has_frontmatter is True
        assert stats.total_fields == 5
        assert stats.nested_fields == 2
        assert stats.format == "yaml"
        assert stats.field_types["string"] == 3

    def test_frontmatter_stats_to_dict(self):
        """Test FrontmatterStats conversion to dictionary."""
        stats = FrontmatterStats(
            has_frontmatter=True,
            total_fields=5,
            nested_fields=2,
            format="yaml",
            field_types={"string": 3}
        )

        stats_dict = stats.to_dict()

        assert stats_dict["has_frontmatter"] is True
        assert stats_dict["total_fields"] == 5
        assert stats_dict["format"] == "yaml"