```python
"""Select fictional campaign proposals by descending priority."""

import re
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP, localcontext


_DECIMAL_PRICE = re.compile(
    r"[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?"
)


def _cost_in_cents(row):
    """Validate and convert an eligible row's cost to integer cents."""
    if "price" not in row:
        cost = row.get("cost_cents")
        if isinstance(cost, bool) or not isinstance(cost, int) or cost < 0:
            raise ValueError("cost_cents must be a nonnegative integer")
        return cost

    price = row["price"]
    if not isinstance(price, str):
        raise ValueError("price must be a finite nonnegative decimal string")

    price = price.strip()
    if _DECIMAL_PRICE.fullmatch(price) is None:
        raise ValueError("price must be a finite nonnegative decimal string")

    try:
        amount = Decimal(price)
    except InvalidOperation as exc:
        raise ValueError(
            "price must be a finite nonnegative decimal string"
        ) from exc

    if not amount.is_finite() or amount < 0:
        raise ValueError("price must be a finite nonnegative decimal string")

    if amount.is_zero():
        return 0

    # Preserve all significant digits before rounding this row to cents.
    with localcontext() as context:
        context.prec = max(28, len(amount.as_tuple().digits), amount.adjusted() + 3)
        cents = (amount * Decimal(100)).to_integral_value(
            rounding=ROUND_HALF_UP
        )
        return int(cents)


def plan(rows, budget_cents, excluded_channels=()):
    """Select affordable, eligible first occurrences in stable priority order."""
    if isinstance(budget_cents, bool) or not isinstance(budget_cents, int):
        raise ValueError("budget_cents must be a nonnegative integer")
    if budget_cents < 0:
        raise ValueError("budget_cents must be a nonnegative integer")

    normalized_exclusions = {
        channel.strip().casefold() for channel in excluded_channels
    }
    seen_ids = set()
    eligible = []

    for row in rows:
        proposal_id = row["id"]
        if proposal_id in seen_ids:
            continue
        seen_ids.add(proposal_id)

        if row.get("archived", False):
            continue

        channel = row.get("channel", "").strip().casefold()
        if channel in normalized_exclusions:
            continue

        cost_cents = _cost_in_cents(row)
        eligible.append((proposal_id, row.get("priority", 0), cost_cents))

    eligible.sort(key=lambda proposal: proposal[1], reverse=True)

    selected_ids = []
    remaining_cents = budget_cents

    for proposal_id, _, cost_cents in eligible:
        if cost_cents <= remaining_cents:
            selected_ids.append(proposal_id)
            remaining_cents -= cost_cents

    return {
        "selected_ids": selected_ids,
        "spent_cents": budget_cents - remaining_cents,
        "remaining_cents": remaining_cents,
    }
```