"""project_summary + entries_by_description over the migrated fixture
graph: T-PRJ-001/010/022/023/025, T-RPT-004 (expansion sums to parent),
and the missing-info view."""

import uuid
from decimal import Decimal

from app.services.reporting.filters import EntryFilters
from app.services.reporting.projects import entries_by_description, project_summary

SITE_REBUILD = uuid.UUID("07000000-0000-0000-0000-000000000001")


def row_for(result, project_name):
    return next(r for r in result["rows"] if r["project"] == project_name)


def test_t_prj_001_groups_are_stamp_sums(rpt_session, rian):
    result = project_summary(rpt_session, rian, EntryFilters())
    site = row_for(result, "Site Rebuild")
    assert site["row_count"] == 3  # te-1, te-2, te-7
    assert site["seconds"] == 10800
    assert site["cost"]["amount"] == "73.50"  # 14 + 7 + 52.50
    assert site["billout"]["amount"] == "142.50"  # 35 + 17.5 + 90


def test_t_prj_010_display_adjustment_never_stamped(rpt_session, rian):
    result = project_summary(rpt_session, rian, EntryFilters())
    site = row_for(result, "Site Rebuild")
    # raw*1.1 + 250 — display arithmetic only
    assert site["billout_adjusted"]["amount"] == "406.75"
    assert site["billout"]["amount"] == "142.50"  # stamps untouched
    assert site["billout_adjustment_pct"] == "10.0000"
    assert site["billout_adjustment_amount"]["amount"] == "250.00"


def test_t_prj_022_headline_margin_math(rpt_session, rian):
    """margin = Σincome − Σ(cost of rows WITH income); income-less rows'
    cost stays out (B7 parity)."""
    result = project_summary(rpt_session, rian, EntryFilters())
    summary = result["summary"]
    assert summary["income_rows"] == 1  # only Site Rebuild has income
    assert summary["income"]["amount"] == "12000.00"
    assert summary["margin"]["amount"] == "11926.50"  # 12000 − 73.50
    site = row_for(result, "Site Rebuild")
    assert site["income"]["amount"] == "12000.00"
    assert site["margin"]["amount"] == "11926.50"
    admin = row_for(result, "Admin")
    assert admin["income"] is None and admin["margin"] is None


def test_t_prj_023_non_owner_payload_has_no_money_keys(rpt_session, adi):
    """Income/billout/margin are ABSENT at the data layer for
    non-owners — not nulled, not UI-hidden (fixes judgment #20's leak
    class structurally)."""
    result = project_summary(rpt_session, adi, EntryFilters())
    assert result["rows"], "adi sees his own groups"
    for row in result["rows"]:
        for key in ("billout", "billout_adjusted", "income", "margin"):
            assert key not in row
        assert "cost" in row
    assert "income" not in result["summary"]
    assert "margin" not in result["summary"]


def test_unassigned_group_and_missing_info(rpt_session, rian):
    result = project_summary(rpt_session, rian, EntryFilters(missing_info=True))
    assert len(result["rows"]) == 1
    group = result["rows"][0]
    assert group["project_id"] is None
    assert (group["operator"], group["client"], group["project"]) == (
        "PlusROI", None, "Mystery",
    )
    assert group["row_count"] == 1


def test_t_rpt_004_expansion_sums_to_parent(rpt_session, rian):
    """ONE status predicate + ONE hours definition everywhere: the
    per-description breakdown adds up to its parent group — the §2.4
    staleness is dead."""
    for status in ("all", "pending", "applied"):
        f = EntryFilters(status=status)
        parent = next(
            (r for r in project_summary(rpt_session, rian, f)["rows"]
             if r["project"] == "Site Rebuild"),
            None,
        )
        expansion = entries_by_description(
            rpt_session, rian, f, project_id=SITE_REBUILD
        )
        if parent is None:
            assert expansion == []
            continue
        assert sum(r["row_count"] for r in expansion) == parent["row_count"]
        assert sum(r["seconds"] for r in expansion) == parent["seconds"]
        assert sum(Decimal(r["cost"]["amount"]) for r in expansion) == Decimal(
            parent["cost"]["amount"]
        )


def test_expansion_ordering_and_shape(rpt_session, rian):
    rows = entries_by_description(rpt_session, rian, EntryFilters(), project_id=SITE_REBUILD)
    seconds = [r["seconds"] for r in rows]
    assert seconds == sorted(seconds, reverse=True)
    assert {r["description"] for r in rows} == {
        "March work A", "March work B", "feb toggl work",
    }


def test_t_prj_025_unassigned_group_expands_via_raw_pins(rpt_session, rian):
    """The NULL-group expansion works — NULL-safe raw pins, distinct
    param names (fixes the legacy __null__ collision B20)."""
    rows = entries_by_description(
        rpt_session, rian, EntryFilters(),
        project_id=None, raw_operator="PlusROI", raw_client=None, raw_project="Mystery",
    )
    assert len(rows) == 1
    assert rows[0]["description"] == "who did this"
    assert rows[0]["seconds"] == 900


def test_expansion_respects_scope(rpt_session, adi):
    rows = entries_by_description(rpt_session, adi, EntryFilters(), project_id=SITE_REBUILD)
    # te-7 (rian's toggl batch) is outside adi's scope
    assert {r["description"] for r in rows} == {"March work A", "March work B"}
