"""Resolution computation — known/unknown chaining (imports.md B42/B43),
incomplete buckets (B44), summary states (B45 / T-IMP-013)."""

from app.services.imports.resolver import (
    bucket_signature,
    catalog_payload,
    compute_resolution,
)


def entry(operator="Bowden Works", client="Brentwood", project="Website", **kw):
    return {
        "operator": operator,
        "client": client,
        "project": project,
        "source_user_email": "info@adipramono.com",
        **kw,
    }


def test_all_known_when_names_match_case_insensitively(graph):
    res = compute_resolution(
        graph.session,
        graph.tenant.id,
        [entry(operator="bowden works", client="BRENTWOOD", project="website")],
    )
    assert res.all_known
    assert res.known_operators[0]["canonical_name"] == "Bowden Works"
    assert res.known_clients[0]["canonical_name"] == "Brentwood"
    assert res.known_projects[0]["canonical_name"] == "Website"


def test_unknown_names_are_listed_with_parent_linkage(graph):
    res = compute_resolution(
        graph.session,
        graph.tenant.id,
        [
            entry(operator="NewCo", client="NewClient", project="NewProject"),
            entry(client="Vicotria", project="Blocks"),  # typo under a known op
        ],
    )
    assert [o["name"] for o in res.unknown_operators] == ["NewCo"]
    unknown_clients = {c["name"]: c for c in res.unknown_clients}
    assert set(unknown_clients) == {"NewClient", "Vicotria"}
    # the typo'd client sits under a KNOWN operator -> linkage provided
    assert unknown_clients["Vicotria"]["operator_id"] is not None
    assert unknown_clients["NewClient"]["operator_id"] is None


def test_known_ness_chains_through_parents(graph):
    """A project name that exists under a DIFFERENT client is unknown
    here: known-ness is (client-scoped), chained through the operator."""
    res = compute_resolution(
        graph.session,
        graph.tenant.id,
        [entry(client="Victoria", project="Website")],  # Website is Brentwood's
    )
    assert res.known_clients and not res.unknown_clients
    assert [p["name"] for p in res.unknown_projects] == ["Website"]


def test_imp_buckets_grouped_sorted_desc_with_missing_labels(graph):
    entries = [
        entry(client=None),
        entry(client=None),
        entry(operator=None, client=None, project="Solo"),
    ]
    res = compute_resolution(graph.session, graph.tenant.id, entries)
    assert res.missing_rows == 3
    assert [b["count"] for b in res.buckets] == [2, 1]
    top = res.buckets[0]
    assert top["operator"] == "Bowden Works"
    assert top["client"] is None
    assert top["missing"] == ["client"]
    assert top["key"] == bucket_signature("Bowden Works", None, "Website")


def test_incomplete_rows_still_feed_operator_lists(graph):
    """B43: a row with op but no client contributes its operator to the
    distinct-operator list AND lands in a bucket."""
    res = compute_resolution(
        graph.session, graph.tenant.id, [entry(operator="NewCo", client=None)]
    )
    assert [o["name"] for o in res.unknown_operators] == ["NewCo"]
    assert len(res.buckets) == 1
    assert not res.all_known


def test_catalog_projects_labeled_client_colon_project(graph):
    res = compute_resolution(graph.session, graph.tenant.id, [entry()])
    catalog = catalog_payload(res.graph)
    labels = [p["label"] for p in catalog["projects"]]
    assert "Brentwood : Website" in labels
    assert "DaxTech : Website" in labels
    assert labels == sorted(labels, key=str.lower)
    operator_names = [o["name"] for o in catalog["operators"]]
    assert "Bowden Works" in operator_names and "PlusROI" in operator_names
