from collections.abc import Callable, Hashable
from typing import TypeVar, overload

from networkx.classes.digraph import DiGraph
from networkx.classes.graph import Graph, _DataBound, _EdgeData, _Node, _NodeData
from networkx.classes.multidigraph import MultiDiGraph
from networkx.classes.multigraph import MultiGraph

_G = TypeVar("_G", bound=Graph[Hashable, _DataBound, _DataBound])
_D = TypeVar("_D", bound=DiGraph[Hashable, _DataBound, _DataBound])

__all__ = ["generic_graph_view", "subgraph_view", "reverse_view"]

@overload
def generic_graph_view(G: _G, create_using: None = None) -> _G: ...
@overload
def generic_graph_view(
    G: Graph[_Node, _NodeData, _EdgeData], create_using: type[MultiDiGraph[_Node, _NodeData, _EdgeData]]
) -> MultiDiGraph[_Node, _NodeData, _EdgeData]: ...
@overload
def generic_graph_view(
    G: Graph[_Node, _NodeData, _EdgeData], create_using: type[DiGraph[_Node, _NodeData, _EdgeData]]
) -> DiGraph[_Node, _NodeData, _EdgeData]: ...
@overload
def generic_graph_view(
    G: Graph[_Node, _NodeData, _EdgeData], create_using: type[MultiGraph[_Node, _NodeData, _EdgeData]]
) -> MultiGraph[_Node, _NodeData, _EdgeData]: ...
@overload
def generic_graph_view(
    G: Graph[_Node, _NodeData, _EdgeData], create_using: type[Graph[_Node, _NodeData, _EdgeData]]
) -> Graph[_Node, _NodeData, _EdgeData]: ...

@overload
def subgraph_view(
    G: MultiDiGraph[_Node, _NodeData, _EdgeData],
    *,
    filter_node: Callable[[_Node], bool] = ...,
    filter_edge: Callable[[_Node, _Node, int], bool] = ...,
) -> MultiDiGraph[_Node, _NodeData, _EdgeData]: ...
@overload
def subgraph_view(
    G: MultiGraph[_Node, _NodeData, _EdgeData],
    *,
    filter_node: Callable[[_Node], bool] = ...,
    filter_edge: Callable[[_Node, _Node, int], bool] = ...,
) -> MultiGraph[_Node, _NodeData, _EdgeData]: ...
@overload
def subgraph_view(
    G: DiGraph[_Node, _NodeData, _EdgeData],
    *,
    filter_node: Callable[[_Node], bool] = ...,
    filter_edge: Callable[[_Node, _Node], bool] = ...,
) -> DiGraph[_Node, _NodeData, _EdgeData]: ...
@overload
def subgraph_view(
    G: Graph[_Node, _NodeData, _EdgeData],
    *,
    filter_node: Callable[[_Node], bool] = ...,
    filter_edge: Callable[[_Node, _Node], bool] = ...,
) -> Graph[_Node, _NodeData, _EdgeData]: ...

def reverse_view(G: _D) -> _D: ...
