/**
 * Base class for all Zapier SDK errors
 * Use instanceof ZapierError to check if an error is from the SDK
 */
export class ZapierError extends Error {
    constructor(message, options = {}) {
        super(message);
        this.statusCode = options.statusCode;
        this.errors = options.errors;
        this.cause = options.cause;
        this.response = options.response;
        // Maintain proper prototype chain for instanceof checks
        Object.setPrototypeOf(this, new.target.prototype);
    }
}
/**
 * Error thrown when API requests fail
 */
export class ZapierApiError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierApiError";
        this.code = "ZAPIER_API_ERROR";
    }
}
/**
 * Error thrown when an app is not found
 */
export class ZapierAppNotFoundError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierAppNotFoundError";
        this.code = "ZAPIER_APP_NOT_FOUND_ERROR";
        this.appKey = options.appKey;
    }
}
/**
 * Error thrown when validation fails (e.g., invalid parameters)
 */
export class ZapierValidationError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierValidationError";
        this.code = "ZAPIER_VALIDATION_ERROR";
        this.details = options.details;
    }
}
/**
 * Error for unrecognized/unknown errors that get wrapped by safe functions
 */
export class ZapierUnknownError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierUnknownError";
        this.code = "ZAPIER_UNKNOWN_ERROR";
    }
}
/**
 * Error thrown for authentication and authorization failures (401/403)
 */
export class ZapierAuthenticationError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierAuthenticationError";
        this.code = "ZAPIER_AUTHENTICATION_ERROR";
    }
}
/**
 * Error thrown when a resource is not found (404)
 */
export class ZapierNotFoundError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierNotFoundError";
        this.code = "ZAPIER_NOT_FOUND_ERROR";
    }
}
/**
 * Error thrown when a requested resource is not found and the caller
 * provided a `resource` hint on the request, populating `resourceType`
 * and (optionally) `resourceId`. Subclass of `ZapierNotFoundError` so
 * `instanceof ZapierNotFoundError` checks continue to work.
 */
export class ZapierResourceNotFoundError extends ZapierNotFoundError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierResourceNotFoundError";
        this.code = "ZAPIER_RESOURCE_NOT_FOUND_ERROR";
        this.resourceType = options.resourceType;
        this.resourceId = options.resourceId;
    }
}
/**
 * Error thrown when required app or implementation configuration is missing
 */
export class ZapierConfigurationError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierConfigurationError";
        this.code = "ZAPIER_CONFIGURATION_ERROR";
        this.configType = options.configType;
    }
}
/**
 * Error thrown when code bundling or compilation fails
 */
export class ZapierBundleError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierBundleError";
        this.code = "ZAPIER_BUNDLE_ERROR";
        this.buildErrors = options.buildErrors;
    }
}
/**
 * Error thrown when operations timeout or exceed retry limits
 */
export class ZapierTimeoutError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierTimeoutError";
        this.code = "ZAPIER_TIMEOUT_ERROR";
        this.attempts = options.attempts;
        this.maxAttempts = options.maxAttempts;
    }
}
/**
 * Error thrown when action execution fails due to errors returned from the third-party service
 * This happens when the Actions API returns a 200 status but includes errors in the response
 */
export class ZapierActionError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierActionError";
        this.code = "ZAPIER_ACTION_ERROR";
        this.appKey = options.appKey;
        this.actionKey = options.actionKey;
    }
    // Legacy accessor for backward compatibility
    get actionErrors() {
        return this.errors;
    }
}
/**
 * Error thrown when an operation conflicts with existing state (409). Common
 * cases: creating a named resource where a different one already exists with
 * the same name, or operating on a resource in an incompatible state.
 */
export class ZapierConflictError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierConflictError";
        this.code = "ZAPIER_CONFLICT_ERROR";
        this.resourceType = options.resourceType;
    }
}
/**
 * Error thrown when rate limited (429) after all retries are exhausted
 */
export class ZapierRateLimitError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierRateLimitError";
        this.code = "ZAPIER_RATE_LIMIT_ERROR";
        this.rateLimit = options.rateLimit ?? {};
        this.retries = options.retries ?? 0;
    }
}
/**
 * Error thrown when a request requires approval and the approval is denied, times out,
 * or the auth type doesn't support the approval flow.
 */
export class ZapierApprovalError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierApprovalError";
        this.code = "ZAPIER_APPROVAL_ERROR";
        this.approvalId = options.approvalId;
        this.approvalStatus = options.status;
        this.approvalUrl = options.approvalUrl;
        this.pollUrl = options.pollUrl;
    }
}
/**
 * Error thrown when Relay itself encounters an error (as opposed to the upstream partner API returning an error).
 * Indicated by the presence of the X-Relay-Error response header.
 */
export class ZapierRelayError extends ZapierError {
    constructor(message, options = {}) {
        super(message, options);
        this.name = "ZapierRelayError";
        this.code = "ZAPIER_RELAY_ERROR";
    }
}
/**
 * Utility function to format error messages for display
 */
export function formatErrorMessage(error) {
    let message = error.message;
    // Add additional context from errors array if available
    if (error.errors && error.errors.length > 0) {
        const additionalErrors = error.errors.slice(1); // Skip first error (already in message)
        if (additionalErrors.length > 0) {
            const errorDetails = additionalErrors
                .map((err) => `  • ${err.detail || err.title || err.code}`)
                .join("\n");
            message += "\n\nAdditional errors:\n" + errorDetails;
        }
    }
    // Add error-specific context
    if (error instanceof ZapierAppNotFoundError && error.appKey) {
        message += `\nApp key: ${error.appKey}`;
    }
    if (error instanceof ZapierActionError && (error.appKey || error.actionKey)) {
        const context = [];
        if (error.appKey)
            context.push(`App: ${error.appKey}`);
        if (error.actionKey)
            context.push(`Action: ${error.actionKey}`);
        message += `\n${context.join(", ")}`;
    }
    if (error instanceof ZapierResourceNotFoundError &&
        (error.resourceType || error.resourceId)) {
        const context = [];
        if (error.resourceType)
            context.push(`Type: ${error.resourceType}`);
        if (error.resourceId)
            context.push(`ID: ${error.resourceId}`);
        message += `\n${context.join(", ")}`;
    }
    if (error instanceof ZapierConflictError && error.resourceType) {
        message += `\nType: ${error.resourceType}`;
    }
    if (error instanceof ZapierTimeoutError &&
        (error.attempts || error.maxAttempts)) {
        const context = [];
        if (error.attempts)
            context.push(`Attempts: ${error.attempts}`);
        if (error.maxAttempts)
            context.push(`Max attempts: ${error.maxAttempts}`);
        message += `\n${context.join(", ")}`;
    }
    if (error instanceof ZapierBundleError &&
        error.buildErrors &&
        error.buildErrors.length > 0) {
        message +=
            "\n\nBuild errors:\n" +
                error.buildErrors.map((err) => `  • ${err}`).join("\n");
    }
    if (error instanceof ZapierApprovalError && error.approvalUrl) {
        message += `\nApproval URL: ${error.approvalUrl}`;
    }
    if (error instanceof ZapierRateLimitError) {
        const { limit, remaining, retryAfterMs } = error.rateLimit;
        const parts = [];
        if (limit !== undefined) {
            const used = remaining !== undefined ? limit - remaining : limit;
            parts.push(`${used}/${limit} used`);
        }
        if (retryAfterMs !== undefined && retryAfterMs > 0) {
            parts.push(`retry in ${formatDuration(retryAfterMs)}`);
        }
        if (error.retries > 0) {
            parts.push(`after ${error.retries} retr${error.retries === 1 ? "y" : "ies"}`);
        }
        if (parts.length > 0) {
            message += `\n${parts.join(", ")}`;
        }
    }
    // Add status code if available and not already obvious from the message
    if (error.statusCode && !message.includes(`${error.statusCode}`)) {
        message += `\nHTTP Status: ${error.statusCode}`;
    }
    return message;
}
function formatDuration(ms) {
    const seconds = Math.round(ms / 1000);
    if (seconds < 60)
        return `${seconds}s`;
    const minutes = Math.round(seconds / 60);
    if (minutes < 60)
        return `${minutes}m`;
    const hours = Math.round(minutes / 60);
    if (hours < 24)
        return `${hours}h`;
    const days = Math.round(hours / 24);
    return `${days}d`;
}
