"""Integration test: legacy datastax flows upgrade cleanly.

Generated by scripts/migrate/port_bundle.py.  Mirrors the duckduckgo and
arxiv pilots: every bundle class is exercised through the migration
table via the four legacy forms (bare name, full import path, package
re-export path, pre-Phase-A slot), plus distribution importability,
manifest discovery, and loader resolution.
"""

from __future__ import annotations

import json
from importlib import metadata as importlib_metadata
from pathlib import Path

import pytest
from lfx.extension.migration.loader import load_migration_table

REPO_ROOT = Path(__file__).resolve().parents[5]
TABLE_PATH = REPO_ROOT / "src" / "lfx" / "src" / "lfx" / "extension" / "migration" / "migration_table.json"

BUNDLE_CLASSES: tuple[tuple[str, str], ...] = (
    ("AstraDBCQLToolComponent", "astradb_cql"),
    ("AstraDBChatMemory", "astradb_chatmemory"),
    ("AstraDBDataAPIComponent", "astradb_data_api"),
    ("AstraDBGraphVectorStoreComponent", "astradb_graph"),
    ("AstraDBToolComponent", "astradb_tool"),
    ("AstraDBVectorStoreComponent", "astradb_vectorstore"),
    ("AstraVectorizeComponent", "astradb_vectorize"),
    ("Dotenv", "dotenv"),
    ("GraphRAGComponent", "graph_rag"),
    ("HCDVectorStoreComponent", "hcd"),
)


@pytest.fixture(scope="module")
def migration_table():
    table, error = load_migration_table(TABLE_PATH)
    assert error is None, f"failed to load migration table: {error}"
    assert table is not None
    return table


def _saved_flow_node(node_id: str, type_value: str) -> dict:
    return {
        "id": node_id,
        "type": "genericNode",
        "data": {"id": node_id, "type": type_value, "node": {"template": {}}},
    }


def _saved_flow(*nodes: dict) -> dict:
    return {"data": {"nodes": list(nodes), "edges": []}}


@pytest.mark.integration
@pytest.mark.parametrize(("class_name", "module_stem"), BUNDLE_CLASSES)
def test_legacy_bare_name_flow_upgrades(migration_table, class_name: str, module_stem: str) -> None:  # noqa: ARG001
    """Bare class name rewrites to the canonical namespaced ID."""
    from lfx.extension.migration.rewrite import migrate_flow_payload

    expected = f"ext:datastax:{class_name}@official"
    flow = _saved_flow(_saved_flow_node(f"datastax-bare-{class_name}", class_name))
    report = migrate_flow_payload(flow, table=migration_table)
    assert report.rewritten_count == 1
    assert flow["data"]["nodes"][0]["data"]["type"] == expected
    [record] = report.records
    assert record.legacy_form_kind == "bare_class_name"
    assert record.new_value == expected


@pytest.mark.integration
@pytest.mark.parametrize(("class_name", "module_stem"), BUNDLE_CLASSES)
def test_legacy_import_path_flow_upgrades(migration_table, class_name: str, module_stem: str) -> None:
    """Full dotted import path rewrites to the canonical namespaced ID."""
    from lfx.extension.migration.rewrite import migrate_flow_payload

    expected = f"ext:datastax:{class_name}@official"
    legacy = f"lfx.components.datastax.{module_stem}.{class_name}"
    flow = _saved_flow(_saved_flow_node(f"datastax-full-{class_name}", legacy))
    report = migrate_flow_payload(flow, table=migration_table)
    assert report.rewritten_count == 1
    assert flow["data"]["nodes"][0]["data"]["type"] == expected
    assert report.records[0].legacy_form_kind == "import_path"


@pytest.mark.integration
@pytest.mark.parametrize(("class_name", "module_stem"), BUNDLE_CLASSES)
def test_short_import_path_flow_upgrades(migration_table, class_name: str, module_stem: str) -> None:  # noqa: ARG001
    """Package-level import-path rewrites cleanly."""
    from lfx.extension.migration.rewrite import migrate_flow_payload

    expected = f"ext:datastax:{class_name}@official"
    legacy = f"lfx.components.datastax.{class_name}"
    flow = _saved_flow(_saved_flow_node(f"datastax-short-{class_name}", legacy))
    report = migrate_flow_payload(flow, table=migration_table)
    assert report.rewritten_count == 1
    assert flow["data"]["nodes"][0]["data"]["type"] == expected


@pytest.mark.integration
def test_lfx_datastax_distribution_is_importable() -> None:
    """The bundle is importable in the development workspace."""
    try:
        import lfx_datastax  # type: ignore[import-not-found]
    except ImportError:
        pytest.skip("lfx-datastax not installed in this test environment")

    for class_name, _module_stem in BUNDLE_CLASSES:
        klass = getattr(lfx_datastax, class_name, None)
        assert klass is not None, f"lfx_datastax does not re-export {class_name!r}"
        assert klass.__name__ == class_name


def _is_editable_install(dist: importlib_metadata.Distribution) -> bool:
    direct_url = dist.read_text("direct_url.json")
    if not direct_url:
        return False
    try:
        payload = json.loads(direct_url)
    except json.JSONDecodeError:
        return False
    return bool(payload.get("dir_info", {}).get("editable"))


@pytest.mark.integration
def test_lfx_datastax_ships_manifest() -> None:
    """``importlib.metadata`` can find ``extension.json`` for the installed dist."""
    try:
        dist = importlib_metadata.distribution("lfx-datastax")
    except importlib_metadata.PackageNotFoundError:
        pytest.skip("lfx-datastax not installed in this test environment")

    if _is_editable_install(dist):
        import lfx_datastax  # type: ignore[import-not-found]

        package_dir = Path(lfx_datastax.__file__).parent
        manifest_path = package_dir / "extension.json"
        assert manifest_path.is_file()
    else:
        files = dist.files or []
        manifests = [f for f in files if f.parts and f.parts[-1] == "extension.json"]
        assert manifests
        manifest_path = Path(dist.locate_file(manifests[0]))

    manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    assert manifest["id"] == "lfx-datastax"
    assert manifest["lfx"]["compat"] == ["1"]
    assert any(b["name"] == "datastax" for b in manifest["bundles"])
