import chalk from 'chalk';

export class ApiError extends Error {
    constructor(
        public statusCode: number,
        message: string
    ) {
        super(message);
        this.name = 'ApiError';
    }
}

export class RetryableError extends Error {
    constructor(message: string) {
        super(message);
        this.name = 'RetryableError';
    }
}

export class JsonSageError extends Error {
    constructor(
        message: string,
        public code: string,
        public details?: any
    ) {
        super(message);
        this.name = 'JsonSageError';
    }
}

export function safeJsonParse(data: string): { success: boolean; data?: any; error?: Error } {
    try {
        const parsed = JSON.parse(data);
        return { success: true, data: parsed };
    } catch (error) {
        return {
            success: false,
            error: new JsonSageError(
                'Error parsing JSON data. Please ensure the input is valid JSON.',
                'INVALID_JSON',
                error
            )
        };
    }
}

export async function safeExecute<T>(
    fn: () => Promise<T> | T,
    errorMessage: string = 'An unexpected error occurred'
): Promise<{ success: boolean; data?: T; error?: Error }> {
    try {
        const result = await fn();
        return { success: true, data: result };
    } catch (error) {
        return {
            success: false,
            error: new JsonSageError(
                errorMessage,
                'EXECUTION_ERROR',
                error
            )
        };
    }
}

export async function retryWithBackoff<T>(
    operation: () => Promise<T>,
    options: {
        maxRetries?: number;
        initialDelay?: number;
        maxDelay?: number;
        shouldRetry?: (error: any) => boolean;
    } = {}
): Promise<T> {
    const {
        maxRetries = 3,
        initialDelay = 1000,
        maxDelay = 10000,
        shouldRetry = (error) => {
            if (error instanceof ApiError) {
                return [502, 503, 504].includes(error.statusCode);
            }
            return error instanceof RetryableError;
        }
    } = options;

    let lastError: Error;
    let delay = initialDelay;

    for (let attempt = 1; attempt <= maxRetries; attempt++) {
        try {
            return await operation();
        } catch (error) {
            lastError = error;

            if (!shouldRetry(error) || attempt === maxRetries) {
                break;
            }

            console.log(chalk.yellow(`Attempt ${attempt} failed, retrying in ${delay}ms...`));
            await new Promise(resolve => setTimeout(resolve, delay));
            delay = Math.min(delay * 2, maxDelay);
        }
    }

    throw lastError;
}

export function handleApiError(error: any): never {
    if (error instanceof ApiError) {
        throw new Error(`API Error (${error.statusCode}): ${error.message}`);
    } else if (error instanceof RetryableError) {
        throw new Error(`Retryable Error: ${error.message}`);
    } else if (error instanceof JsonSageError) {
        throw new Error(`JSON Sage Error (${error.code}): ${error.message}`);
    } else if (error instanceof Error) {
        throw new Error(`Unexpected Error: ${error.message}`);
    } else {
        throw new Error('An unknown error occurred');
    }
}
