/**
 * Resource timeout error class
 */
export class ResourceTimeoutError extends Error {
    constructor(resourceType, resourceId, timeoutMs, operation) {
        super(`${resourceType} ${resourceId} did not complete ${operation} within ${timeoutMs}ms`);
        this.name = "ResourceTimeoutError";
    }
}
/**
 * Validate timeout configuration
 */
export function validateTimeoutConfig(config) {
    if (config.maxAttempts <= 0) {
        throw new Error("maxAttempts must be greater than 0");
    }
    if (config.delayMs <= 0) {
        throw new Error("delayMs must be greater than 0");
    }
}
/**
 * Merge default timeout configuration with optional overrides
 */
export function mergeTimeoutConfig(defaultConfig, override) {
    if (!override) {
        return defaultConfig;
    }
    const merged = {
        maxAttempts: override.maxAttempts ?? defaultConfig.maxAttempts,
        delayMs: override.delayMs ?? defaultConfig.delayMs,
    };
    validateTimeoutConfig(merged);
    return merged;
}
/**
 * Wait for a resource to reach a desired state with configurable timeout
 */
export async function waitForResourceState(checkFunction, isReady, config, resourceId, resourceType, operation = "become ready") {
    validateTimeoutConfig(config);
    const { maxAttempts, delayMs } = config;
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
        try {
            const result = await checkFunction();
            if (isReady(result)) {
                return;
            }
            console.log(`${resourceType} ${resourceId} not ready, waiting... (attempt ${attempt}/${maxAttempts})`);
            if (attempt < maxAttempts) {
                await new Promise((resolve) => setTimeout(resolve, delayMs));
            }
        }
        catch (error) {
            console.log(`Error checking ${resourceType} status (attempt ${attempt}/${maxAttempts}):`, error);
            // Check if this is a non-retryable error (contains "NonRetryableError" in the name)
            if (error instanceof Error && error.name.includes("NonRetryableError")) {
                throw error;
            }
            if (attempt === maxAttempts) {
                throw error;
            }
            await new Promise((resolve) => setTimeout(resolve, delayMs));
        }
    }
    const totalTimeMs = maxAttempts * delayMs;
    throw new ResourceTimeoutError(resourceType, resourceId, totalTimeMs, operation);
}
/**
 * Generic function to wait for an operation to complete
 * This is the most flexible version that can handle any type of operation
 */
export async function waitForOperation(checkFunction, isComplete, config, resourceId, resourceType, operation = "complete", onError) {
    validateTimeoutConfig(config);
    const { maxAttempts, delayMs } = config;
    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
        try {
            const result = await checkFunction();
            if (isComplete(result)) {
                console.log(`${resourceType} ${resourceId} ${operation} completed`);
                return;
            }
            console.log(`${resourceType} ${resourceId} ${operation} in progress... (attempt ${attempt}/${maxAttempts})`);
            if (attempt < maxAttempts) {
                await new Promise((resolve) => setTimeout(resolve, delayMs));
            }
        }
        catch (error) {
            // Allow custom error handling
            if (onError) {
                const shouldContinue = onError(error, attempt, maxAttempts);
                if (!shouldContinue) {
                    // If onError returns false, it means the operation completed successfully
                    // (e.g., resource was deleted and not found)
                    console.log(`${resourceType} ${resourceId} ${operation} completed (via error handling)`);
                    return;
                }
            }
            else {
                console.log(`Error checking ${resourceType} ${operation} status (attempt ${attempt}/${maxAttempts}):`, error);
            }
            if (attempt === maxAttempts) {
                throw error;
            }
            await new Promise((resolve) => setTimeout(resolve, delayMs));
        }
    }
    const totalTimeMs = maxAttempts * delayMs;
    throw new ResourceTimeoutError(resourceType, resourceId, totalTimeMs, operation);
}
//# sourceMappingURL=timeout.js.map