```python
from typing import Dict, List, Tuple

class ChargeRejectionError(Exception):
    pass

def apply_charges(caps: Dict[str, Tuple[int, int]], charges: List[Tuple[int, str, int]]) -> Dict:
    remaining_caps = {bucket: allowance for bucket, (allowance, _) in caps.items()}
    refused = []

    for charge_id, bucket, amount in charges:
        if not isinstance(amount, int) or amount <= 0:
            raise ValueError("Charge amount must be a positive whole number.")
        
        if bucket not in remaining_caps:
            refused.append((charge_id, "unknown"))
            continue
        
        max_charge = caps[bucket][1]
        if amount > max_charge:
            refused.append((charge_id, "single"))
            continue
        
        if amount > remaining_caps[bucket]:
            refused.append((charge_id, "cap"))
            continue
        
        remaining_caps[bucket] -= amount

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