"""Helpers for deriving pipeline identifiers from RDF documents."""

from __future__ import annotations

from typing import Any, Dict, Iterable

from .namespaces import cf_type_candidates
from .rdf_document import DocumentInput, load_rdf_document


_PIPELINE_TYPES = cf_type_candidates("ProcessingPipeline")


def _iter_nodes(doc: Dict[str, Any]) -> Iterable[Dict[str, Any]]:
    graph = doc.get("@graph")
    if isinstance(graph, list):
        for node in graph:
            if isinstance(node, dict):
                yield node
    elif isinstance(doc, dict):
        yield doc


def _has_type(node: Dict[str, Any], candidates: set[str]) -> bool:
    raw = node.get("@type")
    types: list[str] = []
    if isinstance(raw, str):
        types.append(raw)
    elif isinstance(raw, list):
        for entry in raw:
            if isinstance(entry, str):
                types.append(entry)
            elif isinstance(entry, dict):
                entry_id = entry.get("@id")
                if isinstance(entry_id, str):
                    types.append(entry_id)
    elif isinstance(raw, dict):
        entry_id = raw.get("@id")
        if isinstance(entry_id, str):
            types.append(entry_id)
    return any(t in candidates for t in types)


def _local_id(value: str) -> str:
    if "#" in value:
        return value.split("#", 1)[1]
    if "/" in value:
        return value.rsplit("/", 1)[-1]
    if ":" in value:
        return value.split(":", 1)[1]
    return value


def derive_pipeline_id_from_document(value: DocumentInput) -> str:
    """Derive a stable pipeline id from an RDF document (ProcessingPipeline @id)."""
    doc = load_rdf_document(value, label="pipeline document")
    pipeline_nodes = [
        node for node in _iter_nodes(doc) if _has_type(node, _PIPELINE_TYPES)
    ]
    if not pipeline_nodes:
        raise ValueError("No cf:ProcessingPipeline node found in RDF document")
    if len(pipeline_nodes) > 1:
        raise ValueError("Multiple cf:ProcessingPipeline nodes found; pipeline id is ambiguous")
    node_id = pipeline_nodes[0].get("@id")
    if not isinstance(node_id, str) or not node_id.strip():
        raise ValueError("ProcessingPipeline node is missing a string @id")
    return _local_id(node_id)
