"""Provenance metadata for UKO triples produced by analyzers.

When the :class:`UKOIndexer` indexes a resource, it attaches provenance
metadata to every triple so that downstream consumers can trace each
fact back to its source.

+-------------------------+--------------------------------------------+
| Model                   | Purpose                                    |
+=========================+============================================+
| ``ProvenanceMetadata``  | Source-tracking metadata for a triple      |
| ``ProvenancedTriple``   | UKOTriple + ProvenanceMetadata composite   |
| ``IndexResult``         | Summary of an indexing operation           |
+-------------------------+--------------------------------------------+

Based on ``docs/specification.md`` > ACMS > Real-time Index
Synchronization: provenance includes ``sourceResource``,
``sourcePath``, ``sourceRange``, ``validFrom``, ``isCurrent``.
"""

from __future__ import annotations

import re
from datetime import UTC, datetime

from pydantic import BaseModel, ConfigDict, Field, field_validator

from cleveragents.domain.models.acms.analyzers import UKOTriple

# ---------------------------------------------------------------------------
# ProvenanceMetadata
# ---------------------------------------------------------------------------


class ProvenanceMetadata(BaseModel):
    """Source-tracking metadata attached to indexed UKO triples.

    Attributes:
        source_resource: ULID of the resource that produced the triple.
        source_path: File path within the resource (e.g.
            ``"src/main.py"``).  Empty string if not applicable.
        source_range: Line range within the file (e.g. ``"10-25"``).
            Empty string if not applicable (whole-file scope).
        valid_from: UTC timestamp when this triple was produced.
        is_current: Whether this triple reflects the latest state
            of the source.  Set to ``False`` when a resource is
            removed or superseded.
    """

    source_resource: str = Field(
        ...,
        min_length=1,
        description="ULID of the source resource.",
    )
    source_path: str = Field(
        default="",
        description="File path within the resource.",
    )
    source_range: str = Field(
        default="",
        description="Line range (e.g. '10-25').",
    )

    @field_validator("source_range")
    @classmethod
    def _validate_source_range(cls, value: str) -> str:
        """Validate source_range format: empty or ``'<int>-<int>'``."""
        if not value:
            return value
        if not re.fullmatch(r"\d+-\d+", value):
            raise ValueError(
                f"source_range must be empty or match '<start>-<end>' "
                f"(e.g. '10-25'), got '{value}'"
            )
        return value

    valid_from: datetime = Field(
        default_factory=lambda: datetime.now(UTC),
        description="UTC timestamp when this triple was produced.",
    )
    is_current: bool = Field(
        default=True,
        description="Whether this triple reflects the latest state.",
    )

    model_config = ConfigDict(frozen=True)


# ---------------------------------------------------------------------------
# ProvenancedTriple
# ---------------------------------------------------------------------------


class ProvenancedTriple(BaseModel):
    """A UKO triple paired with its provenance metadata.

    This composite is the primary output of the :class:`UKOIndexer`
    pipeline: every triple produced by an analyzer is wrapped with
    provenance before being stored in the graph backend.

    Attributes:
        triple: The UKO triple (subject, predicate, object).
        provenance: Source-tracking metadata.
    """

    triple: UKOTriple
    provenance: ProvenanceMetadata

    model_config = ConfigDict(frozen=True)


# ---------------------------------------------------------------------------
# IndexResult
# ---------------------------------------------------------------------------


class IndexResult(BaseModel):
    """Summary of an indexing operation on a single resource.

    Returned by :meth:`UKOIndexer.index_resource` and
    :meth:`UKOIndexer.reindex_resource` for observability.

    Attributes:
        resource_id: ULID of the indexed resource.
        triple_count: Number of UKO triples produced.
        text_docs_indexed: Number of text documents indexed.
        embeddings_indexed: Number of vector embeddings indexed.
        analyzer_domain: Domain of the analyzer used (e.g.
            ``"python"``), or ``"none"`` if no analyzer matched.
        errors: Error messages for any backends that failed.
    """

    resource_id: str = Field(
        ...,
        min_length=1,
        description="ULID of the indexed resource.",
    )
    triple_count: int = Field(
        default=0,
        ge=0,
        description="Number of UKO triples produced.",
    )
    text_docs_indexed: int = Field(
        default=0,
        ge=0,
        description="Number of text documents indexed.",
    )
    embeddings_indexed: int = Field(
        default=0,
        ge=0,
        description="Number of vector embeddings indexed.",
    )
    analyzer_domain: str = Field(
        default="none",
        description="Domain of the analyzer used.",
    )
    errors: tuple[str, ...] = Field(
        default=(),
        description="Error messages from failed backends.",
    )

    model_config = ConfigDict(frozen=True)


# ---------------------------------------------------------------------------
# Module exports
# ---------------------------------------------------------------------------

__all__: list[str] = [
    "IndexResult",
    "ProvenanceMetadata",
    "ProvenancedTriple",
]
