"""
Pure, dependency-free helpers used by `app/api/v1/serializers.py`'s batched lookups.

Deliberately has ZERO imports from sqlalchemy/fastapi/pydantic (stdlib only) so it can be unit
tested in environments where those aren't installed -- same reasoning as
`app/engines/scoring/wiring.py` (StockLab overhaul, Part A1): this module holds the reducible,
DB-free logic; `serializers.py` holds the part that actually needs a `Session` to run the query.
"""

from typing import Iterable, Tuple


def aggregate_is_demo_rows(rows: Iterable[Tuple[str, bool]]) -> dict:
    """Reduce a list of (security_id, is_demo) pairs -- one row per FinancialPeriod, joined to its
    Source -- down to one bool per security_id: True if ANY of that security's periods came from a
    demo Source.

    AUDIT FIX (StockLab overhaul, Part A2, docs/AUDIT_PERFORMANCE.md's remaining
    company_summary() N+1 finding): this is the reduction step of
    `app/api/v1/serializers.py::is_demo_by_security()`, split out here specifically so it has real
    unit test coverage without a database. The original single-security
    `serializers.py::security_is_demo()` takes an arbitrary (unordered, `.first()`) matching row
    instead -- in practice a security's periods all come from one ingestion pipeline and are
    uniformly demo or uniformly real (never mixed), so this "any" aggregation is not expected to
    change any real result for a real security; documented as the one piece of the A2 fix that is
    not a byte-for-byte-identical query to the original, only an equivalent-in-practice one.
    """
    result: dict = {}
    for security_id, is_demo in rows:
        result[security_id] = result.get(security_id, False) or bool(is_demo)
    return result
