"""This module implements `DotOutput`, a class that outputs a Graphviz
DOT file, given a `Geneagraph` object.

The DOT file syntax is generated by code in the module. A dependency,
such as [graphviz](https://pypi.org/project/graphviz/) could have been
used. It was decided to not take on a dependency since the DOT files
being generated in this project are very simple.
"""

from ..types import Geneagraph, Record

from typing import Generator


def make_node_str(record: Record) -> str:
    label = record["name"]
    institution = record["institution"]
    year = record["year"]
    if institution is not None or year is not None:
        inst_comp = [institution] if institution is not None else []
        year_comp = [f"({year})"] if year is not None else []
        label += "\\n" + " ".join(inst_comp + year_comp)

    return f'{record["id"]} [label="{label}"];'


def make_edge_str(record: Record, graph: Geneagraph) -> Generator[str, None, None]:
    for advisor_id in filter(
        lambda aid: aid in graph["nodes"],
        set(
            record["advisors"]
        )  # make `set` to eliminate the occasional duplicate advisor (e.g., at this
        # time, 125886)
    ):  # filter out advisors that are not part of the graph
        yield f'{advisor_id} -> {record["id"]};'


class DotOutput:
    def __init__(self, graph: Geneagraph) -> None:
        self.graph = graph

    @property
    def output(self) -> str:
        template = """digraph {{
    graph [ordering="out"];
    node [shape=plaintext];
    edge [style=bold];

    {nodes}

    {edges}
}}"""
        nodes = [
            make_node_str(record)
            for record in sorted(self.graph["nodes"].values(), key=lambda r: r["id"])
        ]
        edges = [
            edge_str
            for record in sorted(
                self.graph["nodes"].values(),
                key=lambda r: (r["year"] or -10000, r["name"]),
            )
            for edge_str in make_edge_str(record, self.graph)
        ]
        prefix = "\n    "
        return template.format(nodes=prefix.join(nodes), edges=prefix.join(edges))
