/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import { parseGoogleApiError } from './googleErrors.js';
function isGaxiosError(error) {
    return (typeof error === 'object' &&
        error !== null &&
        'response' in error &&
        typeof error.response === 'object' &&
        error.response !== null);
}
export function isNodeError(error) {
    return error instanceof Error && 'code' in error;
}
/**
 * Checks if an error is an AbortError.
 */
export function isAbortError(error) {
    return error instanceof Error && error.name === 'AbortError';
}
export function getErrorMessage(error) {
    const friendlyError = toFriendlyError(error);
    if (friendlyError instanceof Error) {
        return friendlyError.message;
    }
    if (typeof friendlyError === 'object' &&
        friendlyError !== null &&
        'message' in friendlyError &&
        typeof friendlyError.message === 'string') {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
        return friendlyError.message;
    }
    try {
        return String(friendlyError);
    }
    catch {
        return 'Failed to get error details';
    }
}
export function getErrorType(error) {
    if (!(error instanceof Error))
        return 'unknown';
    // Use the constructor name if the standard error name is missing or generic.
    const name = error.name && error.name !== 'Error'
        ? error.name
        : (error.constructor?.name ?? 'Error');
    // Strip leading underscore from error names. Bundlers like esbuild sometimes
    // rename classes to avoid scope collisions.
    return name.replace(/^_+/, '');
}
export class FatalError extends Error {
    exitCode;
    constructor(message, exitCode) {
        super(message);
        this.exitCode = exitCode;
        this.name = 'FatalError';
    }
}
export class FatalAuthenticationError extends FatalError {
    constructor(message) {
        super(message, 41);
        this.name = 'FatalAuthenticationError';
    }
}
export class FatalInputError extends FatalError {
    constructor(message) {
        super(message, 42);
        this.name = 'FatalInputError';
    }
}
export class FatalSandboxError extends FatalError {
    constructor(message) {
        super(message, 44);
        this.name = 'FatalSandboxError';
    }
}
export class FatalConfigError extends FatalError {
    constructor(message) {
        super(message, 52);
        this.name = 'FatalConfigError';
    }
}
export class FatalTurnLimitedError extends FatalError {
    constructor(message) {
        super(message, 53);
        this.name = 'FatalTurnLimitedError';
    }
}
export class FatalToolExecutionError extends FatalError {
    constructor(message) {
        super(message, 54);
        this.name = 'FatalToolExecutionError';
    }
}
export class FatalUntrustedWorkspaceError extends FatalError {
    constructor(message) {
        super(message, 55);
        this.name = 'FatalUntrustedWorkspaceError';
    }
}
export class FatalCancellationError extends FatalError {
    constructor(message) {
        super(message, 130); // Standard exit code for SIGINT
        this.name = 'FatalCancellationError';
    }
}
export class CanceledError extends Error {
    constructor(message = 'The operation was canceled.') {
        super(message);
        this.name = 'CanceledError';
    }
}
export class ForbiddenError extends Error {
    constructor(message) {
        super(message);
        this.name = 'ForbiddenError';
    }
}
export class AccountSuspendedError extends ForbiddenError {
    appealUrl;
    appealLinkText;
    constructor(message, metadata) {
        super(message);
        this.name = 'AccountSuspendedError';
        this.appealUrl = metadata?.['appeal_url'];
        this.appealLinkText = metadata?.['appeal_url_link_text'];
    }
}
export class UnauthorizedError extends Error {
    constructor(message) {
        super(message);
        this.name = 'UnauthorizedError';
    }
}
export class BadRequestError extends Error {
    constructor(message) {
        super(message);
        this.name = 'BadRequestError';
    }
}
export class ChangeAuthRequestedError extends Error {
    constructor() {
        super('User requested to change authentication method');
        this.name = 'ChangeAuthRequestedError';
    }
}
function isResponseData(data) {
    if (typeof data !== 'object' || data === null) {
        return false;
    }
    const candidate = data;
    if (!('error' in candidate)) {
        return false;
    }
    const error = candidate.error;
    if (typeof error !== 'object' || error === null) {
        return false; // error property exists but is not an object (could be undefined, but we checked 'in')
    }
    // Optional properties check
    if ('code' in error &&
        typeof error.code !== 'number' &&
        error.code !== undefined) {
        return false;
    }
    if ('message' in error &&
        typeof error.message !== 'string' &&
        error.message !== undefined) {
        return false;
    }
    return true;
}
export function toFriendlyError(error) {
    // First, try structured parsing for TOS_VIOLATION detection.
    const googleApiError = parseGoogleApiError(error);
    if (googleApiError && googleApiError.code === 403) {
        const tosDetail = googleApiError.details.find((d) => d['@type'] === 'type.googleapis.com/google.rpc.ErrorInfo' &&
            'reason' in d &&
            d.reason === 'TOS_VIOLATION');
        if (tosDetail) {
            return new AccountSuspendedError(googleApiError.message, tosDetail.metadata);
        }
    }
    // Fall back to basic Gaxios error parsing for other HTTP errors.
    if (isGaxiosError(error)) {
        const data = parseResponseData(error);
        if (data && data.error && data.error.message && data.error.code) {
            switch (data.error.code) {
                case 400:
                    return new BadRequestError(data.error.message);
                case 401:
                    return new UnauthorizedError(data.error.message);
                case 403:
                    return new ForbiddenError(data.error.message);
                default:
            }
        }
    }
    return error;
}
export function isAccountSuspendedError(error) {
    const friendly = toFriendlyError(error);
    return friendly instanceof AccountSuspendedError ? friendly : null;
}
function parseResponseData(error) {
    let data = error.response?.data;
    // Inexplicably, Gaxios sometimes doesn't JSONify the response data.
    if (typeof data === 'string') {
        try {
            data = JSON.parse(data);
        }
        catch {
            return undefined;
        }
    }
    if (isResponseData(data)) {
        return data;
    }
    return undefined;
}
/**
 * Checks if an error is a 401 authentication error.
 * Uses structured error properties from MCP SDK errors.
 *
 * @param error The error to check
 * @returns true if this is a 401/authentication error
 */
export function isAuthenticationError(error) {
    // Check for MCP SDK errors with code property
    // (SseError and StreamableHTTPError both have numeric 'code' property)
    if (error &&
        typeof error === 'object' &&
        'code' in error &&
        typeof error.code === 'number') {
        // Safe access after check
        // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
        const errorCode = error.code;
        if (errorCode === 401) {
            return true;
        }
    }
    // Check for UnauthorizedError class (from MCP SDK or our own)
    if (error instanceof Error && error.name === 'UnauthorizedError') {
        return true;
    }
    if (error instanceof UnauthorizedError) {
        return true;
    }
    // Fallback: Check for MCP SDK's plain Error messages with HTTP 401
    // The SDK sometimes throws: new Error(`Error POSTing to endpoint (HTTP 401): ...`)
    const message = getErrorMessage(error);
    if (message.includes('401')) {
        return true;
    }
    return false;
}
//# sourceMappingURL=errors.js.map