"""
Tests for the works search agent.
"""

import pytest
import asyncio
from src.works_search_agent import (
    search_work,
    search_work_direct,
    normalize_string,
    titles_match,
    author_in_work
)


class TestNormalizeString:
    """Tests for string normalization."""
    
    def test_basic_normalization(self):
        """Test basic normalization: lowercase and whitespace."""
        result = normalize_string("The Quick Brown Fox")
        assert result == "the quick brown fox"
    
    def test_punctuation_removal(self):
        """Test that common punctuation is removed."""
        result = normalize_string("Title: A Study (2024)")
        assert result == "title a study 2024"
    
    def test_multiple_spaces(self):
        """Test that multiple spaces are collapsed."""
        result = normalize_string("Title   with    spaces")
        assert result == "title with spaces"
    
    def test_special_dashes(self):
        """Test that em-dashes and en-dashes are handled."""
        result = normalize_string("Title–with—dashes")
        assert result == "title with dashes"
    
    def test_empty_string(self):
        """Test empty string handling."""
        result = normalize_string("")
        assert result == ""
    
    def test_none_handling(self):
        """Test None handling."""
        result = normalize_string(None)
        assert result == ""


class TestTitlesMatch:
    """Tests for title matching."""
    
    def test_exact_match(self):
        """Test exact match after normalization."""
        title1 = "The State of OA"
        title2 = "The State of OA"
        assert titles_match(title1, title2)
    
    def test_case_insensitive(self):
        """Test case-insensitive matching."""
        title1 = "The State of OA"
        title2 = "the state of oa"
        assert titles_match(title1, title2)
    
    def test_punctuation_differences(self):
        """Test matching with different punctuation."""
        title1 = "Title: A Study"
        title2 = "Title, A Study"
        assert titles_match(title1, title2)
    
    def test_subtitle_handling(self):
        """Test that subtitles are handled (one contains the other)."""
        title1 = "The Impact of Open Access on Research Distribution"
        title2 = "The Impact of Open Access on Research Distribution: A Study"
        # This should match as one is substring of other
        # But only for long titles (>20 chars)
        assert titles_match(title1, title2)
    
    def test_different_titles(self):
        """Test that different titles don't match."""
        title1 = "Completely Different Title"
        title2 = "Another Unrelated Paper"
        assert not titles_match(title1, title2)
    
    def test_short_titles_no_substring(self):
        """Test that short titles require exact match."""
        title1 = "Short"
        title2 = "Short Title"
        # Should NOT match because titles are too short for substring matching
        assert not titles_match(title1, title2)


class TestAuthorInWork:
    """Tests for author detection in works."""
    
    def test_author_found(self):
        """Test finding an author in a work."""
        work = {
            'authorships': [
                {
                    'author': {
                        'display_name': 'Jason Priem',
                        'id': 'https://openalex.org/A5023888391',
                        'orcid': 'https://orcid.org/0000-0001-6187-6610'
                    }
                }
            ]
        }
        
        found, author_id, orcid = author_in_work('Priem', work)
        assert found is True
        assert author_id == 'https://openalex.org/A5023888391'
        assert orcid == 'https://orcid.org/0000-0001-6187-6610'
    
    def test_author_not_found(self):
        """Test when author is not in work."""
        work = {
            'authorships': [
                {
                    'author': {
                        'display_name': 'Jason Priem',
                        'id': 'https://openalex.org/A5023888391',
                        'orcid': 'https://orcid.org/0000-0001-6187-6610'
                    }
                }
            ]
        }
        
        found, author_id, orcid = author_in_work('Einstein', work)
        assert found is False
        assert author_id is None
        assert orcid is None
    
    def test_multiple_authors(self):
        """Test finding specific author among multiple authors."""
        work = {
            'authorships': [
                {
                    'author': {
                        'display_name': 'John Smith',
                        'id': 'https://openalex.org/A1',
                        'orcid': None
                    }
                },
                {
                    'author': {
                        'display_name': 'Jane Doe',
                        'id': 'https://openalex.org/A2',
                        'orcid': 'https://orcid.org/0000-0002-1234-5678'
                    }
                }
            ]
        }
        
        found, author_id, orcid = author_in_work('Doe', work)
        assert found is True
        assert author_id == 'https://openalex.org/A2'
        assert orcid == 'https://orcid.org/0000-0002-1234-5678'
    
    def test_case_insensitive_search(self):
        """Test that author search is case insensitive."""
        work = {
            'authorships': [
                {
                    'author': {
                        'display_name': 'Marie Curie',
                        'id': 'https://openalex.org/A123',
                        'orcid': None
                    }
                }
            ]
        }
        
        found, author_id, orcid = author_in_work('curie', work)
        assert found is True
        assert author_id == 'https://openalex.org/A123'
    
    def test_empty_authorships(self):
        """Test handling of work with no authorships."""
        work = {'authorships': []}
        
        found, author_id, orcid = author_in_work('Smith', work)
        assert found is False
        assert author_id is None
        assert orcid is None


class TestSearchWorkDirect:
    """Tests for direct PyAlex search."""
    
    @pytest.mark.integration
    def test_successful_search_with_author(self):
        """Test successful search with author validation."""
        result = search_work_direct(
            title='The state of OA: a large-scale analysis of the prevalence and impact of Open Access articles',
            author_last_name='Priem',
            year=2018
        )
        
        assert result is not None
        assert 'openalex_id' in result
        assert 'doi' in result
        assert 'title' in result
        assert result['author_found_in_work'] is True
        assert result['match_confidence'] == 'direct'
    
    @pytest.mark.integration
    def test_wrong_author_rejected(self):
        """Test that work is rejected if author doesn't match."""
        result = search_work_direct(
            title='The state of OA: a large-scale analysis of the prevalence and impact of Open Access articles',
            author_last_name='Einstein',  # Wrong author
            year=2018
        )
        
        # Should not find a match because author validation fails
        assert result is None
    
    @pytest.mark.integration
    def test_search_without_author(self):
        """Test search without author validation."""
        result = search_work_direct(
            title='The state of OA: a large-scale analysis of the prevalence and impact of Open Access articles',
            year=2018
        )
        
        assert result is not None
        assert 'openalex_id' in result
        assert 'doi' in result
        # No author was specified, so author fields should be None
        assert result['author_openalex_id'] is None
        assert result['author_orcid'] is None
        assert result['author_found_in_work'] is True  # True because no validation needed
    
    @pytest.mark.integration
    def test_nonexistent_paper(self):
        """Test search for non-existent paper."""
        result = search_work_direct(
            title='This Paper Does Not Exist At All Nowhere Never',
            author_last_name='Nobody',
            year=2099
        )
        
        assert result is None


class TestSearchWork:
    """Tests for the main search_work function."""
    
    @pytest.mark.integration
    @pytest.mark.asyncio
    async def test_successful_search(self):
        """Test successful work search."""
        result = await search_work(
            title='The state of OA: a large-scale analysis of the prevalence and impact of Open Access articles',
            author_last_name='Priem',
            year=2018
        )
        
        assert result['status'] == 'success'
        assert result['work'] is not None
        assert result['work']['openalex_id']
        assert result['work']['doi']
        assert result['work']['title']
        assert result['work']['author_openalex_id']
        assert result['work']['author_orcid']
        assert result['work']['author_found_in_work'] is True
        assert result['work']['match_confidence'] == 'direct'
    
    @pytest.mark.integration
    @pytest.mark.asyncio
    async def test_author_validation_failure(self):
        """Test that incorrect author causes not_found."""
        result = await search_work(
            title='The state of OA: a large-scale analysis of the prevalence and impact of Open Access articles',
            author_last_name='WrongAuthor',
            year=2018
        )
        
        assert result['status'] == 'not_found'
        assert result['work'] is None
        assert 'message' in result
    
    @pytest.mark.integration
    @pytest.mark.asyncio
    async def test_search_without_author(self):
        """Test search without author specification."""
        result = await search_work(
            title='The state of OA: a large-scale analysis of the prevalence and impact of Open Access articles',
            year=2018
        )
        
        assert result['status'] == 'success'
        assert result['work'] is not None
        assert result['work']['author_openalex_id'] is None
        assert result['work']['author_orcid'] is None
    
    @pytest.mark.integration
    @pytest.mark.asyncio
    async def test_not_found(self):
        """Test search for non-existent work."""
        result = await search_work(
            title='Completely Nonexistent Paper Title XYZ123',
            author_last_name='Nobody'
        )
        
        assert result['status'] == 'not_found'
        assert result['work'] is None
    
    @pytest.mark.asyncio
    async def test_empty_title(self):
        """Test that empty title returns error."""
        result = await search_work(title='')
        
        assert result['status'] == 'not_found'
        assert result['work'] is None


class TestOutputSchema:
    """Tests for output schema validation."""
    
    @pytest.mark.integration
    @pytest.mark.asyncio
    async def test_success_schema(self):
        """Test that successful response matches expected schema."""
        result = await search_work(
            title='The state of OA: a large-scale analysis of the prevalence and impact of Open Access articles',
            author_last_name='Priem',
            year=2018
        )
        
        # Check required top-level keys
        assert 'status' in result
        assert 'work' in result
        
        # Check status value
        assert result['status'] in ['success', 'not_found', 'error']
        
        # If success, check work structure
        if result['status'] == 'success':
            work = result['work']
            assert work is not None
            assert 'openalex_id' in work
            assert 'doi' in work
            assert 'title' in work
            assert 'author_openalex_id' in work
            assert 'author_orcid' in work
            assert 'author_found_in_work' in work
            assert 'match_confidence' in work
            assert work['match_confidence'] in ['direct', 'agent']
    
    @pytest.mark.asyncio
    async def test_not_found_schema(self):
        """Test that not_found response has correct structure."""
        result = await search_work(
            title='Nonexistent Paper XYZ'
        )
        
        assert result['status'] == 'not_found'
        assert result['work'] is None
        assert 'message' in result
