"""Tests for ArtifactCompiler AGENTS.md-first approach with hybrid ownership model."""

import tempfile
from datetime import datetime
from pathlib import Path

import pytest

from egokit.compiler import (
    EGOKIT_BEGIN_MARKER,
    EGOKIT_END_MARKER,
    ArtifactCompiler,
    extract_human_content,
    find_egokit_section,
)
from egokit.models import (
    CompilationContext,
    EgoConfig,
    ModeConfig,
    PolicyCharter,
    SessionConfig,
    SessionShutdown,
    SessionStartup,
    Severity,
    ToneConfig,
)


class TestArtifactCompiler:
    """Test ArtifactCompiler AGENTS.md-first approach."""

    @pytest.fixture
    def sample_context(self) -> CompilationContext:
        """Create a sample compilation context for testing."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={
                "global": {
                    "security": [
                        {
                            "id": "SEC-001",
                            "rule": "Never commit credentials or API keys",
                            "severity": "critical",
                            "detector": "secret.regex.v1",
                            "auto_fix": False,
                            "example_violation": "api_key = 'sk-123456'",
                            "example_fix": "api_key = os.environ['API_KEY']",
                            "tags": ["security", "credentials"],
                        },
                    ],
                    "code_quality": [
                        {
                            "id": "QUAL-001",
                            "rule": "Use comprehensive type hints",
                            "severity": "warning",
                            "detector": "python.ast.typehints.v1",
                            "auto_fix": True,
                            "tags": ["python", "typing"],
                        },
                    ],
                    "docs": [
                        {
                            "id": "DOCS-001",
                            "rule": "Avoid marketing superlatives in documentation",
                            "severity": "warning",
                            "detector": "docs.style.superlatives.v1",
                            "auto_fix": False,
                            "tags": ["documentation", "style"],
                        },
                    ],
                },
            },
            metadata={"description": "Test charter"},
        )

        ego_config = EgoConfig(
            role="Senior Software Engineer",
            tone=ToneConfig(
                voice="professional, precise, helpful",
                verbosity="balanced",
                formatting=["code-with-comments", "bullet-lists-for-steps"],
            ),
            defaults={
                "structure": "overview → implementation → validation",
                "code_style": "Follow project conventions",
                "testing": "unit tests with meaningful assertions",
            },
            reviewer_checklist=[
                "Code follows established patterns",
                "Type hints are comprehensive",
                "Security best practices followed",
            ],
            ask_when_unsure=[
                "Breaking API changes",
                "Security-sensitive modifications",
            ],
            modes={
                "implementer": ModeConfig(
                    verbosity="balanced",
                    focus="clean implementation",
                ),
                "security": ModeConfig(
                    verbosity="detailed",
                    focus="security implications and threat modeling",
                ),
            },
        )

        return CompilationContext(
            target_repo=Path("/test/repo"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
            generation_timestamp=datetime(2025, 1, 1, 12, 0, 0),
        )

    def test_compile_all_artifacts_completeness(self, sample_context: CompilationContext) -> None:
        """Test that all artifacts are generated for both tools."""
        compiler = ArtifactCompiler(sample_context)
        artifacts = compiler.compile_all_artifacts()

        # Check AGENTS.md is present
        assert "AGENTS.md" in artifacts

        # Check slash commands exist for both Claude and Augment
        claude_cmds = [k for k in artifacts if k.startswith(".claude/commands/")]
        augment_cmds = [k for k in artifacts if k.startswith(".augment/commands/")]

        assert len(claude_cmds) == 9, f"Expected 9 Claude commands, got {len(claude_cmds)}"
        assert len(augment_cmds) == 9, f"Expected 9 Augment commands, got {len(augment_cmds)}"

        # Check all commands use ego-* prefix
        for cmd_path in claude_cmds + augment_cmds:
            cmd_name = Path(cmd_path).stem
            assert cmd_name.startswith("ego-"), f"Command {cmd_path} should have ego- prefix"

        # Check that all artifacts have content
        for artifact_path, content in artifacts.items():
            assert content.strip(), f"Artifact {artifact_path} is empty"

    def test_generate_agents_md_template_content(self, sample_context: CompilationContext) -> None:
        """Test AGENTS.md template content structure for new projects."""
        compiler = ArtifactCompiler(sample_context)
        agents_md = compiler.generate_agents_md_template()

        # Check header
        assert "# AGENTS.md" in agents_md

        # Check project overview section (human-managed with defaults)
        assert "## Project Overview" in agents_md
        assert "Senior Software Engineer" in agents_md

        # Check EgoKit markers are present
        assert EGOKIT_BEGIN_MARKER in agents_md
        assert EGOKIT_END_MARKER in agents_md

        # Check policy compliance section within markers
        assert "## Policy Compliance" in agents_md
        assert "binding constraints" in agents_md
        assert "policies take precedence" in agents_md

        # Check severity sections
        assert "### Critical (Must Follow)" in agents_md
        assert "SEC-001" in agents_md

        # Check EgoKit commands reference
        assert "## EgoKit Commands" in agents_md
        assert "/ego-validate" in agents_md
        assert "/ego-rules" in agents_md

    def test_compile_egokit_section_has_markers(self, sample_context: CompilationContext) -> None:
        """Test that EgoKit section is wrapped with markers."""
        compiler = ArtifactCompiler(sample_context)
        section = compiler.compile_egokit_section()

        # Check markers
        assert section.startswith(EGOKIT_BEGIN_MARKER)
        assert section.endswith(EGOKIT_END_MARKER)

        # Check warning comment
        assert "Auto-generated by EgoKit" in section
        assert "Do not edit manually" in section

        # Check policy content is inside
        assert "## Policy Compliance" in section
        assert "## EgoKit Commands" in section

    def test_inject_egokit_section_new_file(self, sample_context: CompilationContext) -> None:
        """Test injection into new file generates template."""
        compiler = ArtifactCompiler(sample_context)
        result = compiler.inject_egokit_section(None)

        # Should generate full template
        assert "# AGENTS.md" in result
        assert "## Project Overview" in result
        assert EGOKIT_BEGIN_MARKER in result
        assert EGOKIT_END_MARKER in result

    def test_inject_egokit_section_with_markers(self, sample_context: CompilationContext) -> None:
        """Test injection replaces content between markers."""
        compiler = ArtifactCompiler(sample_context)

        existing = f"""# My Custom AGENTS.md

## My Custom Section
This is my custom content.

{EGOKIT_BEGIN_MARKER}
<!-- Old EgoKit content here -->
## Old Policy Compliance
Old content...
{EGOKIT_END_MARKER}

## Another Custom Section
More custom content.
"""

        result = compiler.inject_egokit_section(existing)

        # Custom content preserved
        assert "# My Custom AGENTS.md" in result
        assert "## My Custom Section" in result
        assert "This is my custom content." in result
        assert "## Another Custom Section" in result
        assert "More custom content." in result

        # Old EgoKit content replaced
        assert "Old Policy Compliance" not in result
        assert "Old content..." not in result

        # New EgoKit content present
        assert "## Policy Compliance" in result
        assert "binding constraints" in result

    def test_inject_egokit_section_without_markers(self, sample_context: CompilationContext) -> None:
        """Test injection appends to file without markers."""
        compiler = ArtifactCompiler(sample_context)

        existing = """# Existing AGENTS.md

## Custom Project Info
This is my project description.

## Custom Guidelines
- Do this
- Don't do that
"""

        result = compiler.inject_egokit_section(existing)

        # Original content preserved
        assert "# Existing AGENTS.md" in result
        assert "## Custom Project Info" in result
        assert "This is my project description." in result
        assert "## Custom Guidelines" in result

        # EgoKit section appended
        assert EGOKIT_BEGIN_MARKER in result
        assert EGOKIT_END_MARKER in result
        assert "## Policy Compliance" in result

        # EgoKit section should come after custom content
        custom_idx = result.find("## Custom Guidelines")
        egokit_idx = result.find(EGOKIT_BEGIN_MARKER)
        assert egokit_idx > custom_idx

    def test_compile_slash_commands_are_pure_prompts(self, sample_context: CompilationContext) -> None:
        """Test that slash commands are pure AI prompts, not CLI wrappers."""
        compiler = ArtifactCompiler(sample_context)
        commands = compiler.compile_slash_commands()

        # Check ego-validate command
        validate_cmd = commands["ego-validate.md"]
        assert "AGENTS.md" in validate_cmd  # References policy file
        assert "ego validate" not in validate_cmd.lower()  # No CLI invocation
        assert "```bash" not in validate_cmd  # No bash blocks

        # Check ego-rules command
        rules_cmd = commands["ego-rules.md"]
        assert "AGENTS.md" in rules_cmd

        # Check ego-security command
        security_cmd = commands["ego-security.md"]
        assert "security" in security_cmd.lower()

        # Check all commands have frontmatter
        for name, content in commands.items():
            assert content.startswith("---"), f"Command {name} missing frontmatter"
            assert "description:" in content, f"Command {name} missing description"

    def test_policy_rule_extraction_and_filtering(self, sample_context: CompilationContext) -> None:
        """Test that policy rules are properly extracted and filtered by severity."""
        compiler = ArtifactCompiler(sample_context)
        rules = compiler._extract_rules_from_charter()

        assert len(rules) == 3

        # Test rule extraction accuracy
        rule_ids = {rule.id for rule in rules}
        assert "SEC-001" in rule_ids
        assert "QUAL-001" in rule_ids
        assert "DOCS-001" in rule_ids

        # Test severity filtering
        critical_rules = [r for r in rules if r.severity == Severity.CRITICAL]
        warning_rules = [r for r in rules if r.severity == Severity.WARNING]

        assert len(critical_rules) == 1  # SEC-001
        assert len(warning_rules) == 2   # QUAL-001, DOCS-001

        # Test tag-based filtering for security
        security_rules = [r for r in rules if "security" in (r.tags or [])]
        assert len(security_rules) == 1
        assert security_rules[0].id == "SEC-001"


class TestArtifactInjector:
    """Test ArtifactInjector functionality."""

    @pytest.fixture
    def temp_repo(self) -> Path:
        """Create a temporary repository for testing."""
        with tempfile.TemporaryDirectory() as temp_dir:
            repo_path = Path(temp_dir) / "test_repo"
            repo_path.mkdir()
            yield repo_path

    def test_inject_artifacts_complete(self, temp_repo: Path) -> None:
        """Test complete artifact injection for AGENTS.md-first approach."""
        artifacts = {
            "AGENTS.md": "# Test AGENTS.md content",
            ".claude/commands/ego-validate.md": "# Validate command",
            ".augment/commands/ego-validate.md": "# Validate command",
        }

        # Inject artifacts directly (simulating CLI behavior)
        for path, content in artifacts.items():
            full_path = temp_repo / path
            full_path.parent.mkdir(parents=True, exist_ok=True)
            full_path.write_text(content)

        # Check all files were created
        assert (temp_repo / "AGENTS.md").exists()
        assert (temp_repo / ".claude" / "commands" / "ego-validate.md").exists()
        assert (temp_repo / ".augment" / "commands" / "ego-validate.md").exists()

        # Check content
        assert (temp_repo / "AGENTS.md").read_text() == "# Test AGENTS.md content"

    def test_inject_artifacts_creates_directories(self, temp_repo: Path) -> None:
        """Test that nested directories are created automatically."""
        artifacts = {
            ".claude/commands/ego-test.md": "Deep nested content",
        }

        for path, content in artifacts.items():
            full_path = temp_repo / path
            full_path.parent.mkdir(parents=True, exist_ok=True)
            full_path.write_text(content)

        nested_file = temp_repo / ".claude" / "commands" / "ego-test.md"
        assert nested_file.exists()
        assert nested_file.read_text() == "Deep nested content"

    def test_inject_artifacts_overwrites_existing(self, temp_repo: Path) -> None:
        """Test that existing artifacts are properly overwritten."""
        # Create existing file
        agents_md = temp_repo / "AGENTS.md"
        agents_md.write_text("Old content")

        artifacts = {
            "AGENTS.md": "New content",
        }

        for path, content in artifacts.items():
            full_path = temp_repo / path
            full_path.parent.mkdir(parents=True, exist_ok=True)
            full_path.write_text(content)

        assert agents_md.read_text() == "New content"


class TestMarkerParsing:
    """Test helper functions for EgoKit marker parsing."""

    def test_find_egokit_section_with_markers(self) -> None:
        """Test finding section when markers are present."""
        content = f"""# AGENTS.md

## Human Content

{EGOKIT_BEGIN_MARKER}
Policy content here
{EGOKIT_END_MARKER}

## More Human Content
"""
        result = find_egokit_section(content)
        assert result is not None
        start, end = result

        # Check we found the right positions
        assert content[start:].startswith(EGOKIT_BEGIN_MARKER)
        assert content[:end].endswith(EGOKIT_END_MARKER)

    def test_find_egokit_section_without_markers(self) -> None:
        """Test that None is returned when no markers present."""
        content = """# AGENTS.md

## Human Content
Just regular content here.
"""
        result = find_egokit_section(content)
        assert result is None

    def test_find_egokit_section_only_begin_marker(self) -> None:
        """Test that None is returned when only begin marker present."""
        content = f"""# AGENTS.md

{EGOKIT_BEGIN_MARKER}
Content without end marker
"""
        result = find_egokit_section(content)
        assert result is None

    def test_extract_human_content_with_markers(self) -> None:
        """Test extracting content before and after markers."""
        content = f"""# AGENTS.md

## Before Section
Content before.

{EGOKIT_BEGIN_MARKER}
EgoKit content
{EGOKIT_END_MARKER}

## After Section
Content after.
"""
        before, after = extract_human_content(content)

        assert "# AGENTS.md" in before
        assert "## Before Section" in before
        assert "Content before." in before
        assert EGOKIT_BEGIN_MARKER not in before

        assert "## After Section" in after
        assert "Content after." in after
        assert EGOKIT_END_MARKER not in after

    def test_extract_human_content_without_markers(self) -> None:
        """Test that all content is 'before' when no markers."""
        content = """# AGENTS.md

All content here.
"""
        before, after = extract_human_content(content)

        assert before == content
        assert after == ""

    def test_extract_human_content_markers_at_end(self) -> None:
        """Test when markers are at the end of file."""
        content = f"""# AGENTS.md

## All Human Content

{EGOKIT_BEGIN_MARKER}
EgoKit content
{EGOKIT_END_MARKER}"""

        before, after = extract_human_content(content)

        assert "# AGENTS.md" in before
        assert "## All Human Content" in before
        assert after == ""


class TestAgentParity:
    """Test that Claude and Augment agents receive identical content."""

    @pytest.fixture
    def sample_context(self) -> CompilationContext:
        """Create a sample compilation context for testing."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={
                "global": {
                    "security": [
                        {
                            "id": "SEC-001",
                            "rule": "Never commit credentials",
                            "severity": "critical",
                            "detector": "secret.regex.v1",
                            "tags": ["security"],
                        },
                    ],
                },
            },
            metadata={"description": "Test"},
        )

        ego_config = EgoConfig(
            role="Engineer",
            tone=ToneConfig(voice="professional", verbosity="balanced"),
        )

        return CompilationContext(
            target_repo=Path("/test/repo"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

    def test_claude_and_augment_commands_are_identical(
        self, sample_context: CompilationContext,
    ) -> None:
        """Verify that Claude and Augment receive identical slash command content."""
        compiler = ArtifactCompiler(sample_context)
        artifacts = compiler.compile_all_artifacts()

        # Get all command names
        claude_cmds = {
            k.replace(".claude/commands/", ""): v
            for k, v in artifacts.items()
            if k.startswith(".claude/commands/")
        }
        augment_cmds = {
            k.replace(".augment/commands/", ""): v
            for k, v in artifacts.items()
            if k.startswith(".augment/commands/")
        }

        # Same command names
        assert set(claude_cmds.keys()) == set(augment_cmds.keys())

        # Identical content for each command
        for cmd_name in claude_cmds:
            assert claude_cmds[cmd_name] == augment_cmds[cmd_name], (
                f"Command {cmd_name} differs between Claude and Augment"
            )

    def test_all_nine_commands_generated_for_both_agents(
        self, sample_context: CompilationContext,
    ) -> None:
        """Verify all 9 ego-* commands are generated for both agents."""
        compiler = ArtifactCompiler(sample_context)
        artifacts = compiler.compile_all_artifacts()

        expected_commands = {
            "ego-validate.md",
            "ego-rules.md",
            "ego-stats.md",
            "ego-suggest.md",
            "ego-checkpoint.md",
            "ego-review.md",
            "ego-security.md",
            "ego-refresh.md",
            "ego-persona.md",
        }

        claude_cmds = {
            k.replace(".claude/commands/", "")
            for k in artifacts
            if k.startswith(".claude/commands/")
        }
        augment_cmds = {
            k.replace(".augment/commands/", "")
            for k in artifacts
            if k.startswith(".augment/commands/")
        }

        assert claude_cmds == expected_commands
        assert augment_cmds == expected_commands


class TestSlashCommandContent:
    """Test slash command content quality."""

    @pytest.fixture
    def sample_context(self) -> CompilationContext:
        """Create a sample compilation context for testing."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={"global": {"security": []}},
            metadata={"description": "Test"},
        )
        ego_config = EgoConfig(
            role="Engineer",
            tone=ToneConfig(voice="professional", verbosity="balanced"),
        )
        return CompilationContext(
            target_repo=Path("/test/repo"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

    def test_all_commands_have_frontmatter_and_description(
        self, sample_context: CompilationContext,
    ) -> None:
        """Verify all commands have proper frontmatter with description."""
        compiler = ArtifactCompiler(sample_context)
        commands = compiler.compile_slash_commands()

        for cmd_name, content in commands.items():
            assert content.startswith("---"), f"{cmd_name} missing frontmatter"
            assert "description:" in content, f"{cmd_name} missing description"
            # Frontmatter should be closed
            parts = content.split("---")
            assert len(parts) >= 3, f"{cmd_name} has malformed frontmatter"

    def test_no_commands_invoke_cli(self, sample_context: CompilationContext) -> None:
        """Verify no commands contain CLI invocations (pure AI prompts)."""
        compiler = ArtifactCompiler(sample_context)
        commands = compiler.compile_slash_commands()

        cli_patterns = [
            "python3 -m egokit",
            "ego validate",
            "ego apply",
            "egokit validate",
            "```bash\nego",
            "```shell\nego",
        ]

        for cmd_name, content in commands.items():
            for pattern in cli_patterns:
                assert pattern not in content.lower(), (
                    f"{cmd_name} contains CLI invocation: {pattern}"
                )

    def test_policy_commands_reference_agents_md(
        self, sample_context: CompilationContext,
    ) -> None:
        """Verify policy-related commands reference AGENTS.md."""
        compiler = ArtifactCompiler(sample_context)
        commands = compiler.compile_slash_commands()

        # These commands should reference AGENTS.md
        policy_commands = [
            "ego-validate.md",
            "ego-rules.md",
            "ego-refresh.md",
            "ego-checkpoint.md",
            "ego-review.md",
        ]

        for cmd_name in policy_commands:
            assert "AGENTS.md" in commands[cmd_name], (
                f"{cmd_name} should reference AGENTS.md"
            )


class TestSeverityPresentation:
    """Test that severity levels are correctly presented in AGENTS.md."""

    @pytest.fixture
    def context_with_all_severities(self) -> CompilationContext:
        """Create context with rules of all severity levels."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={
                "global": {
                    "security": [
                        {
                            "id": "SEC-001",
                            "rule": "Critical security rule",
                            "severity": "critical",
                            "detector": "security.v1",
                            "tags": ["security"],
                        },
                    ],
                    "code_quality": [
                        {
                            "id": "QUAL-001",
                            "rule": "Warning quality rule",
                            "severity": "warning",
                            "detector": "quality.v1",
                            "tags": ["quality"],
                        },
                    ],
                    "docs": [
                        {
                            "id": "INFO-001",
                            "rule": "Info documentation rule",
                            "severity": "info",
                            "detector": "docs.v1",
                            "tags": ["docs"],
                        },
                    ],
                },
            },
            metadata={"description": "Test"},
        )
        ego_config = EgoConfig(
            role="Engineer",
            tone=ToneConfig(voice="professional", verbosity="balanced"),
        )
        return CompilationContext(
            target_repo=Path("/test/repo"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

    def test_critical_rules_in_must_follow_section(
        self, context_with_all_severities: CompilationContext,
    ) -> None:
        """Verify critical rules appear in 'Critical (Must Follow)' section."""
        compiler = ArtifactCompiler(context_with_all_severities)
        section = compiler.compile_egokit_section()

        assert "### Critical (Must Follow)" in section
        # SEC-001 should be in the critical section
        critical_start = section.find("### Critical (Must Follow)")
        required_start = section.find("### Required (Should Follow)")
        sec_001_pos = section.find("SEC-001")

        assert critical_start < sec_001_pos < required_start

    def test_warning_rules_in_required_section(
        self, context_with_all_severities: CompilationContext,
    ) -> None:
        """Verify warning rules appear in 'Required (Should Follow)' section."""
        compiler = ArtifactCompiler(context_with_all_severities)
        section = compiler.compile_egokit_section()

        assert "### Required (Should Follow)" in section
        # QUAL-001 should be in the required section
        required_start = section.find("### Required (Should Follow)")
        recommended_start = section.find("### Recommended")
        qual_001_pos = section.find("QUAL-001")

        assert required_start < qual_001_pos < recommended_start

    def test_info_rules_in_recommended_section(
        self, context_with_all_severities: CompilationContext,
    ) -> None:
        """Verify info rules appear in 'Recommended' section."""
        compiler = ArtifactCompiler(context_with_all_severities)
        section = compiler.compile_egokit_section()

        assert "### Recommended" in section
        # INFO-001 should be in the recommended section
        recommended_start = section.find("### Recommended")
        info_001_pos = section.find("INFO-001")

        assert recommended_start < info_001_pos


class TestSecuritySection:
    """Test security considerations section generation."""

    @pytest.fixture
    def context_with_security_rules(self) -> CompilationContext:
        """Create context with security-tagged rules of different severities."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={
                "global": {
                    "security": [
                        {
                            "id": "SEC-001",
                            "rule": "Critical security rule",
                            "severity": "critical",
                            "detector": "security.v1",
                            "tags": ["security"],
                        },
                        {
                            "id": "SEC-002",
                            "rule": "Warning security rule",
                            "severity": "warning",
                            "detector": "security.v2",
                            "tags": ["security"],
                        },
                    ],
                },
            },
            metadata={"description": "Test"},
        )
        ego_config = EgoConfig(
            role="Engineer",
            tone=ToneConfig(voice="professional", verbosity="balanced"),
        )
        return CompilationContext(
            target_repo=Path("/test/repo"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

    def test_security_section_generated_for_security_tagged_rules(
        self, context_with_security_rules: CompilationContext,
    ) -> None:
        """Verify Security Considerations section is generated."""
        compiler = ArtifactCompiler(context_with_security_rules)
        section = compiler.compile_egokit_section()

        assert "## Security Considerations" in section
        assert "SEC-001" in section
        assert "SEC-002" in section

    def test_security_rules_have_severity_markers(
        self, context_with_security_rules: CompilationContext,
    ) -> None:
        """Verify security rules have correct emoji severity markers."""
        compiler = ArtifactCompiler(context_with_security_rules)
        section = compiler.compile_egokit_section()

        # Find the security section
        security_start = section.find("## Security Considerations")
        security_section = section[security_start:]

        # Critical should have red marker
        assert "🔴" in security_section
        # Warning should have yellow marker
        assert "🟡" in security_section


class TestMarkerEdgeCases:
    """Test edge cases in marker parsing."""

    def test_markers_with_extra_whitespace(self) -> None:
        """Test that markers with surrounding whitespace are found."""
        content = f"""# AGENTS.md

  {EGOKIT_BEGIN_MARKER}
Content here
  {EGOKIT_END_MARKER}
"""
        # Should still find the section (markers are exact match)
        result = find_egokit_section(content)
        # Note: Current implementation requires exact match, so this may fail
        # This test documents expected behavior
        if result is not None:
            start, end = result
            assert EGOKIT_BEGIN_MARKER in content[start:end]

    def test_only_end_marker_present(self) -> None:
        """Test that only end marker returns None."""
        content = f"""# AGENTS.md

Content here
{EGOKIT_END_MARKER}
"""
        result = find_egokit_section(content)
        assert result is None

    def test_markers_in_wrong_order(self) -> None:
        """Test that reversed markers return None."""
        content = f"""# AGENTS.md

{EGOKIT_END_MARKER}
Content here
{EGOKIT_BEGIN_MARKER}
"""
        result = find_egokit_section(content)
        assert result is None


class TestEgoDefaultsRendering:
    """Test that ego.defaults dict is properly rendered in AGENTS.md."""

    def test_defaults_rendered_in_project_overview(self) -> None:
        """Test that ego.defaults are rendered as Development Approach section."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={"global": {}},
            metadata={},
        )
        ego_config = EgoConfig(
            role="Python Developer",
            tone=ToneConfig(voice="direct", verbosity="concise"),
            defaults={
                "error_handling": "Use explicit exceptions",
                "code_style": "Follow PEP-8",
                "testing_approach": "TDD with pytest",
            },
        )
        context = CompilationContext(
            target_repo=Path("/test"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

        compiler = ArtifactCompiler(context)
        agents_md = compiler.inject_egokit_section(None)

        # Check defaults are rendered
        assert "**Development Approach:**" in agents_md
        assert "- Error Handling: Use explicit exceptions" in agents_md
        assert "- Code Style: Follow PEP-8" in agents_md
        assert "- Testing Approach: TDD with pytest" in agents_md

    def test_no_defaults_section_when_empty(self) -> None:
        """Test that Development Approach section is omitted when no defaults."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={"global": {}},
            metadata={},
        )
        ego_config = EgoConfig(
            role="Developer",
            tone=ToneConfig(voice="direct", verbosity="concise"),
            # No defaults specified
        )
        context = CompilationContext(
            target_repo=Path("/test"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

        compiler = ArtifactCompiler(context)
        agents_md = compiler.inject_egokit_section(None)

        assert "**Development Approach:**" not in agents_md


class TestSetupCommandsFromMetadata:
    """Test that charter metadata.setup is rendered in AGENTS.md."""

    def test_custom_setup_commands_from_metadata(self) -> None:
        """Test that setup commands from metadata are rendered."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={"global": {}},
            metadata={
                "setup": {
                    "install": "uv sync --dev",
                    "test": "uv run pytest tests/",
                    "lint": "uv run ruff check src/",
                    "build": "uv build",
                },
            },
        )
        ego_config = EgoConfig(
            role="Developer",
            tone=ToneConfig(voice="direct", verbosity="concise"),
        )
        context = CompilationContext(
            target_repo=Path("/test"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

        compiler = ArtifactCompiler(context)
        agents_md = compiler.inject_egokit_section(None)

        # Check custom setup commands are rendered
        assert "## Setup Commands" in agents_md
        assert "- **Install:** `uv sync --dev`" in agents_md
        assert "- **Test:** `uv run pytest tests/`" in agents_md
        assert "- **Lint:** `uv run ruff check src/`" in agents_md
        assert "- **Build:** `uv build`" in agents_md

    def test_default_setup_commands_when_no_metadata(self) -> None:
        """Test that default setup commands are used when no metadata."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={"global": {}},
            metadata={},  # No setup in metadata
        )
        ego_config = EgoConfig(
            role="Developer",
            tone=ToneConfig(voice="direct", verbosity="concise"),
        )
        context = CompilationContext(
            target_repo=Path("/test"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

        compiler = ArtifactCompiler(context)
        agents_md = compiler.inject_egokit_section(None)

        # Check default fallback messages
        assert "## Setup Commands" in agents_md
        assert "See project README" in agents_md
        assert "See project test configuration" in agents_md


class TestSessionProtocol:
    """Test session protocol compilation in AGENTS.md and slash commands."""

    @pytest.fixture
    def context_with_session(self) -> CompilationContext:
        """Create a compilation context with session protocol enabled."""
        charter = PolicyCharter(
            version="1.1.0",
            scopes={
                "global": {
                    "security": [
                        {
                            "id": "SEC-001",
                            "rule": "Never commit secrets",
                            "severity": "critical",
                            "tags": ["security"],
                        },
                    ],
                },
            },
            session=SessionConfig(
                startup=SessionStartup(
                    read=["PROGRESS.md", "STATUS.md"],
                    run=["git status", "git log --oneline -5"],
                ),
                shutdown=SessionShutdown(
                    update=["PROGRESS.md"],
                    commit=True,
                ),
                progress_file="PROGRESS.md",
            ),
        )

        ego_config = EgoConfig(
            role="Senior Engineer",
            tone=ToneConfig(voice="professional", verbosity="balanced"),
        )

        return CompilationContext(
            target_repo=Path("/test/repo"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

    @pytest.fixture
    def context_without_session(self) -> CompilationContext:
        """Create a compilation context without session protocol."""
        charter = PolicyCharter(
            version="1.0.0",
            scopes={
                "global": {
                    "security": [
                        {
                            "id": "SEC-001",
                            "rule": "Never commit secrets",
                            "severity": "critical",
                            "tags": ["security"],
                        },
                    ],
                },
            },
        )

        ego_config = EgoConfig(
            role="Senior Engineer",
            tone=ToneConfig(voice="professional", verbosity="balanced"),
        )

        return CompilationContext(
            target_repo=Path("/test/repo"),
            policy_charter=charter,
            ego_config=ego_config,
            active_scope="global",
        )

    def test_session_protocol_section_in_agents_md(
        self, context_with_session: CompilationContext,
    ) -> None:
        """Test that session protocol section is included when configured."""
        compiler = ArtifactCompiler(context_with_session)
        section = compiler.compile_egokit_section()

        assert "## Session Protocol" in section
        assert "### Starting a Session" in section
        assert "### Ending a Session" in section
        assert "PROGRESS.md" in section
        assert "STATUS.md" in section

    def test_session_protocol_not_in_agents_md_when_disabled(
        self, context_without_session: CompilationContext,
    ) -> None:
        """Test that session protocol section is not included when not configured."""
        compiler = ArtifactCompiler(context_without_session)
        section = compiler.compile_egokit_section()

        assert "## Session Protocol" not in section

    def test_session_startup_commands_in_section(
        self, context_with_session: CompilationContext,
    ) -> None:
        """Test that startup commands are included in session protocol."""
        compiler = ArtifactCompiler(context_with_session)
        section = compiler.compile_egokit_section()

        assert "git status" in section
        assert "git log --oneline -5" in section

    def test_session_shutdown_commit_requirement(
        self, context_with_session: CompilationContext,
    ) -> None:
        """Test that commit requirement is shown when enabled."""
        compiler = ArtifactCompiler(context_with_session)
        section = compiler.compile_egokit_section()

        assert "Commit all changes" in section

    def test_ego_refresh_includes_session_startup(
        self, context_with_session: CompilationContext,
    ) -> None:
        """Test that /ego-refresh includes session startup when configured."""
        compiler = ArtifactCompiler(context_with_session)
        commands = compiler.compile_slash_commands()

        refresh_cmd = commands["ego-refresh.md"]
        assert "Session Startup" in refresh_cmd
        assert "PROGRESS.md" in refresh_cmd
        assert "STATUS.md" in refresh_cmd

    def test_ego_refresh_hint_when_no_session(
        self, context_without_session: CompilationContext,
    ) -> None:
        """Test that /ego-refresh shows hint when session not configured."""
        compiler = ArtifactCompiler(context_without_session)
        commands = compiler.compile_slash_commands()

        refresh_cmd = commands["ego-refresh.md"]
        assert "session:" in refresh_cmd
        assert "charter.yaml" in refresh_cmd

    def test_ego_checkpoint_includes_handoff(
        self, context_with_session: CompilationContext,
    ) -> None:
        """Test that /ego-checkpoint includes handoff mode when configured."""
        compiler = ArtifactCompiler(context_with_session)
        commands = compiler.compile_slash_commands()

        checkpoint_cmd = commands["ego-checkpoint.md"]
        assert "Session Handoff" in checkpoint_cmd
        assert "Progress File Template" in checkpoint_cmd
        assert "PROGRESS.md" in checkpoint_cmd

    def test_ego_checkpoint_no_handoff_when_no_session(
        self, context_without_session: CompilationContext,
    ) -> None:
        """Test that /ego-checkpoint has no handoff when session not configured."""
        compiler = ArtifactCompiler(context_without_session)
        commands = compiler.compile_slash_commands()

        checkpoint_cmd = commands["ego-checkpoint.md"]
        assert "Session Handoff" not in checkpoint_cmd
