"""BibResolveBatchTool — batch-resolve N papers, each with M refs.

Registered as 'bib_resolve_batch' via axm.tools entry point.
"""

from __future__ import annotations

from typing import Any

from axm.tools.base import AXMTool, ToolResult

from axm_bib.exceptions import AmbiguousIdentifier, ResolutionError
from axm_bib.merge import check_coherence, merge_entries
from axm_bib.pipeline import BibPipeline
from axm_bib.resolver import IdentifierType, detect_identifier

__all__ = ["BibResolveBatchTool"]


class BibResolveBatchTool(AXMTool):
    """Batch-resolve N papers, each identified by M refs.

    Each paper is a list of identifiers (DOI, arXiv ID, etc.) for the
    *same* paper. For each paper, all refs are resolved and merged.

    Registered as 'bib_resolve_batch' via axm.tools entry point.
    """

    @property
    def name(self) -> str:
        """Return the MCP tool name."""
        return "bib_resolve_batch"

    def execute(
        self,
        *,
        papers: list[list[str]] | None = None,
        **kwargs: Any,
    ) -> ToolResult:
        """Resolve N papers, each identified by a list of refs.

        Args:
            papers: List of paper ref groups. Each group is a list of
                identifiers for the same paper to resolve and merge.

        Returns:
            ToolResult with data={"results": [...], "total": N,
            "succeeded": N, "failed": N}.
        """
        if not papers:
            return ToolResult(
                success=False,
                error="papers list is required",
            )

        pipeline = BibPipeline()
        results: list[dict[str, Any]] = []
        succeeded = 0
        failed = 0

        for ref_group in papers:
            try:
                result = self._resolve_paper(pipeline, ref_group)
                results.append(result)
                succeeded += 1
            except Exception as exc:
                results.append(
                    {
                        "success": False,
                        "refs": ref_group,
                        "error": str(exc),
                    }
                )
                failed += 1

        return ToolResult(
            success=True,
            data={
                "results": results,
                "total": len(papers),
                "succeeded": succeeded,
                "failed": failed,
            },
        )

    @staticmethod
    def _resolve_paper(
        pipeline: BibPipeline,
        ref_group: list[str],
    ) -> dict[str, Any]:
        """Resolve and merge refs for a single paper.

        Args:
            pipeline: Active BibPipeline instance.
            ref_group: List of identifiers for the same paper.

        Returns:
            Result dict with bibtex, key, doi, entry_type.

        Raises:
            ValueError: If ref_group is empty, contains titles,
                or refs are incoherent.
        """
        if not ref_group:
            msg = "empty ref group"
            raise ResolutionError(msg)

        # Dedup preserving order
        ref_group = list(dict.fromkeys(ref_group))

        # Reject titles
        for r in ref_group:
            id_type, _ = detect_identifier(r)
            if id_type == IdentifierType.TITLE:
                msg = f"'{r}' looks like a title. Use bib_search to discover papers."
                raise ResolutionError(msg)

        entries = [pipeline.resolve(r) for r in ref_group]

        if len(entries) == 1:
            entry = entries[0]
        else:
            if not check_coherence(entries):
                msg = "Identifiers refer to different papers"
                raise AmbiguousIdentifier(msg)
            entry = merge_entries(entries)

        return {
            "success": True,
            "refs": ref_group,
            "bibtex": entry.raw,
            "key": entry.key,
            "doi": entry.doi,
            "entry_type": entry.entry_type,
        }
