from __future__ import annotations

from dataclasses import dataclass

PandasDTypeName = str


@dataclass(frozen=True)
class GraphAlgorithmDefinition:
    name: str
    label: str
    supports_directed: bool = True
    default_selected: bool = True
    # Dtype strings are passed to pandas.Series(dtype=...) when building empty outputs.
    output_columns: tuple[tuple[str, PandasDTypeName], ...] = ()


GRAPH_FEATURE_ALGORITHM_DEFINITIONS: tuple[GraphAlgorithmDefinition, ...] = (
    GraphAlgorithmDefinition(
        name="degree",
        label="Degree",
        output_columns=(("degree", "Int64"),),
    ),
    GraphAlgorithmDefinition(
        name="eigenvector_centrality",
        label="Eigenvector centrality",
        output_columns=(("eigenvector_centrality", "float64"),),
    ),
    GraphAlgorithmDefinition(
        name="clustering",
        label="Clustering coefficient",
        output_columns=(("clustering_coefficient", "float64"),),
    ),
    GraphAlgorithmDefinition(
        name="triangles",
        label="Count of triangles",
        supports_directed=False,
        output_columns=(("triangles", "Int64"),),
    ),
    GraphAlgorithmDefinition(
        name="closeness",
        label="Closeness centrality",
        output_columns=(("closeness_centrality", "float64"),),
    ),
    GraphAlgorithmDefinition(
        name="pagerank",
        label="PageRank",
        output_columns=(("pagerank", "float64"),),
    ),
    GraphAlgorithmDefinition(
        name="sq_clustering",
        label="Square clustering",
        output_columns=(("square_clustering_coefficient", "float64"),),
    ),
    GraphAlgorithmDefinition(
        name="connected_components",
        label="Connected components",
        supports_directed=False,
        output_columns=(
            ("connected_component_id", "Int64"),
            ("connected_component_size", "Int64"),
        ),
    ),
)

GRAPH_CLUSTERING_ALGORITHM_DEFINITIONS: tuple[GraphAlgorithmDefinition, ...] = (
    GraphAlgorithmDefinition(name="fastgreedy", label="Fastgreedy", supports_directed=False),
    GraphAlgorithmDefinition(name="multilevel", label="Multilevel", supports_directed=False),
    GraphAlgorithmDefinition(name="infomap", label="Infomap"),
    GraphAlgorithmDefinition(name="walktrap", label="Walktrap"),
)

GRAPH_FEATURE_ALGORITHM_NAMES = tuple(definition.name for definition in GRAPH_FEATURE_ALGORITHM_DEFINITIONS)
GRAPH_CLUSTERING_ALGORITHM_NAMES = tuple(definition.name for definition in GRAPH_CLUSTERING_ALGORITHM_DEFINITIONS)
GRAPH_FEATURE_ALGORITHMS_BY_NAME = {
    definition.name: definition for definition in GRAPH_FEATURE_ALGORITHM_DEFINITIONS
}


def default_algorithm_names(
    definitions: tuple[GraphAlgorithmDefinition, ...],
    *,
    directed_graph: bool = False,
) -> tuple[str, ...]:
    return tuple(
        definition.name
        for definition in definitions
        if definition.default_selected and (definition.supports_directed or not directed_graph)
    )


__all__ = [
    "GRAPH_CLUSTERING_ALGORITHM_DEFINITIONS",
    "GRAPH_CLUSTERING_ALGORITHM_NAMES",
    "GRAPH_FEATURE_ALGORITHM_DEFINITIONS",
    "GRAPH_FEATURE_ALGORITHM_NAMES",
    "GRAPH_FEATURE_ALGORITHMS_BY_NAME",
    "GraphAlgorithmDefinition",
    "default_algorithm_names",
]
