"""Route module comprehensive coverage steps (ported)."""

from __future__ import annotations

from pathlib import Path

from behave import given, then, when

from cleveragents.core.exceptions import ConfigurationError
from cleveragents.langgraph.graph import GraphConfig
from cleveragents.langgraph.nodes import Edge, NodeConfig, NodeType
from cleveragents.reactive.route import (
    BridgeConfig,
    RouteComplexityAnalyzer,
    RouteConfig,
    RouteType,
)
from cleveragents.reactive.stream_router import StreamConfig, StreamType


@given("the route system is available")
def step_route_system_available(context):
    context.route_configs = {}
    context.stream_configs = {}
    context.graph_configs = {}
    context.analysis_results = {}
    context.errors = {}


@given("I create a RouteConfig with graph type but no nodes")
def step_create_graph_config_no_nodes(context):
    context.test_config_params = {
        "name": "test_graph",
        "type": RouteType.GRAPH,
        "nodes": {},
    }


@when("I validate the configuration")
def step_validate_configuration(context):
    try:
        context.test_config = RouteConfig(**context.test_config_params)
        context.validation_error = None
    except Exception as exc:  # pylint: disable=broad-except
        context.validation_error = exc


@then("I should get a ConfigurationError about missing nodes")
def step_verify_missing_nodes_error(context):
    assert context.validation_error is not None
    assert isinstance(context.validation_error, ConfigurationError)
    assert "must have nodes defined" in str(context.validation_error)


@given("I create a RouteConfig with graph type")
def step_create_graph_route_config(context):
    context.test_config = RouteConfig(
        name="test_graph",
        type=RouteType.GRAPH,
        nodes={"test_node": {"type": "agent"}},
        edges=[{"source": "start", "target": "test_node"}],
    )


@when("I try to convert it to StreamConfig")
def step_convert_to_stream_config(context):
    try:
        context.conversion_result = context.test_config.to_stream_config()
        context.conversion_error = None
    except Exception as exc:  # pylint: disable=broad-except
        context.conversion_error = exc


@then("I should get a ValueError about invalid conversion")
def step_verify_invalid_conversion_error(context):
    assert context.conversion_error is not None
    assert isinstance(context.conversion_error, ValueError)
    assert "Cannot convert" in str(context.conversion_error)


@given("I create a RouteConfig with stream type")
def step_create_stream_route_config(context):
    context.test_config = RouteConfig(
        name="test_stream",
        type=RouteType.STREAM,
        stream_type=StreamType.COLD,
        operators=[{"type": "map"}],
    )


@when("I try to convert it to GraphConfig")
def step_convert_to_graph_config(context):
    try:
        context.conversion_result = context.test_config.to_graph_config()
        context.conversion_error = None
    except Exception as exc:  # pylint: disable=broad-except
        context.conversion_error = exc


@given("I have a valid StreamConfig")
def step_create_valid_stream_config(context):
    context.source_stream_config = StreamConfig(
        name="test_stream",
        type=StreamType.HOT,
        operators=[
            {"type": "map", "params": {"function": "transform"}},
            {"type": "filter", "params": {"predicate": "is_valid"}},
        ],
        subscriptions=["input_topic"],
        publications=["output_topic"],
        agents=["agent1", "agent2"],
        initial_value="initial",
        buffer_size=10,
        template_config={"template": "test"},
    )


@when("I create a RouteConfig from the StreamConfig")
def step_create_route_from_stream(context):
    context.result_route_config = RouteConfig.from_stream_config(
        context.source_stream_config
    )


@then("the RouteConfig should have stream type")
def step_verify_stream_type(context):
    assert context.result_route_config.type == RouteType.STREAM


@then("it should preserve all stream configuration details")
def step_verify_stream_details_preserved(context):
    route = context.result_route_config
    source = context.source_stream_config
    assert route.name == source.name
    assert route.stream_type == source.type
    assert route.operators == source.operators
    assert route.subscriptions == source.subscriptions
    assert route.publications == source.publications
    assert route.agents == source.agents
    assert route.initial_value == source.initial_value
    assert route.buffer_size == source.buffer_size
    assert route.template_config == source.template_config


@given("I have a valid GraphConfig with nodes and edges")
def step_create_valid_graph_config(context):
    node1 = NodeConfig(
        name="node1",
        type=NodeType.AGENT,
        agent="test_agent",
        tools=["tool1", "tool2"],
        retry_policy={"max_retries": 3},
        timeout=30,
        parallel=True,
        metadata={"key": "value"},
    )
    node2 = NodeConfig(
        name="node2",
        type=NodeType.FUNCTION,
        function="test_function",
        condition={"type": "test_condition"},
        subgraph="test_subgraph",
    )
    edge1 = Edge(
        source="start",
        target="node1",
        condition={"type": "edge_condition"},
        metadata={"edge_key": "edge_value"},
    )
    edge2 = Edge(source="node1", target="node2")
    context.source_graph_config = GraphConfig(
        name="test_graph",
        nodes={"node1": node1, "node2": node2},
        edges=[edge1, edge2],
        entry_point="start",
        state_class=None,
        checkpointing=True,
        checkpoint_dir=Path("/tmp/checkpoints"),
        enable_time_travel=True,
        parallel_execution=True,
        metadata={"graph_meta": "meta"},
    )


@when("I create a RouteConfig from the GraphConfig")
def step_create_route_from_graph(context):
    context.result_route_config = RouteConfig.from_graph_config(
        context.source_graph_config
    )


@then("the RouteConfig should have graph type")
def step_verify_graph_type(context):
    assert context.result_route_config.type == RouteType.GRAPH


@then("it should preserve all graph configuration details")
def step_verify_graph_details_preserved(context):
    route = context.result_route_config
    source = context.source_graph_config
    assert route.name == source.name
    assert route.nodes
    assert route.edges
    assert route.entry_point == source.entry_point
    assert route.checkpointing == source.checkpointing
    assert (
        (route.checkpoint_dir == str(source.checkpoint_dir))
        if source.checkpoint_dir
        else True
    )
    assert route.enable_time_travel == source.enable_time_travel
    assert route.parallel_execution == source.parallel_execution
    assert route.metadata == source.metadata


@then("node configurations should be converted to dictionaries")
def step_verify_node_conversion(context):
    assert all(isinstance(v, dict) for v in context.result_route_config.nodes.values())


@then("edge configurations should be converted to dictionaries")
def step_verify_edge_conversion(context):
    assert all(isinstance(e, dict) for e in context.result_route_config.edges)


@given("I have a RouteConfig with bridge type")
def step_bridge_type_config(context):
    context.bridge_route = RouteConfig(
        name="bridge_route", type=RouteType.BRIDGE, bridge=BridgeConfig()
    )


@when("I analyze the route complexity")
def step_analyze_route_complexity(context):
    context.analysis = RouteComplexityAnalyzer.analyze_route(context.bridge_route)


@then("it should return bridge complexity analysis")
def step_bridge_analysis(context):
    assert context.analysis.get("complexity") == "bridge"


@then('the complexity should be "{expected}"')
def step_complexity_value(context, expected):
    assert str(context.analysis.get("complexity")) == expected


@then("the score should be {score:d}")
def step_score_value(context, score):
    assert int(context.analysis.get("score", -1)) == score


@given("I have stream routes with different features")
def step_stream_routes_features(context):
    context.stream_routes = [
        RouteConfig(name="simple", type=RouteType.STREAM, operators=[]),
        RouteConfig(
            name="ops",
            type=RouteType.STREAM,
            operators=[{"type": "map"}, {"type": "filter"}],
        ),
        RouteConfig(
            name="connected",
            type=RouteType.STREAM,
            operators=[{"type": "map"}],
            subscriptions=["s1"],
            publications=["s2"],
        ),
        RouteConfig(
            name="hot", type=RouteType.STREAM, stream_type=StreamType.HOT, operators=[]
        ),
    ]


@when("I analyze each route complexity")
def step_analyze_each_route(context):
    context.analysis_results = [
        RouteComplexityAnalyzer.analyze_route(rc) for rc in context.stream_routes
    ]


@then("routes with more operators should have higher complexity scores")
def step_routes_more_ops_score(context):
    scores = [a["score"] for a in context.analysis_results]
    assert max(scores) >= min(scores)


@then("routes with routing connections should get additional score")
def step_routes_connections_score(context):
    scores = [a["score"] for a in context.analysis_results]
    assert scores[2] >= scores[1]


@then("hot streams should get additional score")
def step_hot_stream_score(context):
    scores = [a["score"] for a in context.analysis_results]
    assert scores[3] >= scores[0]


@then("the complexity classification should match the score ranges")
def step_complexity_classification(context):
    for analysis in context.analysis_results:
        c = analysis["complexity"]
        s = analysis["score"]
        if s <= 2:
            assert c == "simple"
        elif s <= 5:
            assert c in ("simple", "moderate")
        else:
            assert c in ("moderate", "complex")


@given("I have graph routes with conditional edges")
def step_graph_routes_conditionals(context):
    context.graph_routes = [
        RouteConfig(
            name="cond_graph",
            type=RouteType.GRAPH,
            nodes={"n1": {"type": "agent"}, "n2": {"type": "function"}},
            edges=[{"source": "n1", "target": "n2", "condition": {"field": "x"}}],
            checkpointing=True,
            enable_time_travel=True,
        )
    ]


@when("I analyze the graph complexity")
def step_analyze_graph_complexity(context):
    context.analysis_results = [
        RouteComplexityAnalyzer.analyze_route(rc) for rc in context.graph_routes
    ]


@then("conditional edges should increase the complexity score")
def step_cond_edges_score(context):
    assert context.analysis_results[0]["score"] >= 9


@then("the features should include conditional edge information")
def step_cond_edges_features(context):
    assert any("conditional" in f for f in context.analysis_results[0]["features"])


@then("checkpointing should increase the score")
def step_checkpointing_score(context):
    assert "checkpointing enabled" in context.analysis_results[0]["features"]


@then("time travel should increase the score")
def step_time_travel_score(context):
    assert "time travel enabled" in context.analysis_results[0]["features"]


@given("I have streams with different complexity scores")
def step_streams_for_recommendations(context):
    context.stream_scores = [
        RouteComplexityAnalyzer._get_stream_recommendation(1),
        RouteComplexityAnalyzer._get_stream_recommendation(3),
        RouteComplexityAnalyzer._get_stream_recommendation(6),
    ]


@when("I get recommendations for each stream")
def step_get_stream_recs(context):
    context.recs = context.stream_scores


@then("simple streams should get simple transformation recommendation")
def step_simple_stream_rec(context):
    assert "simple" in context.recs[0].lower()


@then("moderate streams should get multi-step processing recommendation")
def step_moderate_stream_rec(context):
    assert (
        "multi-step" in context.recs[1].lower()
        or "pipelines" in context.recs[1].lower()
    )


@then("complex streams should get graph consideration recommendation")
def step_complex_stream_rec(context):
    assert "graph" in context.recs[2].lower() or "consider" in context.recs[2].lower()


@given("I have graphs with different complexity scores")
def step_graphs_for_recommendations(context):
    context.graph_scores = [
        RouteComplexityAnalyzer._get_graph_recommendation(8),
        RouteComplexityAnalyzer._get_graph_recommendation(12),
        RouteComplexityAnalyzer._get_graph_recommendation(18),
    ]


@when("I get recommendations for each graph")
def step_get_graph_recs(context):
    context.graph_recs = context.graph_scores


@then("moderate graphs should get conditional logic recommendation")
def step_moderate_graph_rec(context):
    assert "conditional" in context.graph_recs[0].lower()


@then("complex graphs should get stateful workflow recommendation")
def step_complex_graph_rec(context):
    assert "stateful" in context.graph_recs[1].lower()


@then("advanced graphs should get feature evaluation recommendation")
def step_advanced_graph_rec(context):
    assert (
        "advanced" in context.graph_recs[2].lower()
        or "ensure" in context.graph_recs[2].lower()
    )


@given("I have different requirement scenarios")
def step_requirement_scenarios(context):
    context.requirements = [
        {"needs_persistence": True},
        {"needs_state": True, "needs_conditionals": True},
        {"needs_conditionals": True, "is_continuous": False},
        {"is_stateless": True, "is_continuous": True},
        {},
    ]


@when("I ask for route type suggestions")
def step_suggest_route_types(context):
    context.suggestions = [
        RouteComplexityAnalyzer.suggest_route_type(req) for req in context.requirements
    ]


@then("persistence requirements should suggest graph")
def step_persistence_suggests_graph(context):
    assert context.suggestions[0] == RouteType.GRAPH


@then("state with conditionals should suggest graph")
def step_state_conditionals_suggest_graph(context):
    assert context.suggestions[1] == RouteType.GRAPH


@then("conditionals without continuous should suggest graph")
def step_conditionals_no_continuous_graph(context):
    assert context.suggestions[2] == RouteType.GRAPH


@then("stateless continuous should suggest stream")
def step_stateless_continuous_stream(context):
    assert context.suggestions[3] == RouteType.STREAM


@then("simple cases should default to stream")
def step_simple_cases_stream(context):
    assert context.suggestions[4] == RouteType.STREAM


@given("I create a stream RouteConfig without specifying stream_type")
def step_stream_without_stream_type(context):
    context.test_config = RouteConfig(name="default_stream", type=RouteType.STREAM)


@when("the post_init validation runs")
def step_post_init_validation(context):
    context.post_init_type = context.test_config.stream_type


@then("the stream_type should default to COLD")
def step_stream_type_default(context):
    assert context.post_init_type == StreamType.COLD


@given("I have a RouteConfig with various node types and configurations")
def step_complex_node_route(context):
    context.complex_route = RouteConfig(
        name="complex_graph",
        type=RouteType.GRAPH,
        nodes={
            "agent_node": {
                "type": "agent",
                "agent": "agentA",
                "retry_policy": {"max_retries": 2},
                "timeout": 5,
            },
            "func_node": {
                "type": "function",
                "function": "fn1",
                "parallel": True,
                "metadata": {"x": 1},
            },
            "router": {
                "type": "message_router",
                "metadata": {"rules": [{"target": "end"}]},
            },
        },
        edges=[
            {"source": "start", "target": "agent_node"},
            {
                "source": "agent_node",
                "target": "func_node",
                "condition": {"equals": "ok"},
            },
            {"source": "func_node", "target": "router"},
            {"source": "router", "target": "end"},
        ],
        entry_point="start",
        checkpointing=False,
        enable_time_travel=False,
        parallel_execution=True,
        metadata={"meta": True},
    )


@when("I convert it to GraphConfig")
def step_convert_complex_route(context):
    context.graph_config_result = context.complex_route.to_graph_config()


@then("all node types should be properly converted")
def step_all_nodes_converted(context):
    assert set(context.graph_config_result.nodes.keys()) == set(
        context.complex_route.nodes.keys()
    )


@then("all node properties should be preserved")
def step_node_properties_preserved(context):
    for name, node_cfg in context.graph_config_result.nodes.items():
        source = context.complex_route.nodes[name]
        if "agent" in source:
            assert node_cfg.agent == source["agent"]
        if "function" in source:
            assert node_cfg.function == source["function"]


@then("retry policies and timeouts should be included")
def step_retry_timeout_included(context):
    node = context.graph_config_result.nodes["agent_node"]
    assert node.retry_policy == context.complex_route.nodes["agent_node"].get(
        "retry_policy"
    )
    assert node.timeout == context.complex_route.nodes["agent_node"].get("timeout")


@then("conditions and subgraphs should be handled")
def step_conditions_subgraphs(context):
    edge = next(e for e in context.graph_config_result.edges if e.target == "func_node")
    assert edge.condition is not None


@then("node metadata should be preserved in conversion")
def step_metadata_preserved(context):
    router = context.graph_config_result.nodes["router"]
    assert router.metadata.get("rules") is not None
