/**
 * Error hierarchy for the Amigo Platform SDK.
 * All errors extend AmigoError which can be caught with a single catch.
 */
const SENSITIVE_FIELDS = new Set([
    'id_token',
    'access_token',
    'refresh_token',
    'authorization',
    'api_key',
    'apikey',
    'token',
    'secret',
    'password',
    'x-api-key',
    'cookie',
    'set-cookie',
]);
function sanitizeErrorContext(obj) {
    if (typeof obj !== 'object' || !obj)
        return obj;
    if (Array.isArray(obj))
        return obj.map(sanitizeErrorContext);
    const result = {};
    for (const [key, value] of Object.entries(obj)) {
        if (SENSITIVE_FIELDS.has(key.toLowerCase())) {
            result[key] = '[REDACTED]';
        }
        else if (typeof value === 'object' && value !== null) {
            result[key] = sanitizeErrorContext(value);
        }
        else {
            result[key] = value;
        }
    }
    return result;
}
/** Base class for all Amigo Platform SDK errors */
export class AmigoError extends Error {
    statusCode;
    errorCode;
    requestId;
    detail;
    context;
    constructor(message, ctx = {}) {
        super(message);
        this.name = this.constructor.name;
        this.statusCode = ctx.statusCode;
        this.errorCode = ctx.errorCode;
        this.requestId = ctx.requestId;
        this.detail = ctx.detail;
        this.context = ctx.context
            ? sanitizeErrorContext(ctx.context)
            : undefined;
        Object.setPrototypeOf(this, new.target.prototype);
        if (typeof Error.captureStackTrace === 'function') {
            Error.captureStackTrace(this, this.constructor);
        }
    }
    toJSON() {
        return {
            name: this.name,
            message: this.message,
            statusCode: this.statusCode,
            errorCode: this.errorCode,
            requestId: this.requestId,
            detail: this.detail,
            context: this.context,
            stack: this.stack,
        };
    }
}
/** 400 Bad Request */
export class BadRequestError extends AmigoError {
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: 400 });
    }
}
/** 401 Unauthorized — invalid or missing API key */
export class AuthenticationError extends AmigoError {
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: 401 });
    }
}
/** 403 Forbidden — insufficient permissions */
export class PermissionError extends AmigoError {
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: 403 });
    }
}
/** 404 Not Found */
export class NotFoundError extends AmigoError {
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: 404 });
    }
}
/** 409 Conflict — duplicate slug or resource version conflict */
export class ConflictError extends AmigoError {
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: 409 });
    }
}
/** 422 Unprocessable Entity — validation failure */
export class ValidationError extends AmigoError {
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: 422 });
    }
}
/** 429 Too Many Requests */
export class RateLimitError extends AmigoError {
    retryAfter;
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: 429 });
        this.retryAfter = ctx.retryAfter;
    }
}
/** 5xx Server Error */
export class ServerError extends AmigoError {
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: ctx.statusCode ?? 500 });
    }
}
/** 503 Service Unavailable */
export class ServiceUnavailableError extends ServerError {
    constructor(message, ctx = {}) {
        super(message, { ...ctx, statusCode: 503 });
    }
}
/** Network or fetch failure (no HTTP status available) */
export class NetworkError extends AmigoError {
    constructor(message, cause) {
        super(message);
        if (cause !== undefined) {
            Object.defineProperty(this, 'cause', { value: cause, writable: false, enumerable: true });
        }
    }
}
/** Request timed out before receiving a response */
export class RequestTimeoutError extends NetworkError {
    timeoutMs;
    constructor(message, timeoutMs, cause) {
        super(message, cause);
        this.timeoutMs = timeoutMs;
    }
}
/** Failed to parse response body */
export class ParseError extends AmigoError {
    body;
    constructor(message, body) {
        super(message);
        this.body = body;
    }
}
/** SDK misconfiguration */
export class ConfigurationError extends AmigoError {
    constructor(message) {
        super(message);
    }
}
export async function createApiError(response) {
    let body = {};
    try {
        const rawBody = await response.text();
        body = JSON.parse(rawBody);
    }
    catch {
        // ignore parse failure
    }
    const ctx = {
        statusCode: response.status,
        errorCode: body.error_code,
        requestId: body.request_id ?? response.headers.get('x-request-id') ?? undefined,
        detail: body.detail,
        context: { url: response.url, response: body },
    };
    const message = body.message ?? body.detail ?? response.statusText ?? `HTTP ${response.status}`;
    switch (response.status) {
        case 400:
            return new BadRequestError(message, ctx);
        case 401:
            return new AuthenticationError(message, ctx);
        case 403:
            return new PermissionError(message, ctx);
        case 404:
            return new NotFoundError(message, ctx);
        case 409:
            return new ConflictError(message, ctx);
        case 422:
            return new ValidationError(message, ctx);
        case 429: {
            const retryAfter = parseRetryAfter(response);
            return new RateLimitError(message, { ...ctx, retryAfter });
        }
        case 503:
            return new ServiceUnavailableError(message, ctx);
        default:
            return new ServerError(message, ctx);
    }
}
function parseRetryAfter(response) {
    const header = response.headers.get('Retry-After');
    if (!header)
        return undefined;
    const seconds = Number(header);
    if (!isNaN(seconds))
        return seconds;
    const date = new Date(header);
    if (!isNaN(date.getTime())) {
        return Math.max(0, Math.ceil((date.getTime() - Date.now()) / 1000));
    }
    return undefined;
}
// --- Type guards ---
export function isAmigoError(err) {
    return err instanceof AmigoError;
}
export function isNotFoundError(err) {
    return err instanceof NotFoundError;
}
export function isRateLimitError(err) {
    return err instanceof RateLimitError;
}
export function isAuthenticationError(err) {
    return err instanceof AuthenticationError;
}
export function isRequestTimeoutError(err) {
    return err instanceof RequestTimeoutError;
}
//# sourceMappingURL=errors.js.map