"""The canonical document is byte-identical regardless of the order the
database hands rows back (spec.md: all determinism lives in keys and
explicitly sorted lists)."""

import random
from decimal import Decimal

from extract_legacy import (
    assemble,
    build_cc,
    build_entities,
    build_invoices,
    build_months,
    canonical_dumps,
    unassigned_key,
)

D = Decimal
P1 = "11111111-1111-1111-1111-111111111111"
P2 = "22222222-2222-2222-2222-222222222222"
INV = "aaaaaaaa-0000-0000-0000-000000000001"
BATCH = "bbbbbbbb-0000-0000-0000-000000000001"
META = {"extracted_at": "2026-07-07T00:00:00Z", "source_mode": "supabase-public",
        "schema": "public", "row_counts": {"time_entries": 5}}


def build_doc() -> dict:
    operator_rows = [("PlusROI",), ("Bowden Works",)]
    client_rows = [("PlusROI", "Brentwood"), ("Bowden Works", "Internal")]
    project_rows = [
        (P1, "Site Rebuild", "Brentwood", "PlusROI", D("1200.00"), D("10"), D("50")),
        (P2, "Admin", "Internal", "Bowden Works", None, None, None),
    ]
    member_rows = [
        ("rian@rian.ca", "Rian Bowden", D("50.00"), D("75.00"), True),
        ("rian@rian.ca", "Rian Bowden", D("0.00"), D("25.00"), True),
        ("info@adipramono.com", "Adi Pramono", D("14.00"), D("35.00"), True),
    ]
    assigned = [
        ("2026-03", P1, 3, 5400, D("75.00"), D("112.50"), D("150.0000"), D("80.00")),
        ("2026-04", P2, 1, 3600, D("50.00"), D("75.00"), None, None),
    ]
    unassigned = [
        ("2026-03", "PlusROI", None, "Mystery", 1, 1800, None, None, None, None),
    ]
    invoice_rows = [(INV, "PlusROI 2026-03", "paid", None, D("120.00"))]
    attached = [(INV, "cccccccc-0000-0000-0000-000000000002", D("60.00")),
                (INV, "cccccccc-0000-0000-0000-000000000001", D("52.50"))]

    def shuffled(rows):
        rows = list(rows)
        random.shuffle(rows)
        return rows

    entities = build_entities(
        shuffled(operator_rows), shuffled(client_rows), shuffled(project_rows),
        {P1: 4, P2: 1}, shuffled(member_rows),
        {"rian@rian.ca": 2, "info@adipramono.com": 3},
        unassigned_entry_count=1)
    months = build_months(shuffled(assigned), shuffled(unassigned),
                          entities["projects"])
    invoices = build_invoices(shuffled(invoice_rows), shuffled(attached))
    cc = build_cc([(BATCH, "March statement")],
                  shuffled([(BATCH, P1, 2, D("40.10")), (BATCH, None, 1, None)]),
                  highlight_keyword_count=2, auto_rule_count=1)
    return assemble(months, invoices, cc, entities, META)


def test_output_is_deterministic_across_row_orderings():
    random.seed(1)
    first = canonical_dumps(build_doc())
    for seed in range(2, 6):
        random.seed(seed)
        assert canonical_dumps(build_doc()) == first


def test_canonical_content_pins_the_contract():
    random.seed(0)
    doc = build_doc()

    # grouping keys: project uuid for assigned, verbatim triple for not
    march = doc["months"]["2026-03"]
    assert set(march) == {P1, unassigned_key("PlusROI", None, "Mystery")}
    assert 'unassigned:["PlusROI",null,"Mystery"]' in march

    # adjusted billout: 112.50 * 1.10 + 50 = 173.75; unadjusted carries raw
    group = march[P1]
    assert group["billout"] == {"CAD": "112.50"}
    assert group["billout_adjusted"] == {"CAD": "173.75"}
    assert group["source_rate_sum"] == {"CAD": "150.0000"}

    # unassigned group: adjusted == raw, all-NULL sums emit zero money
    blocked = march[unassigned_key("PlusROI", None, "Mystery")]
    assert blocked["billout"] == blocked["billout_adjusted"] == {"CAD": "0.00"}

    # attached entry ids sorted; computed vs manual totals both present
    invoice = doc["invoices"][INV]
    assert invoice["attached_entry_ids"] == sorted(invoice["attached_entry_ids"])
    assert invoice["computed_total"] == {"CAD": "112.50"}
    assert invoice["manual_total"] == {"CAD": "120.00"}

    # entity rollups flow through the FK chain; rian's two rows, one key
    assert doc["entities"]["operators"]["PlusROI"]["entry_count"] == 4
    assert len(doc["entities"]["team_members"]["rian@rian.ca"]["rates"]) == 2
    assert "_adj_pct" not in doc["entities"]["projects"][P1]

    # cc: unassigned lines under the plain key; batch line_count totals up
    batch = doc["cc"]["batches"][BATCH]
    assert batch["line_count"] == 3
    assert batch["assigned_totals"]["unassigned"]["amount"] == {"CAD": "0.00"}
