```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 charge of charges) {
        const [chargeId, bucketName, amount] = charge;

        if (!Number.isInteger(amount) || amount <= 0) {
            throw new Error('Invalid charge amount');
        }

        if (!caps.hasOwnProperty(bucketName)) {
            result.refused.push([chargeId, 'unknown']);
            continue;
        }

        const [totalAllowance, maxSingleCharge] = caps[bucketName];
        if (amount > maxSingleCharge) {
            result.refused.push([chargeId, 'single']);
            continue;
        }

        if (result.left[bucketName] !== undefined && amount > result.left[bucketName]) {
            result.refused.push([chargeId, 'cap']);
            continue;
        }

        if (!result.left[bucketName]) {
            result.left[bucketName] = totalAllowance - amount;
        } else {
            result.left[bucketName] -= amount;
        }
    }

    return result;
}
```