"""
JSON-LD Emitter -- convert internal models to JSON-LD documents.

Produces compacted JSON-LD using the shared @context from context.py.
"""

from __future__ import annotations

from typing import Any

from pycharter.wiki.ontology.models import (
    ConceptDefinition,
    ConceptScheme,
    OntologyArtifact,
)
from pycharter.wiki.rdf.context import (
    JSONLD_CONTEXT,
    concept_uri,
    field_uri,
    scheme_uri,
)

_REL_TYPE_TO_PREDICATE = {
    # W3C standard predicates
    "is_a": "broader",  # skos:broader
    "derived_from": "wasDerivedFrom",  # prov:wasDerivedFrom
    "same_as": "exactMatch",  # skos:exactMatch
    "equivalent_to": "equivalentTo",  # statfyi:equivalentTo
    "related_to": "related",  # skos:related
    "has": "hasPart",  # dct:hasPart
    # Custom statfyi predicates
    "depends_on": "dependsOn",
    "precedes": "precedes",
    "causes": "causes",
    "implements": "implements",
    "governs": "governs",
    "describes": "describes",
    # Legacy
    "references": "references",
}


def emit_concepts_jsonld(
    scheme: ConceptScheme,
    *,
    context: dict | None = None,
) -> dict[str, Any]:
    """
    Convert a ConceptScheme to a SKOS JSON-LD document.

    Args:
        scheme: Parsed concept scheme.
        context: Override @context (default: shared JSONLD_CONTEXT).

    Returns:
        Compacted JSON-LD dict ready for serialization.
    """
    ctx = context or JSONLD_CONTEXT
    s_uri = scheme_uri(scheme.id)

    graph: list[dict[str, Any]] = []

    scheme_node: dict[str, Any] = {
        "@id": s_uri,
        "@type": "skos:ConceptScheme",
        "prefLabel": scheme.title,
        "versionInfo": scheme.version,
    }
    if scheme.description:
        scheme_node["definition"] = scheme.description
    graph.append(scheme_node)

    for c in scheme.concepts:
        node: dict[str, Any] = {
            "@id": concept_uri(c.id),
            "@type": "skos:Concept",
            "prefLabel": c.label,
            "inScheme": s_uri,
        }
        if c.definition:
            node["definition"] = c.definition
        if c.broader:
            node["broader"] = concept_uri(c.broader)
        if c.tags:
            node["tag"] = c.tags
        if c.alt_labels:
            node["altLabel"] = c.alt_labels
        if c.notation:
            node["notation"] = c.notation
        if c.examples:
            node["example"] = c.examples
        if c.exact_match:
            node["exactMatch"] = c.exact_match
        if c.deprecated:
            node["deprecated"] = True
        if c.replaced_by:
            node["isReplacedBy"] = concept_uri(c.replaced_by)
        if c.node_kind:
            node["nodeKind"] = c.node_kind
        graph.append(node)

    return {"@context": ctx, "@graph": graph}


def emit_ontology_jsonld(
    artifact: OntologyArtifact,
    contract_id: str,
    *,
    context: dict | None = None,
) -> dict[str, Any]:
    """
    Convert an OntologyArtifact (field mappings) to a JSON-LD document.

    Args:
        artifact: Parsed ontology artifact.
        contract_id: Owning contract identifier.
        context: Override @context (default: shared JSONLD_CONTEXT).

    Returns:
        Compacted JSON-LD dict ready for serialization.
    """
    ctx = context or JSONLD_CONTEXT
    graph: list[dict[str, Any]] = []

    for name, fo in artifact.fields.items():
        node: dict[str, Any] = {
            "@id": field_uri(contract_id, name),
            "@type": "statfyi:FieldMapping",
            "mapsTo": concept_uri(fo.concept),
            "versionInfo": artifact.version,
        }
        if fo.tags:
            node["tag"] = fo.tags
        if fo.owner:
            node["creator"] = fo.owner
        if fo.description:
            node["description"] = fo.description
        if fo.deprecated:
            node["deprecated"] = True
        if fo.lineage:
            if fo.lineage.derived_from:
                node["wasDerivedFrom"] = field_uri(contract_id, fo.lineage.derived_from)
            if fo.lineage.source_system:
                node["wasGeneratedBy"] = fo.lineage.source_system
        for rel in fo.relationships:
            predicate = _REL_TYPE_TO_PREDICATE.get(rel.type.value, "relatedTo")
            target = rel.target
            if "." in target:
                parts = target.split(".", 1)
                target_uri = field_uri(parts[0], parts[1])
            else:
                target_uri = field_uri(contract_id, target)
            node.setdefault(predicate, [])
            node[predicate].append(target_uri)

        graph.append(node)

    return {"@context": ctx, "@graph": graph}
