"""Tests for doc-sync analyze module."""

from __future__ import annotations


class TestPythonChangeAnalyzer:
    """Test suite for Python code change analysis."""

    def test_detect_added_function(self):
        """Detect newly added functions."""
        from scripts.doc_sync.analyze import PythonChangeAnalyzer

        old_content = ''
        new_content = '''
def new_function(param: str) -> dict:
    """A new function."""
    return {"result": param}
'''
        analyzer = PythonChangeAnalyzer()
        changes = analyzer.analyze_diff(old_content, new_content, 'test.py')

        assert len(changes) == 1
        assert changes[0].change_type == 'added'
        assert changes[0].element_name == 'new_function'
        assert changes[0].element_type == 'function'

    def test_detect_removed_function(self):
        """Detect removed functions."""
        from scripts.doc_sync.analyze import PythonChangeAnalyzer

        old_content = '''
def old_function(param: str) -> dict:
    """An old function."""
    return {"result": param}
'''
        new_content = ''
        analyzer = PythonChangeAnalyzer()
        changes = analyzer.analyze_diff(old_content, new_content, 'test.py')

        assert len(changes) == 1
        assert changes[0].change_type == 'removed'
        assert changes[0].element_name == 'old_function'
        assert changes[0].breaking_change is True

    def test_detect_modified_signature(self):
        """Detect modified function signatures."""
        from scripts.doc_sync.analyze import PythonChangeAnalyzer

        old_content = '''
def my_function(param: str) -> dict:
    """A function."""
    return {}
'''
        new_content = '''
def my_function(param: str, timeout: int = 30) -> dict:
    """A function."""
    return {}
'''
        analyzer = PythonChangeAnalyzer()
        changes = analyzer.analyze_diff(old_content, new_content, 'test.py')

        assert len(changes) == 1
        assert changes[0].change_type == 'modified'
        assert changes[0].element_name == 'my_function'
        assert 'timeout' in changes[0].new_signature

    def test_detect_breaking_signature_change(self):
        """Detect breaking signature changes (required param added)."""
        from scripts.doc_sync.analyze import PythonChangeAnalyzer

        old_content = """
def my_function(param: str) -> dict:
    return {}
"""
        new_content = """
def my_function(param: str, required_param: int) -> dict:
    return {}
"""
        analyzer = PythonChangeAnalyzer()
        changes = analyzer.analyze_diff(old_content, new_content, 'test.py')

        assert len(changes) == 1
        assert changes[0].breaking_change is True

    def test_detect_added_class(self):
        """Detect newly added classes."""
        from scripts.doc_sync.analyze import PythonChangeAnalyzer

        old_content = ''
        new_content = '''
class NewClass:
    """A new class."""

    def method(self) -> None:
        pass
'''
        analyzer = PythonChangeAnalyzer()
        changes = analyzer.analyze_diff(old_content, new_content, 'test.py')

        # Should detect class and method
        class_changes = [c for c in changes if c.element_type == 'class']
        assert len(class_changes) == 1
        assert class_changes[0].element_name == 'NewClass'

    def test_detect_docstring_change(self):
        """Detect docstring modifications."""
        from scripts.doc_sync.analyze import PythonChangeAnalyzer

        old_content = '''
def my_function(param: str) -> dict:
    """Old docstring."""
    return {}
'''
        new_content = '''
def my_function(param: str) -> dict:
    """New docstring with more details."""
    return {}
'''
        analyzer = PythonChangeAnalyzer()
        changes = analyzer.analyze_diff(old_content, new_content, 'test.py')

        assert len(changes) == 1
        assert changes[0].docstring_changed is True

    def test_ignore_private_functions(self):
        """Private functions (starting with _) should be flagged as non-public."""
        from scripts.doc_sync.analyze import PythonChangeAnalyzer

        old_content = ''
        new_content = """
def _private_function(param: str) -> dict:
    return {}
"""
        analyzer = PythonChangeAnalyzer()
        changes = analyzer.analyze_diff(old_content, new_content, 'test.py')

        assert len(changes) == 1
        assert changes[0].is_public is False

    def test_detect_return_type_change(self):
        """Detect return type changes."""
        from scripts.doc_sync.analyze import PythonChangeAnalyzer

        old_content = """
def my_function(param: str) -> dict:
    return {}
"""
        new_content = """
def my_function(param: str) -> list:
    return []
"""
        analyzer = PythonChangeAnalyzer()
        changes = analyzer.analyze_diff(old_content, new_content, 'test.py')

        assert len(changes) == 1
        assert changes[0].breaking_change is True


class TestDocumentAnalyzer:
    """Test suite for Markdown document analysis."""

    def test_extract_code_references(self):
        """Extract code references from markdown."""
        from scripts.doc_sync.analyze import DocumentAnalyzer

        content = """
# Documentation

Use the `BaseAction` class to create actions.
Call `execute()` method to run.
Import from `synapse_sdk.plugins.action`.
"""
        analyzer = DocumentAnalyzer()
        result = analyzer.analyze_document('test.md', content)

        assert 'BaseAction' in result.code_references
        assert 'execute()' in result.code_references

    def test_extract_code_examples(self):
        """Extract code examples from markdown."""
        from scripts.doc_sync.analyze import DocumentAnalyzer

        content = """
# Example

```python
from synapse_sdk import BaseAction

class MyAction(BaseAction):
    pass
```
"""
        analyzer = DocumentAnalyzer()
        result = analyzer.analyze_document('test.md', content)

        assert len(result.code_examples) == 1
        assert 'BaseAction' in result.code_examples[0]

    def test_extract_api_signatures(self):
        """Extract API signatures from markdown."""
        from scripts.doc_sync.analyze import DocumentAnalyzer

        content = """
## API

```python
def execute(params: dict, timeout: int = 30) -> dict:
    pass
```
"""
        analyzer = DocumentAnalyzer()
        result = analyzer.analyze_document('test.md', content)

        assert 'execute' in result.api_signatures

    def test_extract_signature_lines_with_function(self):
        """Extract function signature with line numbers from markdown."""
        from scripts.doc_sync.analyze import DocumentAnalyzer

        content = '''# API Reference

## my_function

**Signature:**

```python
def my_function(name: str) -> str:
    """Generate greeting."""
```

This function generates a greeting.
'''
        analyzer = DocumentAnalyzer()
        result = analyzer.analyze_document('test.md', content)

        assert 'my_function' in result.signature_lines
        lines = result.signature_lines['my_function']
        assert len(lines) >= 1
        # Check that line number is tracked (should be around line 8)
        line_num, line_content = lines[0]
        assert line_num > 0
        assert 'def my_function' in line_content

    def test_extract_signature_lines_with_class(self):
        """Extract class signature with line numbers from markdown."""
        from scripts.doc_sync.analyze import DocumentAnalyzer

        content = '''# API Reference

## MyClass

```python
class MyClass:
    """A sample class."""

    def method(self) -> None:
        pass
```
'''
        analyzer = DocumentAnalyzer()
        result = analyzer.analyze_document('test.md', content)

        assert 'MyClass' in result.signature_lines
        lines = result.signature_lines['MyClass']
        assert len(lines) >= 1
        line_num, line_content = lines[0]
        assert line_num > 0
        assert 'class MyClass' in line_content

    def test_extract_signature_lines_multiple(self):
        """Extract multiple signatures with their line numbers."""
        from scripts.doc_sync.analyze import DocumentAnalyzer

        content = """# API

```python
def func_one(a: int) -> int:
    pass
```

```python
def func_two(b: str) -> str:
    pass
```
"""
        analyzer = DocumentAnalyzer()
        result = analyzer.analyze_document('test.md', content)

        assert 'func_one' in result.signature_lines
        assert 'func_two' in result.signature_lines
        # func_one should have a smaller line number than func_two
        line_one = result.signature_lines['func_one'][0][0]
        line_two = result.signature_lines['func_two'][0][0]
        assert line_one < line_two


class TestCodeToDocMapper:
    """Test suite for code-to-documentation mapping."""

    def test_map_api_reference(self):
        """Map Python file to API reference docs."""
        from scripts.doc_sync.analyze import CodeToDocMapper

        mapper = CodeToDocMapper('.github/doc-sync-mapping.yaml')
        mappings = mapper.map_code_to_docs('synapse_sdk/plugins/action.py')

        # Should map to API reference
        assert any('reference' in m.doc_path for m in mappings)

    def test_map_conceptual_docs(self):
        """Map Python file to conceptual docs."""
        from scripts.doc_sync.analyze import CodeToDocMapper

        mapper = CodeToDocMapper('.github/doc-sync-mapping.yaml')
        mappings = mapper.map_code_to_docs('synapse_sdk/plugins/action.py')

        # Should map to conceptual docs
        assert any('plugins' in m.doc_path for m in mappings)

    def test_map_project_docs(self):
        """Map public API changes to project docs."""
        from scripts.doc_sync.analyze import CodeToDocMapper

        mapper = CodeToDocMapper('.github/doc-sync-mapping.yaml')
        mappings = mapper.map_code_to_docs('synapse_sdk/clients/base.py')

        # Should include AGENT.md or README.md
        assert any('AGENT.md' in m.doc_path or 'README.md' in m.doc_path for m in mappings)
