from __future__ import annotations

from typing import Literal, Optional, cast

from pydantic import BaseModel, model_validator

from graph.exploration.algorithms.graph_algorithm_registry import (
    GRAPH_CLUSTERING_ALGORITHM_DEFINITIONS,
    GRAPH_CLUSTERING_ALGORITHM_NAMES,
)

ClusterAlgorithm = Literal["fastgreedy", "multilevel", "infomap", "walktrap"]


class GraphClusteringParams(BaseModel):
    directed_graph: bool
    output_type: Literal["output_nodes", "output_edges"]
    weight_property: Optional[str] = None
    fastgreedy: bool = False
    multilevel: bool = False
    infomap: bool = False
    walktrap: bool = False

    @model_validator(mode="after")
    def validate_graph_clustering_support(self) -> "GraphClusteringParams":
        if not selected_graph_clustering_names(self):
            raise ValueError("At least one graph clustering algorithm must be selected.")

        unsupported_algorithms = (
            tuple(
                definition.name
                for definition in GRAPH_CLUSTERING_ALGORITHM_DEFINITIONS
                if not definition.supports_directed and getattr(self, definition.name)
            )
            if self.directed_graph
            else ()
        )
        if unsupported_algorithms:
            algorithm_names = ", ".join(unsupported_algorithms)
            raise ValueError(f"Directed graph clustering requests do not support: {algorithm_names}.")
        return self


def selected_graph_clustering_names(params: GraphClusteringParams) -> tuple[ClusterAlgorithm, ...]:
    selected: list[ClusterAlgorithm] = []
    for algorithm_name in GRAPH_CLUSTERING_ALGORITHM_NAMES:
        if getattr(params, algorithm_name):
            selected.append(cast(ClusterAlgorithm, algorithm_name))
    return tuple(selected)


__all__ = ["ClusterAlgorithm", "GraphClusteringParams", "selected_graph_clustering_names"]
