"""
Unit tests for disambiguation result schema validation.

Tests all 4 status types: success, ambiguous, not_found, error
"""

import pytest
import sys
import os

# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from src.schemas.disambiguation_result import (
    DISAMBIGUATION_RESULT_SCHEMA,
    validate_disambiguation_result
)


# Fixtures for different result types

@pytest.fixture
def success_result():
    """Valid success result with single candidate."""
    return {
        "status": "success",
        "author_candidates": [
            {
                "rank": 1,
                "author": {
                    "openalex_id": "A5074091984",
                    "openalex_url": "https://openalex.org/A5074091984",
                    "name": "Yves-Alain Barde",
                    "orcid": "0000-0002-7627-461X",
                    "institution": "Cardiff University",
                    "works_count": 177,
                    "cited_by_count": 36058
                },
                "evidence": [
                    "ORCID exact match",
                    "Institution match",
                    "Publications verified"
                ],
                "concerns": []
            }
        ],
        "search_summary": {
            "embo_found": False,
            "orcid_source": "User-provided",
            "candidates_evaluated": 1,
            "disambiguation_needed": False
        },
        "comments": "Single unambiguous match found via ORCID"
    }


@pytest.fixture
def ambiguous_result():
    """Valid ambiguous result with multiple candidates."""
    return {
        "status": "ambiguous",
        "author_candidates": [
            {
                "rank": 1,
                "author": {
                    "openalex_id": "A123456",
                    "openalex_url": "https://openalex.org/A123456",
                    "name": "John Smith",
                    "orcid": None,
                    "institution": "MIT",
                    "works_count": 50,
                    "cited_by_count": 1000
                },
                "evidence": ["Institution match", "1 publication match"],
                "concerns": ["Timeline uncertain"]
            },
            {
                "rank": 2,
                "author": {
                    "openalex_id": "A789012",
                    "openalex_url": "https://openalex.org/A789012",
                    "name": "J. Smith",
                    "orcid": None,
                    "institution": "Stanford",
                    "works_count": 30,
                    "cited_by_count": 500
                },
                "evidence": ["Name match", "Similar field"],
                "concerns": ["Institution mismatch", "No publication matches"]
            }
        ],
        "search_summary": {
            "embo_found": False,
            "orcid_source": "Unknown",
            "candidates_evaluated": 5,
            "disambiguation_needed": True
        },
        "message": "Multiple plausible candidates found. Ranked by likelihood.",
        "recommendation": "Provide known publication titles or ORCID for disambiguation",
        "comments": "Multiple candidates with similar names in related fields"
    }


@pytest.fixture
def not_found_result():
    """Valid not_found result with empty candidates."""
    return {
        "status": "not_found",
        "author_candidates": [],
        "search_summary": {
            "embo_found": False,
            "orcid_source": "Unknown",
            "candidates_evaluated": 0,
            "disambiguation_needed": False
        },
        "message": "No matching author profile found in OpenAlex",
        "possible_reasons": [
            "Researcher not yet indexed in OpenAlex",
            "Name variation not captured",
            "Very early career (no publications)"
        ],
        "recommendation": "Verify researcher name spelling and try with publication titles",
        "comments": "Exhaustive search returned no matches"
    }


@pytest.fixture
def error_result():
    """Valid error result."""
    return {
        "status": "error",
        "author_candidates": [],
        "search_summary": {
            "embo_found": False,
            "orcid_source": "Unknown",
            "candidates_evaluated": 0,
            "disambiguation_needed": False
        },
        "error": "API rate limit exceeded",
        "message": "An error occurred during disambiguation",
        "comments": "Failed to complete disambiguation due to API error"
    }


# Tests for valid results

class TestValidResults:
    """Tests for valid result structures."""

    def test_success_result_is_valid(self, success_result):
        """Test that success result passes validation."""
        is_valid, errors = validate_disambiguation_result(success_result)
        assert is_valid, f"Validation errors: {errors}"
        assert len(errors) == 0

    def test_ambiguous_result_is_valid(self, ambiguous_result):
        """Test that ambiguous result passes validation."""
        is_valid, errors = validate_disambiguation_result(ambiguous_result)
        assert is_valid, f"Validation errors: {errors}"
        assert len(errors) == 0

    def test_not_found_result_is_valid(self, not_found_result):
        """Test that not_found result passes validation."""
        is_valid, errors = validate_disambiguation_result(not_found_result)
        assert is_valid, f"Validation errors: {errors}"
        assert len(errors) == 0

    def test_error_result_is_valid(self, error_result):
        """Test that error result passes validation."""
        is_valid, errors = validate_disambiguation_result(error_result)
        assert is_valid, f"Validation errors: {errors}"
        assert len(errors) == 0


# Tests for missing required fields

class TestMissingRequiredFields:
    """Tests for missing required fields."""

    def test_missing_status(self, success_result):
        """Test validation fails when status is missing."""
        del success_result["status"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("status" in error for error in errors)

    def test_missing_author_candidates(self, success_result):
        """Test validation fails when author_candidates is missing."""
        del success_result["author_candidates"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("author_candidates" in error for error in errors)

    def test_missing_search_summary(self, success_result):
        """Test validation fails when search_summary is missing."""
        del success_result["search_summary"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("search_summary" in error for error in errors)

    def test_missing_comments(self, success_result):
        """Test validation fails when comments is missing."""
        del success_result["comments"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("comments" in error for error in errors)


# Tests for invalid field values

class TestInvalidFieldValues:
    """Tests for invalid field values."""

    def test_invalid_status(self, success_result):
        """Test validation fails with invalid status value."""
        success_result["status"] = "invalid_status"
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("Invalid status" in error for error in errors)

    def test_author_candidates_not_array(self, success_result):
        """Test validation fails when author_candidates is not an array."""
        success_result["author_candidates"] = "not an array"
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("must be an array" in error for error in errors)

    def test_search_summary_not_object(self, success_result):
        """Test validation fails when search_summary is not an object."""
        success_result["search_summary"] = "not an object"
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("must be an object" in error for error in errors)


# Tests for candidate structure

class TestCandidateStructure:
    """Tests for author_candidates structure."""

    def test_missing_candidate_rank(self, success_result):
        """Test validation fails when candidate is missing rank."""
        del success_result["author_candidates"][0]["rank"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("rank" in error for error in errors)

    def test_missing_candidate_author(self, success_result):
        """Test validation fails when candidate is missing author."""
        del success_result["author_candidates"][0]["author"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("author" in error for error in errors)

    def test_missing_candidate_evidence(self, success_result):
        """Test validation fails when candidate is missing evidence."""
        del success_result["author_candidates"][0]["evidence"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("evidence" in error for error in errors)

    def test_missing_author_required_fields(self, success_result):
        """Test validation fails when author is missing required fields."""
        del success_result["author_candidates"][0]["author"]["openalex_id"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("openalex_id" in error for error in errors)


# Tests for search_summary structure

class TestSearchSummaryStructure:
    """Tests for search_summary structure."""

    def test_missing_embo_found(self, success_result):
        """Test validation fails when search_summary missing embo_found."""
        del success_result["search_summary"]["embo_found"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("embo_found" in error for error in errors)

    def test_missing_orcid_source(self, success_result):
        """Test validation fails when search_summary missing orcid_source."""
        del success_result["search_summary"]["orcid_source"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("orcid_source" in error for error in errors)

    def test_missing_candidates_evaluated(self, success_result):
        """Test validation fails when search_summary missing candidates_evaluated."""
        del success_result["search_summary"]["candidates_evaluated"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("candidates_evaluated" in error for error in errors)

    def test_missing_disambiguation_needed(self, success_result):
        """Test validation fails when search_summary missing disambiguation_needed."""
        del success_result["search_summary"]["disambiguation_needed"]
        is_valid, errors = validate_disambiguation_result(success_result)
        assert not is_valid
        assert any("disambiguation_needed" in error for error in errors)


# Tests for edge cases

class TestEdgeCases:
    """Tests for edge cases."""

    def test_empty_author_candidates_is_valid(self, not_found_result):
        """Test that empty author_candidates array is valid (for not_found)."""
        assert not_found_result["author_candidates"] == []
        is_valid, errors = validate_disambiguation_result(not_found_result)
        assert is_valid, f"Validation errors: {errors}"

    def test_null_orcid_is_valid(self, ambiguous_result):
        """Test that null orcid value is valid."""
        assert ambiguous_result["author_candidates"][0]["author"]["orcid"] is None
        is_valid, errors = validate_disambiguation_result(ambiguous_result)
        assert is_valid, f"Validation errors: {errors}"

    def test_null_institution_is_valid(self, success_result):
        """Test that null institution value is valid."""
        success_result["author_candidates"][0]["author"]["institution"] = None
        is_valid, errors = validate_disambiguation_result(success_result)
        assert is_valid, f"Validation errors: {errors}"

    def test_empty_concerns_is_valid(self, success_result):
        """Test that empty concerns array is valid."""
        assert success_result["author_candidates"][0]["concerns"] == []
        is_valid, errors = validate_disambiguation_result(success_result)
        assert is_valid, f"Validation errors: {errors}"
