"""Community detection over AGE graphs via NetworkX or igraph."""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from piggie._integrations.networkx import to_networkx
from piggie.exceptions import AlgorithmError

if TYPE_CHECKING:
    import pandas as pd

    from piggie.driver import PiggieDriver


def _resolve_backend(backend: str) -> str:
    if backend != "auto":
        return backend
    try:
        import igraph  # noqa: F401
        return "igraph"
    except ImportError:
        return "networkx"


def _community_igraph(
    driver: PiggieDriver,
    graph: str,
    *,
    method: str,
    label: str | None,
    rel_type: str | None,
) -> pd.DataFrame:
    import pandas as pd

    from piggie._integrations.igraph_integration import to_igraph_sql

    G_ig, id_map = to_igraph_sql(driver, graph, directed=True)
    G_ig_u = G_ig.as_undirected()

    try:
        if method == "louvain":
            partition = G_ig_u.community_multilevel()
        elif method == "label_propagation":
            partition = G_ig_u.community_label_propagation()
        elif method == "greedy_modularity":
            dendrogram = G_ig_u.community_fastgreedy()
            partition = dendrogram.as_clustering()
        elif method == "leiden":
            try:
                import leidenalg
            except ImportError:
                raise AlgorithmError(
                    "Leiden community detection requires leidenalg. "
                    "Install with: pip install leidenalg"
                ) from None
            partition = leidenalg.find_partition(
                G_ig_u, leidenalg.ModularityVertexPartition,
            )
        else:
            raise AlgorithmError(
                f"Unknown community method: {method!r}. "
                "Valid methods: louvain, label_propagation, greedy_modularity, leiden"
            )
    except AlgorithmError:
        raise
    except Exception as exc:
        raise AlgorithmError(f"Community detection failed: {exc}") from exc

    rows = []
    for cid, members in enumerate(partition):
        for idx in members:
            rows.append({
                "node_id": id_map[idx],
                "label": G_ig.vs[idx]["label"],
                "community_id": cid,
            })
    return pd.DataFrame(rows)


def community_detection(
    driver: PiggieDriver,
    graph: str,
    *,
    method: str = "louvain",
    backend: str = "auto",
    label: str | None = None,
    rel_type: str | None = None,
) -> pd.DataFrame:
    """Detect communities in an AGE graph.

    Args:
        driver: Connected PiggieDriver.
        graph: Graph name.
        method: Algorithm — "louvain" (default), "label_propagation",
            "greedy_modularity", or "leiden".
        backend: Compute backend — "auto" (prefer igraph), "igraph", or
            "networkx".
        label: Filter to a single vertex label.
        rel_type: Filter to a single edge type.

    Returns:
        DataFrame with columns [node_id, label, community_id].
    """
    resolved = _resolve_backend(backend)

    if resolved == "igraph":
        return _community_igraph(
            driver, graph, method=method, label=label, rel_type=rel_type,
        )

    # NetworkX path
    try:
        import networkx as nx
        import pandas as pd
    except ImportError as exc:
        raise ImportError(
            "networkx and pandas are required. "
            "Install with: pip install piggie[networkx,pandas]"
        ) from exc

    labels = [label] if label else None
    rel_types = [rel_type] if rel_type else None
    G = to_networkx(driver, graph, labels=labels, rel_types=rel_types)

    G_undirected = G.to_undirected()

    try:
        if method == "louvain":
            communities: list[set[Any]] = nx.community.louvain_communities(G_undirected)
        elif method == "label_propagation":
            communities = list(nx.community.label_propagation_communities(G_undirected))
        elif method == "greedy_modularity":
            communities = [
                set(c) for c in nx.community.greedy_modularity_communities(G_undirected)
            ]
        elif method == "leiden":
            try:
                import igraph as ig
                import leidenalg
            except ImportError:
                raise AlgorithmError(
                    "Leiden community detection requires leidenalg and igraph. "
                    "Install with: pip install piggie[igraph] leidenalg"
                ) from None
            ig_graph = ig.Graph.from_networkx(G_undirected)
            partition = leidenalg.find_partition(ig_graph, leidenalg.ModularityVertexPartition)
            nx_nodes = list(G_undirected.nodes())
            communities = [
                {nx_nodes[idx] for idx in community}
                for community in partition
            ]
        else:
            raise AlgorithmError(
                f"Unknown community method: {method!r}. "
                "Valid methods: louvain, label_propagation, greedy_modularity, leiden"
            )
    except AlgorithmError:
        raise
    except Exception as exc:
        raise AlgorithmError(f"Community detection failed: {exc}") from exc

    rows = []
    for cid, members in enumerate(communities):
        for node_id in members:
            node_label = G.nodes[node_id].get("label", "")
            rows.append({
                "node_id": node_id,
                "label": node_label,
                "community_id": cid,
            })

    return pd.DataFrame(rows)
