"""Regression test: Spoolman fallback split silently deducted filament from
spools not used in the current print.

_spoolman_unreported() falls back to an equal split across every mapped
AMS slot when per-slot attribution data (_spoolman_slot_usage) is still
empty - previously true right after print start, because
_spoolman_sync_midprint() ran (in the poll loop) before the first
_spoolman_attribute_tick() had a chance to populate any per-slot data,
since _spoolman_last_sync was reset to 0.0 (== "due immediately") instead
of the current time. With 4 spools mapped, that meant the full
(possibly already nonzero) supplies_usage got reported equally to all 4
spools regardless of which one was actually printing with.

The fix: only fall back to a full-credit report when there is exactly
one mapped slot (single-extruder, no ambiguity possible) - otherwise
report nothing until real attribution data exists. Additionally,
_on_print() now resets _spoolman_last_sync to the current time (not 0.0)
at print start, so the first mid-print sync check is due only after a
real sync_rate interval has passed.
"""
import time


def _configure(bridge, slot_spools):
    bridge._spoolman_slot_spools = dict(slot_spools)
    bridge._spoolman_slot_usage = {}
    bridge._spoolman_slot_reported = {}


async def test_unreported_reports_nothing_for_multiple_slots_without_attribution(client):
    _, bridge = client
    _configure(bridge, {0: 11, 1: 15, 2: 17, 3: 23})
    bridge._state["supplies_usage"] = 4369

    result = bridge._spoolman_unreported()

    assert result == {}


async def test_unreported_credits_the_single_slot_without_attribution(client):
    _, bridge = client
    _configure(bridge, {0: 42})
    bridge._state["supplies_usage"] = 500

    result = bridge._spoolman_unreported()

    assert result == {0: 500}


async def test_unreported_single_slot_subtracts_already_reported(client):
    _, bridge = client
    _configure(bridge, {0: 42})
    bridge._spoolman_slot_reported = {0: 200.0}
    bridge._state["supplies_usage"] = 500

    result = bridge._spoolman_unreported()

    assert result == {0: 300.0}


async def test_unreported_uses_real_attribution_once_available(client):
    _, bridge = client
    _configure(bridge, {0: 11, 1: 15})
    bridge._spoolman_slot_usage = {0: 120.0}
    bridge._state["supplies_usage"] = 120

    result = bridge._spoolman_unreported()

    # Only the slot that was actually attributed usage gets a nonzero report;
    # the untouched slot must not receive any share of it.
    assert result[0] == 120.0
    assert result[1] == 0.0


async def test_print_start_resets_last_sync_to_now_not_epoch(client):
    """_spoolman_last_sync=0.0 at print start meant the poll loop's mid-print
    sync check (time.time() - last_sync >= sync_rate) was true on the very
    first tick, before _spoolman_attribute_tick() ever ran once - triggering
    the fallback-split bug above immediately after print start, before any
    real per-slot usage existed yet."""
    _, bridge = client
    before = time.time()

    bridge._on_print({"state": "printing", "data": {"filename": "test.gcode"}})

    assert bridge._spoolman_last_sync >= before
    assert bridge._spoolman_last_sync <= time.time()
