"""Test suite for ClaudeValidator."""

import tempfile
from pathlib import Path

import pytest

from ai_forge.validator import ClaudeValidator


@pytest.fixture
def temp_dir():
    """Create a temporary directory for test files."""
    with tempfile.TemporaryDirectory() as temp_dir:
        yield Path(temp_dir)


@pytest.fixture
def valid_document_content():
    """Valid document content for testing."""
    return """# AI Forge Documentation

## Core Mission
This is the core mission of the AI Forge project.

## Fundamental Principles
These are the fundamental principles that guide the project.

## Agent Specifications

### Orchestrator (Agent Framework)
**ROLE**: Master coordinator and execution engine
**SCOPE**: End-to-end orchestration of all AI Forge workflows

### Helios (Interface Agent)
**ROLE**: Primary interface between users and the AI Forge system
**SCOPE**: User interaction, query processing, and response formatting

## Communication & Artifact Formats

### Task Assignment Format
```
@{agent_name}:
TASK: {single_atomic_objective}
CONTEXT: @{reference_artifact_1}, @{reference_artifact_2}
MODE: {coordination | implementation}
OUTPUT: {format_specification}
BUDGET: {strict_token_limit}
```

### Bug Report Format (`BUG_REPORT.md`)
```markdown
## Bug Report: {timestamp}
- **ID**: BUG-{hash}
- **Failing Artifact**: @{artifact_path}:{version}
- **Suspected Location**: line {number}
- **Error Log Summary**: {one_line_summary_of_error}
- **Root Cause Analysis**: {concise_explanation_of_the_bug}
- **Suggested Fix**: {brief_description_of_the_proposed_solution}
```

## Execution Workflow

### Phase 0: Strategic Planning
1.  **REQUEST** → `ATHENA` works with the user to define a high-level goal.
2.  **DECOMPOSITION** → `ATHENA` creates the project charter and breaks the goal 
    into epics on the `TASK_BOARD.md`.

### Phase 1: Tactical Execution Cycle (per epic)
3.  **BREAKDOWN** → The `HERMES` picks an epic and defines the necessary 
    `contracts` (API specs, data models).
4.  **PARALLEL ASSIGNMENT** → The `HERMES` assigns implementation tasks 
    (to `HEPHAESTUS`) and test generation tasks (to `ASTRAEA`) simultaneously.
"""


@pytest.fixture
def valid_document_path(temp_dir, valid_document_content):
    """Create a valid document file for testing."""
    doc_path = temp_dir / "valid_document.md"
    doc_path.write_text(valid_document_content)
    return doc_path


@pytest.fixture
def invalid_document_content():
    """Invalid document content missing required sections."""
    return """# Incomplete Document

## Core Mission
This document is missing required sections.

## Fundamental Principles
Only has a few sections.
"""


@pytest.fixture
def invalid_document_path(temp_dir, invalid_document_content):
    """Create an invalid document file for testing."""
    doc_path = temp_dir / "invalid_document.md"
    doc_path.write_text(invalid_document_content)
    return doc_path


class TestClaudeValidator:
    """Test suite for ClaudeValidator class."""

    def test_validator_initialization(self, valid_document_path):
        """Test that validator initializes correctly."""
        validator = ClaudeValidator(valid_document_path)
        assert validator.document_path == valid_document_path
        assert validator.validation_results == []

    def test_validate_document_with_valid_content(self, valid_document_path):
        """Test validation of a valid document."""
        validator = ClaudeValidator(valid_document_path)
        result = validator.validate_document()

        assert result is True
        assert len(validator.validation_results) == 5  # Number of validation checks

        # Check that all validations passed
        for validation in validator.validation_results:
            assert validation["status"] == "PASS"

    def test_validate_document_with_invalid_content(self, invalid_document_path):
        """Test validation of an invalid document."""
        validator = ClaudeValidator(invalid_document_path)
        result = validator.validate_document()

        assert result is False
        assert len(validator.validation_results) > 0

        # Check that at least one validation failed
        has_failure = any(
            validation["status"] == "FAIL"
            for validation in validator.validation_results
        )
        assert has_failure

    def test_validate_document_file_not_found(self, temp_dir):
        """Test validation when document file doesn't exist."""
        nonexistent_path = temp_dir / "nonexistent.md"
        validator = ClaudeValidator(nonexistent_path)

        result = validator.validate_document()
        assert result is False

    def test_validate_structure_with_all_sections(self, valid_document_path):
        """Test structure validation with all required sections."""
        validator = ClaudeValidator(valid_document_path)
        content = valid_document_path.read_text()

        result = validator._validate_structure(content)
        assert result is True

        # Check validation result
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "PASS"
        assert validator.validation_results[0]["check"] == "Structure"

    def test_validate_structure_with_missing_sections(self, invalid_document_path):
        """Test structure validation with missing sections."""
        validator = ClaudeValidator(invalid_document_path)
        content = invalid_document_path.read_text()

        result = validator._validate_structure(content)
        assert result is False

        # Check validation result
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "FAIL"
        assert validator.validation_results[0]["check"] == "Structure"
        assert "Missing sections" in validator.validation_results[0]["details"]

    def test_validate_agent_references_valid(self, temp_dir):
        """Test agent reference validation with valid references."""
        content = """
        ## Agent Specifications
        
        ### Orchestrator (Agent Framework)
        **ROLE**: Master coordinator
        
        ### Helios (Interface Agent)
        **ROLE**: Primary interface
        
        ## Communication
        Use @Orchestrator: for coordination and @Helios: for user interaction.
        """

        doc_path = temp_dir / "agent_refs.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_agent_references(content)

        assert result is True
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "PASS"
        assert validator.validation_results[0]["check"] == "Agent References"

    def test_validate_agent_references_invalid(self, temp_dir):
        """Test agent reference validation with invalid references."""
        content = """
        ## Agent Specifications
        
        ### Orchestrator (Agent Framework)
        **ROLE**: Master coordinator
        
        ## Communication
        Use @Orchestrator: for coordination and @InvalidAgent: for something.
        """

        doc_path = temp_dir / "invalid_agent_refs.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_agent_references(content)

        assert result is False
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "FAIL"
        assert validator.validation_results[0]["check"] == "Agent References"
        assert "InvalidAgent" in validator.validation_results[0]["details"]

    def test_validate_agent_references_allowed_non_agent_refs(self, temp_dir):
        """Test that allowed non-agent references don't cause failures."""
        content = """
        ## Agent Specifications
        
        ### Orchestrator (Agent Framework)
        **ROLE**: Master coordinator
        
        ## Communication
        Use @openapi:v2 and @datamodel:v1 references.
        """

        doc_path = temp_dir / "allowed_refs.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_agent_references(content)

        assert result is True
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "PASS"

    def test_validate_artifact_references_valid(self, temp_dir):
        """Test artifact reference validation with valid references."""
        content = """
        ## Key Artifacts
        
        Important artifacts: @artifacts/schema:v1, @artifacts/config:v2
        """

        doc_path = temp_dir / "artifact_refs.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_artifact_references(content)

        assert result is True
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "PASS"
        assert validator.validation_results[0]["check"] == "Artifact References"

    def test_validate_artifact_references_malformed(self, temp_dir):
        """Test artifact reference validation with malformed references."""
        content = """
        ## Key Artifacts
        
        Malformed artifacts: @artifacts/schema, @artifacts/config
        """

        doc_path = temp_dir / "malformed_artifacts.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_artifact_references(content)

        assert result is False
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "FAIL"
        assert validator.validation_results[0]["check"] == "Artifact References"
        assert (
            "Malformed artifact references"
            in validator.validation_results[0]["details"]
        )

    def test_validate_token_optimization_valid(self, temp_dir):
        """Test token optimization validation with valid budget."""
        content = """
        ## Token Optimization Rules
        
        Allocate 40% budget for analysis and 60% budget for implementation.
        """

        doc_path = temp_dir / "token_budget.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_token_optimization(content)

        assert result is True
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "PASS"
        assert validator.validation_results[0]["check"] == "Token Budget"

    def test_validate_token_optimization_invalid_budget(self, temp_dir):
        """Test token optimization validation with invalid budget."""
        content = """
        ## Token Optimization Rules
        
        Allocate 40% budget for analysis and 70% budget for implementation.
        """

        doc_path = temp_dir / "invalid_budget.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_token_optimization(content)

        assert result is False
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "FAIL"
        assert validator.validation_results[0]["check"] == "Token Budget"
        assert "should be 100%" in validator.validation_results[0]["details"]

    def test_validate_token_optimization_no_budget(self, temp_dir):
        """Test token optimization validation with no budget specified."""
        content = """
        ## Token Optimization Rules
        
        No specific budget allocation mentioned.
        """

        doc_path = temp_dir / "no_budget.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_token_optimization(content)

        assert result is True
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "PASS"

    def test_validate_hooks_syntax_valid(self, temp_dir):
        """Test hook syntax validation with valid shell scripts."""
        content = """## Hooks

```bash
#!/bin/bash
echo "Valid script"
```"""

        doc_path = temp_dir / "valid_hooks.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_hooks_syntax(content)

        assert result is True
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "PASS"
        assert validator.validation_results[0]["check"] == "Hook Syntax"

    def test_validate_hooks_syntax_missing_shebang(self, temp_dir):
        """Test hook syntax validation with missing shebang."""
        content = """## Hooks

```bash
echo "Missing shebang"
```"""

        doc_path = temp_dir / "missing_shebang.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_hooks_syntax(content)

        assert result is False
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "FAIL"
        assert validator.validation_results[0]["check"] == "Hook Syntax"
        assert "Missing shebang" in validator.validation_results[0]["details"]

    def test_validate_hooks_syntax_dangerous_commands(self, temp_dir):
        """Test hook syntax validation with dangerous commands."""
        content = """## Hooks

```bash
#!/bin/bash
rm -rf /
```"""

        doc_path = temp_dir / "dangerous_hooks.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_hooks_syntax(content)

        assert result is False
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "FAIL"
        assert validator.validation_results[0]["check"] == "Hook Syntax"
        assert "dangerous command" in validator.validation_results[0]["details"]

    def test_validate_hooks_syntax_no_scripts(self, temp_dir):
        """Test hook syntax validation with no shell scripts."""
        content = """
        ## Hooks
        
        No shell scripts in this document.
        """

        doc_path = temp_dir / "no_scripts.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator._validate_hooks_syntax(content)

        assert result is True
        assert len(validator.validation_results) == 1
        assert validator.validation_results[0]["status"] == "PASS"
        assert validator.validation_results[0]["check"] == "Hook Syntax"

    def test_multiple_validation_checks(self, temp_dir):
        """Test that multiple validation checks work together."""
        content = """
        ## Core Mission
        Test mission
        
        ## Fundamental Principles
        Test principles
        
        ## Agent Specifications
        ### TestAgent (Test)
        **ROLE**: Test agent
        
        ## Communication & Artifact Formats
        Use @TestAgent: for testing
        
        ## Execution Workflow
        Test workflow execution
        """

        doc_path = temp_dir / "multi_validation.md"
        doc_path.write_text(content)

        validator = ClaudeValidator(doc_path)
        result = validator.validate_document()

        assert result is True
        assert len(validator.validation_results) == 5

        # All checks should pass
        for validation in validator.validation_results:
            assert validation["status"] == "PASS"

    def test_validation_results_persistence(self, valid_document_path):
        """Test that validation results are properly stored."""
        validator = ClaudeValidator(valid_document_path)

        # Initially empty
        assert len(validator.validation_results) == 0

        # After validation, should have results
        validator.validate_document()
        assert len(validator.validation_results) > 0

        # Results should have correct structure
        for result in validator.validation_results:
            assert "check" in result
            assert "status" in result
            assert "details" in result
            assert result["status"] in ["PASS", "FAIL"]

    def test_edge_case_empty_document(self, temp_dir):
        """Test validation with empty document."""
        doc_path = temp_dir / "empty.md"
        doc_path.write_text("")

        validator = ClaudeValidator(doc_path)
        result = validator.validate_document()

        assert result is False
        # Should fail structure validation at minimum
        structure_validation = next(
            (v for v in validator.validation_results if v["check"] == "Structure"), None
        )
        assert structure_validation is not None
        assert structure_validation["status"] == "FAIL"

    def test_edge_case_unicode_content(self, temp_dir):
        """Test validation with unicode content."""
        content = """
        ## Core Mission
        Test with unicode: café, naïve, résumé
        
        ## Fundamental Principles
        Unicode principles: 测试, тест, テスト
        
        ## Agent Specifications
        ### TestAgent (Test)
        **ROLE**: Unicode test agent
        
        ## Communication & Artifact Formats
        Test patterns
        
        ## Execution Workflow
        Test workflow execution
        """

        doc_path = temp_dir / "unicode.md"
        doc_path.write_text(content, encoding="utf-8")

        validator = ClaudeValidator(doc_path)
        result = validator.validate_document()

        # Should handle unicode content gracefully
        assert result is True
