```python
def _why_refused(cap, left: int, amount: int) -> str:
    if cap is None:
        return "unknown"
    if amount > cap[1]:
        return "single"
    return "cap" if amount > left else ""


def apply_charges(caps: dict, charges: list) -> dict:
    left = {name: cap[0] for name, cap in caps.items()}
    refused = []
    reasons = {
        "unknown": [],
        "single": [],
        "cap": [],
    }

    for charge_id, bucket, amount in charges:
        if not isinstance(amount, int) or amount <= 0:
            raise ValueError("Amount must be a positive whole number")

        reason = _why_refused(caps.get(bucket), left.get(bucket), amount)
        if reason:
            reasons[reason].append((charge_id, reason))
        else:
            left[bucket] -= amount

    for reason in ["unknown", "single", "cap"]:
        refused.extend(reasons[reason])

    return {"left": left, "refused": refused}
```