"""Convert PyOBO into OBO Graph."""

from typing import Iterable, List

import bioregistry
import curies
from bioontologies.obograph import (
    OBO_SYNONYM_TO_OIO,
    OIO_TO_REFERENCE,
    Definition,
    Edge,
    Graph,
    GraphDocument,
    Meta,
    Node,
    Synonym,
    Xref,
)
from bioontologies.robot import ParseResults

from pyobo.struct import Obo, Reference, Term
from pyobo.struct.typedef import is_a

__all__ = [
    "graph_from_obo",
    "parse_results_from_obo",
]


def parse_results_from_obo(obo: Obo) -> ParseResults:
    """Get parse results from an OBO graph."""
    graph = graph_from_obo(obo)
    return ParseResults(graph_document=GraphDocument(graphs=[graph]))


def graph_from_obo(obo: Obo) -> Graph:
    """Get an OBO Graph object from a PyOBO object."""
    nodes: List[Node] = []
    edges: List[Edge] = []
    for term in obo:
        nodes.append(_get_class_node(term))
        edges.extend(_iter_edges(term))
    return Graph(
        id=bioregistry.get_bioregistry_iri("bioregistry", obo.ontology),
        prefix=obo.ontology,
        meta=_get_meta(obo),
        nodes=nodes,
        edges=edges,
        standardized=True,  # from construction :)
    )


def _get_meta(obo: Obo) -> Meta:
    return Meta(
        version=obo.data_version,
    )


def _rewire(r: Reference) -> curies.Reference:
    return curies.Reference(prefix=r.prefix, identifier=r.identifier)


def _get_class_node(term: Term) -> Node:
    if not term.definition:
        definition = None
    else:
        definition = Definition.from_parsed(
            value=term.definition, references=[_rewire(p) for p in term.provenance]
        )

    if term.xrefs:
        if not term.xref_types:
            term.xref_types = [
                Reference(prefix="oboInOwl", identifier="hasDbXref") for _ in term.xrefs
            ]
        elif len(term.xrefs) != len(term.xref_types):
            raise ValueError

    xrefs = [
        Xref.from_parsed(
            predicate=_rewire(xref_type),
            value=_rewire(xref),
        )
        for xref, xref_type in zip(term.xrefs, term.xref_types)
    ]
    default_st = Reference(prefix="oboInOwl", identifier="SynonymType")
    synonyms = [
        Synonym.from_parsed(
            name=synonym.name,
            predicate=OIO_TO_REFERENCE[OBO_SYNONYM_TO_OIO[synonym.specificity]],
            synonym_type=_rewire(synonym.type.reference) if synonym.type else default_st,
            references=[_rewire(x) for x in synonym.provenance],
        )
        for synonym in term.synonyms
    ]

    meta = Meta(
        definition=definition,
        xrefs=xrefs,
        synonyms=synonyms,
        basicPropertyValues=None,  # TODO properties
        deprecated=term.is_obsolete or False,
    )
    return Node(
        id=term.bioregistry_link,
        lbl=term.name,
        meta=meta,
        type="CLASS",
        reference=_rewire(term.reference),
        standardized=True,
    )


def _iter_edges(term: Term) -> Iterable[Edge]:
    for parent in term.parents:
        yield Edge.from_parsed(
            _rewire(term.reference),
            _rewire(is_a.reference),
            _rewire(parent),
        )

    for typedef, targets in term.relationships.items():
        for target in targets:
            yield Edge.from_parsed(
                _rewire(term.reference),
                _rewire(typedef.reference),
                _rewire(target),
            )
