```ts
export function applyCharges(caps: Record<string, [number, number]>, charges: [string, string, number][]): { left: Record<string, number>; refused: [string, string][] } {
  const result: { left: Record<string, number>; refused: [string, string][] } = {
    left: {},
    refused: []
  };

  for (const [id, bucket, amount] of charges) {
    if (typeof amount !== 'number' || amount <= 0 || !Number.isInteger(amount)) {
      throw new Error('Invalid charge amount');
    }

    const cap = caps[bucket];
    if (!cap) {
      result.refused.push([id, 'unknown']);
      continue;
    }

    const [allowance, maxSingleCharge] = cap;
    if (amount > maxSingleCharge) {
      result.refused.push([id, 'single']);
      continue;
    }
    if (amount > allowance) {
      result.refused.push([id, 'cap']);
      continue;
    }

    result.left[bucket] = (result.left[bucket] || 0) + amount;
  }

  return result;
}
```