import { retry, timeout, RetryExhaustedError, TimeoutError, } from '@git-stunts/alfred';
import OperationPolicyExhaustedError from "../../domain/errors/OperationPolicyExhaustedError.js";
import OperationPolicyTimeoutError from "../../domain/errors/OperationPolicyTimeoutError.js";
import OperationPolicyPort from "../../ports/OperationPolicyPort.js";
export default class AlfredOperationPolicyAdapter extends OperationPolicyPort {
    _retryOptions;
    constructor(options = {}) {
        super();
        this._retryOptions = options.retryOptions ?? {};
    }
    async execute(operation, options = {}) {
        const resolvedOptions = { ...this._retryOptions, ...options };
        return await this._mapErrors(async () => {
            if (resolvedOptions.retries !== undefined) {
                return await retry((retrySignal) => this._executeAttempt(operation, resolvedOptions, retrySignal), resolvedOptions);
            }
            return await this._executeAttempt(operation, resolvedOptions, resolvedOptions.signal);
        });
    }
    async stream(operation, options = {}) {
        return await this.execute(operation, options);
    }
    async _executeAttempt(operation, options, retrySignal) {
        if (options.timeoutMs !== undefined) {
            return await timeout(options.timeoutMs, async (timeoutSignal) => await operation(combineAbortSignals(retrySignal, timeoutSignal)));
        }
        return await operation(retrySignal);
    }
    async _mapErrors(operation) {
        try {
            return await operation();
        }
        catch (err) {
            if (err instanceof RetryExhaustedError) {
                throw new OperationPolicyExhaustedError(err.attempts, err.cause);
            }
            if (err instanceof TimeoutError) {
                throw new OperationPolicyTimeoutError(err.timeout, err.elapsed);
            }
            throw err;
        }
    }
}
function combineAbortSignals(first, second) {
    if (first === undefined) {
        return second;
    }
    if (second === undefined) {
        return first;
    }
    return AbortSignal.any([first, second]);
}
