import pytest

from blends.stack import (
    MultiFileResolutionRequest,
    resolve_definitions_multi_file,
)
from blends.stack.forward_stitcher import ForwardStitcherConfig
from blends.stack.partial_path_database import PartialPathDatabase
from test.stack.test_helpers import (
    QUERY_MAX_PHASES,
    QUERY_MAX_WORK,
    find_symbol_nodes,
    setup_multi_file_database,
)

_CONFIG = ForwardStitcherConfig(
    max_phases=QUERY_MAX_PHASES,
    max_total_work=QUERY_MAX_WORK,
)


def _resolve(
    symbol: str,
    *,
    from_fh: int,
    views: dict,  # type: ignore[type-arg]
    database: PartialPathDatabase,
) -> list:  # type: ignore[type-arg]
    view = views[from_fh]
    symbol_id = view.symbols.symbol_to_id.get(symbol)
    assert symbol_id is not None, f"Symbol '{symbol}' not in view fh={from_fh}"
    ref_nodes = find_symbol_nodes(view, symbol_id=symbol_id, is_definition=False)
    assert ref_nodes, f"No ref nodes for '{symbol}' in fh={from_fh}"
    result = resolve_definitions_multi_file(
        view,
        database,
        request=MultiFileResolutionRequest(
            file_handle=from_fh,
            ref_node_index=ref_nodes[0],
            config=_CONFIG,
        ),
        views=views,
    )
    assert isinstance(result, list)
    return result


@pytest.mark.blends_test_group("functional")
def test_single_segment_regression() -> None:
    fh_core, fh_orch = 1, 2
    views, database = setup_multi_file_database(
        {"mf_core.py": fh_core, "mf_orchestrator.py": fh_orch}
    )
    result = _resolve("SENTINEL", from_fh=fh_orch, views=views, database=database)
    contributing = {fh for fh, _ in result}
    assert contributing == {fh_core}, (
        f"SENTINEL should resolve only to mf_core.py (fh={fh_core}), got {contributing}"
    )


@pytest.mark.blends_test_group("functional")
def test_two_segment_import_resolves() -> None:
    fh_mod, fh_consumer = 1, 2
    views, database = setup_multi_file_database(
        {"pkg/mod.py": fh_mod, "pkg_mod_consumer.py": fh_consumer}
    )
    result = _resolve("HELPER", from_fh=fh_consumer, views=views, database=database)
    contributing = {fh for fh, _ in result}
    assert contributing == {fh_mod}, (
        f"HELPER should resolve only to pkg/mod.py (fh={fh_mod}), got {contributing}"
    )


@pytest.mark.blends_test_group("functional")
def test_stem_collision_no_cross_package_resolution() -> None:
    fh_pkg_mod, fh_other_mod, fh_consumer = 1, 2, 3
    views, database = setup_multi_file_database(
        {
            "pkg/mod.py": fh_pkg_mod,
            "other/mod.py": fh_other_mod,
            "pkg_mod_consumer.py": fh_consumer,
        }
    )
    result = _resolve("HELPER", from_fh=fh_consumer, views=views, database=database)
    contributing_fhs = {fh for fh, _ in result}
    assert fh_pkg_mod in contributing_fhs, (
        f"HELPER should resolve to pkg/mod.py (fh={fh_pkg_mod}), got {contributing_fhs}"
    )
    assert fh_other_mod not in contributing_fhs, (
        f"other/mod.py (fh={fh_other_mod}) should not contribute to HELPER resolution"
    )


@pytest.mark.blends_test_group("functional")
def test_three_segment_import_resolves() -> None:
    fh_util, fh_consumer = 1, 2
    views, database = setup_multi_file_database(
        {"pkg/sub/util.py": fh_util, "pkg_sub_consumer.py": fh_consumer}
    )
    result = _resolve("UTIL", from_fh=fh_consumer, views=views, database=database)
    contributing = {fh for fh, _ in result}
    assert contributing == {fh_util}, (
        f"UTIL should resolve only to pkg/sub/util.py (fh={fh_util}), got {contributing}"
    )


@pytest.mark.blends_test_group("functional")
def test_init_as_package_entry_resolves() -> None:
    fh_init, fh_consumer = 1, 2
    views, database = setup_multi_file_database(
        {"pkg/sub/__init__.py": fh_init, "pkg_sub_consumer.py": fh_consumer}
    )
    result = _resolve("SUB_VALUE", from_fh=fh_consumer, views=views, database=database)
    contributing = {fh for fh, _ in result}
    assert contributing == {fh_init}, (
        f"SUB_VALUE should resolve only to pkg/sub/__init__.py (fh={fh_init}), got {contributing}"
    )
