"""Unit tests for BibResolveBatchTool."""

from __future__ import annotations

from unittest.mock import MagicMock, patch

from axm_bib.models import BibEntry
from axm_bib.resolver import IdentifierType
from axm_bib.tools.bib_resolve_batch import BibResolveBatchTool


def _entry(key: str = "test2024", doi: str = "10.1234/test") -> BibEntry:
    return BibEntry(
        key=key,
        entry_type="article",
        raw=f"@article{{{key}, title={{Test}}}}",
        doi=doi,
        title="Test Paper",
        authors=["Author"],
    )


class TestBibResolveBatchTool:
    """BibResolveBatchTool tests."""

    def test_name(self) -> None:
        """Tool name is 'bib_resolve_batch'."""
        assert BibResolveBatchTool().name == "bib_resolve_batch"

    def test_empty_papers_fails(self) -> None:
        """Empty papers list returns error."""
        result = BibResolveBatchTool().execute(papers=[])
        assert result.success is False
        assert "required" in (result.error or "")

    def test_none_papers_fails(self) -> None:
        """None papers returns error."""
        result = BibResolveBatchTool().execute()
        assert result.success is False

    # ── Single paper, single ref ──────────────────────────────────────

    @patch("axm_bib.tools.bib_resolve_batch.BibPipeline")
    @patch("axm_bib.tools.bib_resolve_batch.detect_identifier")
    def test_batch_single_paper_single_ref(
        self,
        mock_detect: MagicMock,
        mock_cls: MagicMock,
    ) -> None:
        """Single paper with single ref → 1 result."""
        mock_detect.return_value = (IdentifierType.DOI, "10.1/a")
        mock_cls.return_value.resolve.return_value = _entry(key="a")

        result = BibResolveBatchTool().execute(papers=[["10.1/a"]])
        assert result.success is True
        assert result.data is not None
        assert result.data["total"] == 1
        assert result.data["succeeded"] == 1
        assert result.data["results"][0]["key"] == "a"

    # ── N papers (AC4) ────────────────────────────────────────────────

    @patch("axm_bib.tools.bib_resolve_batch.BibPipeline")
    @patch("axm_bib.tools.bib_resolve_batch.check_coherence", return_value=True)
    @patch("axm_bib.tools.bib_resolve_batch.merge_entries")
    @patch("axm_bib.tools.bib_resolve_batch.detect_identifier")
    def test_batch_n_papers(
        self,
        mock_detect: MagicMock,
        mock_merge: MagicMock,
        _mock_coherence: MagicMock,
        mock_cls: MagicMock,
    ) -> None:
        """Two papers: one with merge, one single → 2 results."""
        # Paper 1: 2 refs → merge
        # Paper 2: 1 ref → direct
        mock_detect.side_effect = [
            (IdentifierType.DOI, "10.1/a"),
            (IdentifierType.ARXIV, "2503.1"),
            (IdentifierType.DOI, "10.1/b"),
        ]
        e1, e2, e3 = _entry(key="a"), _entry(key="a2"), _entry(key="b")
        mock_cls.return_value.resolve.side_effect = [e1, e2, e3]
        mock_merge.return_value = _entry(key="merged_a")

        result = BibResolveBatchTool().execute(
            papers=[["10.1/a", "arXiv:2503.1"], ["10.1/b"]],
        )
        assert result.success is True
        data = result.data
        assert data is not None
        assert data["total"] == 2
        assert data["succeeded"] == 2
        assert data["results"][0]["key"] == "merged_a"
        assert data["results"][1]["key"] == "b"

    # ── Partial failure ───────────────────────────────────────────────

    @patch("axm_bib.tools.bib_resolve_batch.BibPipeline")
    @patch("axm_bib.tools.bib_resolve_batch.detect_identifier")
    def test_batch_partial_failure(
        self,
        mock_detect: MagicMock,
        mock_cls: MagicMock,
    ) -> None:
        """One paper succeeds, one fails → partial success."""
        mock_detect.side_effect = [
            (IdentifierType.DOI, "10.1/a"),
            (IdentifierType.DOI, "10.1/bad"),
        ]
        mock_cls.return_value.resolve.side_effect = [
            _entry(key="a"),
            ValueError("not found"),
        ]

        result = BibResolveBatchTool().execute(
            papers=[["10.1/a"], ["10.1/bad"]],
        )
        assert result.success is True
        assert result.data is not None
        assert result.data["succeeded"] == 1
        assert result.data["failed"] == 1

    # ── Title rejection in batch ──────────────────────────────────────

    @patch("axm_bib.tools.bib_resolve_batch.BibPipeline")
    @patch("axm_bib.tools.bib_resolve_batch.detect_identifier")
    def test_batch_rejects_title(
        self,
        mock_detect: MagicMock,
        mock_cls: MagicMock,
    ) -> None:
        """Title in ref group → failure for that paper."""
        mock_detect.return_value = (IdentifierType.TITLE, "Some Title")

        result = BibResolveBatchTool().execute(papers=[["Some Title"]])
        assert result.success is True  # Overall batch succeeds
        assert result.data is not None
        assert result.data["failed"] == 1
        assert "title" in result.data["results"][0]["error"].lower()
