"""Orquestación del matcher canónico y su salida auditable."""

from __future__ import annotations

from collections.abc import Sequence
from typing import Any

from app.clinical_pipeline.application.canonical_resolver import (
    DeterministicCanonicalAssociationResolver,
)
from app.clinical_pipeline.domain.models import (
    CanonicalCatalogEntry,
    CanonicalProcessingResult,
)
from app.clinical_pipeline.domain.ports import CanonicalAssociationResolver, CanonicalTextMatcher


class CanonicalDocumentProcessor:
    def __init__(
        self,
        matcher: CanonicalTextMatcher,
        *,
        strategy: str,
        version: str = "v1",
        resolver: CanonicalAssociationResolver | None = None,
    ) -> None:
        self.matcher = matcher
        self.strategy = strategy
        self.version = version
        self.resolver = resolver or DeterministicCanonicalAssociationResolver()

    def process(
        self,
        document_pages: Sequence[Any],
        candidates: Sequence[CanonicalCatalogEntry | dict[str, Any] | str],
        *,
        context: dict[str, Any] | None = None,
    ) -> CanonicalProcessingResult:
        context = dict(context or {})
        occurrences = self.matcher.find_occurrences(document_pages, candidates, context)
        catalog_entries: dict[str, CanonicalCatalogEntry] = {}
        for raw_item in candidates:
            try:
                item = (
                    raw_item
                    if isinstance(raw_item, CanonicalCatalogEntry)
                    else CanonicalCatalogEntry.model_validate(raw_item)
                )
            except (TypeError, ValueError):
                continue
            catalog_entries[str(item.canonical_id)] = item

        if self.strategy == "canonical":
            catalog_values = list(catalog_entries.values())
            for occurrence in occurrences:
                if occurrence.match_method == "not_found":
                    continue
                decision = self.resolver.resolve(occurrence, catalog_values, context)
                entry = catalog_entries.get(occurrence.canonical_id)
                occurrence.status = decision.status
                occurrence.automatic = {
                    **occurrence.automatic,
                    "applied": decision.status == "validado_automaticamente",
                    "strategy": self.strategy,
                    "strategy_version": self.version,
                    "suggested_value": entry.canonical_term if entry else occurrence.canonical_term,
                    "related_occurrences": [occurrence.occurrence_id],
                    "decision_status": decision.status,
                    "decision_reason": decision.reason,
                    "reasons": list(decision.reasons),
                    "score": decision.score,
                    "margin": decision.margin,
                    "candidates": [item.model_dump(mode="python") for item in decision.candidates],
                    "evidences": list(decision.evidences),
                    "resolver_version": decision.resolver_version,
                    "catalog": decision.catalog,
                    "catalog_version": decision.catalog_version,
                    "normalization_version": decision.normalization_version,
                    "model": decision.model,
                    "score_semantics": decision.score_semantics,
                }
                # Legacy consumers use these names and, for exact matches, the
                # historical method as ``reason``.
                occurrence.automatic.setdefault("reason", occurrence.match_method)
                if decision.status == "validado_automaticamente":
                    occurrence.automatic["aprobado_automaticamente"] = True
                    occurrence.automatic["origin"] = "canonical_resolver"
            for occurrence in occurrences:
                entry = catalog_entries.get(occurrence.canonical_id)
                if entry is not None:
                    occurrence.automatic.setdefault("catalog", str(context.get("catalog") or "canonical"))
                    occurrence.automatic.setdefault("catalog_version", entry.version)
        incidents = [
            item
            for item in occurrences
            if item.match_method != "not_found"
            and item.status in {"pending_canonical_review", "pendiente_revision", "conflicto"}
        ]
        blocked = sorted({item.artifact_type for item in incidents if item.artifact_type})
        return CanonicalProcessingResult(
            strategy=self.strategy,
            strategy_version=self.version,
            normalization_version="v2",
            occurrences=occurrences,
            incidents=incidents,
            blocked_artifact_types=blocked,
        )
