from typing import Any

import pytest
from hypothesis import given, settings, strategies as st
from shapely.geometry import Polygon

from inire.geometry.collision import CollisionEngine
from inire.geometry.primitives import Port
from inire.router.astar import AStarRouter
from inire.router.cost import CostEvaluator
from inire.router.danger_map import DangerMap
from inire.router.pathfinder import RoutingResult
from inire.utils.validation import validate_routing_result


@st.composite
def random_obstacle(draw: Any) -> Polygon:
    x = draw(st.floats(min_value=0, max_value=20))
    y = draw(st.floats(min_value=0, max_value=20))
    w = draw(st.floats(min_value=1, max_value=5))
    h = draw(st.floats(min_value=1, max_value=5))
    return Polygon([(x, y), (x + w, y), (x + w, y + h), (x, y + h)])


@st.composite
def random_port(draw: Any) -> Port:
    x = draw(st.floats(min_value=0, max_value=20))
    y = draw(st.floats(min_value=0, max_value=20))
    orientation = draw(st.sampled_from([0, 90, 180, 270]))
    return Port(x, y, orientation)


@settings(max_examples=3, deadline=None)
@given(obstacles=st.lists(random_obstacle(), min_size=0, max_size=3), start=random_port(), target=random_port())
def test_fuzz_astar_no_crash(obstacles: list[Polygon], start: Port, target: Port) -> None:
    engine = CollisionEngine(clearance=2.0)
    for obs in obstacles:
        engine.add_static_obstacle(obs)

    danger_map = DangerMap(bounds=(0, 0, 30, 30))
    danger_map.precompute(obstacles)

    evaluator = CostEvaluator(engine, danger_map)
    router = AStarRouter(evaluator)
    router.node_limit = 5000  # Lower limit for fuzzing stability

    # Check if start/target are inside obstacles (safety zone check)
    # The router should handle this gracefully (either route or return None)
    try:
        path = router.route(start, target, net_width=2.0)

        # Analytic Correctness: if path is returned, verify it's collision-free
        if path:
            result = RoutingResult(net_id="default", path=path, is_valid=True, collisions=0)
            validation = validate_routing_result(
                result,
                obstacles,
                clearance=2.0,
                expected_start=start,
                expected_end=target,
            )
            assert validation["is_valid"], f"Validation failed: {validation.get('reason')}"

    except Exception as e:
        # Unexpected exceptions are failures
        pytest.fail(f"Router crashed with {type(e).__name__}: {e}")
