"""THE critical tests (ADR #015): a user whose only membership is tenant
B sees ZERO tenant-A rows through every list endpoint, and a tenant-A
object id is a 404 (existence hidden), never a 403. Totals collapse to
zero-shapes for a member who can see nothing."""

from __future__ import annotations

import pytest

MONTH = "2026-06"


@pytest.fixture()
def bob(client_factory):
    return client_factory("bob")


@pytest.fixture()
def carol(client_factory):
    return client_factory("carol")


# ---------------------------------------------------------------- list isolation


def test_entries_list_hides_tenant_a(bob, graph):
    body = bob.get("/api/entries", params={"status": "all"}).json()
    ids = {row["id"] for row in body["rows"]}
    assert ids & graph.A.entry_ids == set()  # not one A row
    assert graph.B.entry_ids <= ids  # bob's own B rows ARE present
    assert all("BW" not in (row.get("project") or "") for row in body["rows"])


def test_entry_totals_reflect_only_tenant_b(bob):
    totals = bob.get("/api/entries/totals", params={"status": "all"}).json()
    # 2 priced B entries + 1 blocked B entry = 3; A's rows never counted.
    assert totals["row_count"] == 3


def test_summary_hides_tenant_a_projects(bob):
    body = bob.get("/api/summary", params={"month": MONTH}).json()
    names = {row.get("project") for row in body["rows"]}
    assert "BW Website" not in names
    assert "PR Website" in names


def test_projects_list_hides_tenant_a(bob, graph):
    body = bob.get("/api/projects").json()
    ids = {row.get("project_id") for row in body["rows"]}
    assert graph.A.project_id not in ids
    assert graph.B.project_id in ids


def test_invoices_list_hides_tenant_a(bob, graph):
    body = bob.get("/api/invoices").json()
    ids = {row["id"] for row in body["rows"]}
    assert graph.A.invoice_id not in ids
    assert graph.B.invoice_id in ids
    assert {row["name"] for row in body["rows"]} == {"PR INV-001"}


def test_import_batches_list_hides_tenant_a(bob, graph):
    body = bob.get("/api/import/batches").json()
    ids = {row["id"] for row in body["rows"]}
    assert graph.A.batch_id not in ids
    assert graph.B.batch_id in ids


def test_comments_list_hides_tenant_a(bob):
    body = bob.get("/api/comments").json()
    bodies = {c["body"] for c in body["comments"]}
    assert "BW board note" not in bodies
    assert "PR board note" in bodies


def test_cc_batches_list_hides_tenant_a(bob, graph):
    body = bob.get("/api/cc/batches").json()
    ids = {row["id"] for row in body["rows"]}
    assert graph.A.cc_batch_id not in ids
    assert graph.B.cc_batch_id in ids


# ------------------------------------------------ direct-GET-by-id: 404 not 403


def test_direct_get_tenant_a_invoice_is_404(bob, graph):
    resp = bob.get(f"/api/invoices/{graph.A.invoice_id}")
    assert resp.status_code == 404  # existence hidden, NOT 403


def test_direct_get_tenant_a_cc_batch_is_404(bob, graph):
    resp = bob.get(f"/api/cc/batches/{graph.A.cc_batch_id}")
    assert resp.status_code == 404


def test_direct_get_tenant_a_batch_summary_is_404(bob, graph):
    resp = bob.get(f"/api/import/batches/{graph.A.batch_id}/summary")
    assert resp.status_code == 404


# ---------------------------------------------------------------- zero-shapes


def test_member_who_sees_nothing_gets_zero_shape_totals(carol):
    # carol is a B worker who imported nothing -> zero visible entries, but
    # she is NOT NO_TENANT (she has a B membership), so it's zero-shape, not 403.
    listed = carol.get("/api/entries", params={"status": "all"})
    assert listed.status_code == 200
    assert listed.json()["total_count"] == 0
    totals = carol.get("/api/entries/totals", params={"status": "all"}).json()
    assert totals["row_count"] == 0
