"""
Relaxed Caveman (RC) benchmark graph generator for pySurprise.

Wraps :func:`networkx.generators.community.relaxed_caveman_graph` and converts
the output into pySurprise's native format.

The RC benchmark starts with *l* perfect cliques of size *k* and then
randomly rewires each edge with probability *p* (the **degradation**
parameter *D*).  When *p = 0* the communities are perfectly separated;
as *p* grows the community structure degrades.

Requirements
------------
``networkx >= 2.6``  (``pip install networkx``)

References
----------
    Fortunato S (2010)
    Community detection in graphs.
    Physics Reports 486(3-5): 75-174
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple

try:
    import networkx as nx
    from networkx.generators.community import relaxed_caveman_graph as _rc_graph
except ImportError:
    raise ImportError(
        "The 'networkx' package is required for RC benchmarks. "
        "Install it with:  pip install networkx"
    )

Edge = Tuple[str, str]
Partition = Dict[str, int]


# ---------------------------------------------------------------------------
# Result container
# ---------------------------------------------------------------------------

@dataclass
class RCResult:
    """Container returned by :func:`relaxed_caveman` and :func:`rc_graph`.

    Attributes
    ----------
    edges : list of (str, str)
        Edge list in pySurprise ``.pairs`` format.
    ground_truth : dict[str, int]
        Ground-truth partition ``{node_label: community_id}``.
    graph : networkx.Graph
        The full NetworkX graph.
    n_communities : int
        Number of ground-truth communities (*l*).
    l : int
        Number of cliques.
    k : int
        Clique size.
    p : float
        Rewiring / degradation probability used.
    """

    edges: List[Edge]
    ground_truth: Partition
    graph: "nx.Graph"
    n_communities: int
    l: int
    k: int
    p: float

    def __repr__(self) -> str:
        return (
            f"RCResult(nodes={self.graph.number_of_nodes()}, "
            f"edges={len(self.edges)}, "
            f"communities={self.n_communities}, "
            f"p={self.p})"
        )


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------

def _nx_to_pysurprise(
    G: nx.Graph, l: int, k: int
) -> Tuple[List[Edge], Partition]:
    """Convert a NetworkX RC graph to pySurprise edge list + partition.

    Ground truth: node *i* belongs to clique ``i // k``.
    """
    edges: List[Edge] = [(str(u), str(v)) for u, v in G.edges()]
    ground_truth: Partition = {str(node): node // k for node in G.nodes()}
    return edges, ground_truth


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------

def rc_graph(
    l: int,
    k: int,
    p: float,
    *,
    seed: Optional[int] = None,
) -> RCResult:
    """Generate a Relaxed Caveman graph and return an :class:`RCResult`.

    Parameters
    ----------
    l : int
        Number of cliques (communities).
    k : int
        Size of each clique.
    p : float
        Rewiring (degradation) probability.  ``0`` → perfect cliques,
        ``1`` → fully random rewiring.
    seed : int, optional
        Random seed for reproducibility.

    Returns
    -------
    RCResult
    """
    G = _rc_graph(l, k, p, seed=seed)
    edges, ground_truth = _nx_to_pysurprise(G, l, k)
    return RCResult(
        edges=edges,
        ground_truth=ground_truth,
        graph=G,
        n_communities=l,
        l=l,
        k=k,
        p=p,
    )


def relaxed_caveman(
    l: int = 10,
    k: int = 25,
    p: float = 0.0,
    *,
    seed: Optional[int] = None,
) -> Tuple[List[Edge], Partition]:
    """Generate a Relaxed Caveman benchmark and return ``(edges, ground_truth)``.

    Convenience wrapper with sensible defaults (10 cliques of 25 nodes →
    250-node graph).

    Parameters
    ----------
    l : int
        Number of cliques (default 10).
    k : int
        Clique size (default 25).
    p : float
        Degradation / rewiring probability in [0, 1] (default 0).
    seed : int, optional
        Random seed for reproducibility.

    Returns
    -------
    edges : list of (str, str)
        Edge list in pySurprise ``.pairs`` format.
    ground_truth : dict[str, int]
        ``{node_label: community_id}``

    Examples
    --------
    >>> from pysurprise.benchmarks import relaxed_caveman
    >>> edges, truth = relaxed_caveman(l=10, k=25, p=0.1, seed=42)
    >>> len(set(truth.values()))
    10
    """
    result = rc_graph(l, k, p, seed=seed)
    return result.edges, result.ground_truth
