"""auction_tick — scheduled state transitions for MpsAuction.

Pure functions are unit-testable without a Pyramid environment; the
script entry point in scripts/auction_tick.py drives them with a real
dbsession.

Transitions:
    SCHEDULED + start_timestamp passed → ACTIVE
    ACTIVE    + end_timestamp passed   → ENDED (winner = is_winning bidder)

Idempotent: running the tick twice in a row is a no-op for any auction
that has already transitioned past the matching threshold.
"""

from ..models.auction import (
    AUCTION_STATE_ACTIVE,
    AUCTION_STATE_ENDED,
    AUCTION_STATE_SCHEDULED,
    MpsAuction,
    MpsBid,
    now_timestamp,
)


def transition_scheduled_to_active(auction, now_ms):
    """Pure: returns True iff the auction should transition.
    Caller (orchestrator) mutates state."""
    if auction.state != AUCTION_STATE_SCHEDULED:
        return False
    if auction.start_timestamp is None:
        return False
    return now_ms >= auction.start_timestamp


def transition_active_to_ended(auction, now_ms):
    """Pure: returns True iff the active auction should end now."""
    if auction.state != AUCTION_STATE_ACTIVE:
        return False
    if auction.end_timestamp is None:
        return False
    return now_ms >= auction.end_timestamp


def tick(dbsession, now_ms=None):
    """Run state transitions on every non-terminal auction.

    Returns a dict {activated: int, ended: int} of how many auctions
    moved each direction. Caller (script entry point) handles its own
    transaction/commit boundary.
    """
    if now_ms is None:
        now_ms = now_timestamp()

    activated = 0
    ended = 0

    # Pull all auctions that could need a transition. Two simple queries
    # avoid scanning the whole table — no MOAD-0001 here.
    scheduled = (
        dbsession.query(MpsAuction)
        .filter(MpsAuction.state == AUCTION_STATE_SCHEDULED)
        .filter(MpsAuction.start_timestamp <= now_ms)
        .all()
    )
    for auction in scheduled:
        auction.state = AUCTION_STATE_ACTIVE
        auction.updated_timestamp = now_ms
        activated += 1

    active = (
        dbsession.query(MpsAuction)
        .filter(MpsAuction.state == AUCTION_STATE_ACTIVE)
        .filter(MpsAuction.end_timestamp <= now_ms)
        .all()
    )
    for auction in active:
        auction.state = AUCTION_STATE_ENDED
        auction.updated_timestamp = now_ms

        # Record winner from is_winning bid (if any).
        winning_bid = (
            dbsession.query(MpsBid)
            .filter_by(auction_id=auction.id, is_winning=True)
            .one_or_none()
        )
        if winning_bid is not None:
            auction.winner_user_id = winning_bid.bidder_user_id
            auction.winning_bid_id = winning_bid.id
            # Set payment deadline to end + 48h by default.
            auction.payment_deadline_timestamp = now_ms + 48 * 3600 * 1000

        ended += 1

    return {"activated": activated, "ended": ended}
