class CryptoVaultError extends Error {
  constructor(message, code, retryable = false, userMessage = null) {
    super(message);
    this.name = 'CryptoVaultError';
    this.code = code;
    this.retryable = retryable;
    this.userMessage = userMessage || message;
    this.timestamp = Date.now();
  }
}

const ERROR_CODES = {
  NETWORK_ERROR: 'NETWORK_ERROR',
  TIMEOUT_ERROR: 'TIMEOUT_ERROR',
  SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',
  INVALID_PASSWORD: 'INVALID_PASSWORD',
  SESSION_EXPIRED: 'SESSION_EXPIRED',
  UNAUTHORIZED: 'UNAUTHORIZED',
  CRYPTO_ERROR: 'CRYPTO_ERROR',
  INVALID_SIGNATURE: 'INVALID_SIGNATURE',
  ENCRYPTION_FAILED: 'ENCRYPTION_FAILED',
  INVALID_INPUT: 'INVALID_INPUT',
  MISSING_KEYPAIR: 'MISSING_KEYPAIR',
  INVALID_FORMAT: 'INVALID_FORMAT',
  WASM_ERROR: 'WASM_ERROR',
  STORAGE_ERROR: 'STORAGE_ERROR',
  UNKNOWN_ERROR: 'UNKNOWN_ERROR'
};

const ERROR_MESSAGES = {
  [ERROR_CODES.NETWORK_ERROR]: 'Connection failed. Please check your internet connection and try again.',
  [ERROR_CODES.TIMEOUT_ERROR]: 'Operation timed out. Please try again.',
  [ERROR_CODES.SERVICE_UNAVAILABLE]: 'Service is temporarily unavailable. Please try again later.',

  [ERROR_CODES.INVALID_PASSWORD]: 'Invalid password. Please check your password and try again.',
  [ERROR_CODES.SESSION_EXPIRED]: 'Your session has expired. Please log in again.',
  [ERROR_CODES.UNAUTHORIZED]: 'You are not authorized to perform this action.',

  [ERROR_CODES.CRYPTO_ERROR]: 'Cryptographic operation failed. Please try again.',
  [ERROR_CODES.INVALID_SIGNATURE]: 'Invalid signature. Please verify your input.',
  [ERROR_CODES.ENCRYPTION_FAILED]: 'Encryption failed. Please try again.',

  [ERROR_CODES.INVALID_INPUT]: 'Invalid input. Please check your data and try again.',
  [ERROR_CODES.MISSING_KEYPAIR]: 'No keypair selected. Please select a keypair first.',
  [ERROR_CODES.INVALID_FORMAT]: 'Invalid data format. Please check your input.',

  [ERROR_CODES.WASM_ERROR]: 'System error occurred. Please refresh and try again.',
  [ERROR_CODES.STORAGE_ERROR]: 'Storage error occurred. Please try again.',
  [ERROR_CODES.UNKNOWN_ERROR]: 'An unexpected error occurred. Please try again.'
};

const RETRYABLE_ERRORS = new Set([
  ERROR_CODES.NETWORK_ERROR,
  ERROR_CODES.TIMEOUT_ERROR,
  ERROR_CODES.SERVICE_UNAVAILABLE,
  ERROR_CODES.WASM_ERROR,
  ERROR_CODES.STORAGE_ERROR
]);

function classifyError(error) {
  const errorMessage = getErrorMessage(error);

  if (errorMessage.includes('fetch') || errorMessage.includes('network') || errorMessage.includes('connection')) {
    return new CryptoVaultError(
      errorMessage,
      ERROR_CODES.NETWORK_ERROR,
      true,
      ERROR_MESSAGES[ERROR_CODES.NETWORK_ERROR]
    );
  }

  if (errorMessage.includes('password') || errorMessage.includes('Invalid password')) {
    return new CryptoVaultError(
      errorMessage,
      ERROR_CODES.INVALID_PASSWORD,
      false,
      ERROR_MESSAGES[ERROR_CODES.INVALID_PASSWORD]
    );
  }

  if (errorMessage.includes('session') || errorMessage.includes('not unlocked') || errorMessage.includes('expired')) {
    return new CryptoVaultError(
      errorMessage,
      ERROR_CODES.SESSION_EXPIRED,
      false,
      ERROR_MESSAGES[ERROR_CODES.SESSION_EXPIRED]
    );
  }

  if (errorMessage.includes('decryption error') || errorMessage.includes('aead::Error')) {
    return new CryptoVaultError(
      errorMessage,
      ERROR_CODES.CRYPTO_ERROR,
      false,
      'Invalid password or corrupted data. Please check your password.'
    );
  }

  if (errorMessage.includes('Crypto error') || errorMessage.includes('encryption')) {
    return new CryptoVaultError(
      errorMessage,
      ERROR_CODES.CRYPTO_ERROR,
      false,
      ERROR_MESSAGES[ERROR_CODES.CRYPTO_ERROR]
    );
  }

  if (errorMessage.includes('No keypair selected')) {
    return new CryptoVaultError(
      errorMessage,
      ERROR_CODES.MISSING_KEYPAIR,
      false,
      ERROR_MESSAGES[ERROR_CODES.MISSING_KEYPAIR]
    );
  }

  if (errorMessage.includes('wasm') || errorMessage.includes('WASM')) {
    return new CryptoVaultError(
      errorMessage,
      ERROR_CODES.WASM_ERROR,
      true,
      ERROR_MESSAGES[ERROR_CODES.WASM_ERROR]
    );
  }

  return new CryptoVaultError(
    errorMessage,
    ERROR_CODES.UNKNOWN_ERROR,
    false,
    ERROR_MESSAGES[ERROR_CODES.UNKNOWN_ERROR]
  );
}

function getErrorMessage(error) {
  if (!error) return 'Unknown error';

  if (typeof error === 'string') {
    return error.trim();
  }

  if (error instanceof Error) {
    return error.message;
  }

  if (error.error) {
    return getErrorMessage(error.error);
  }

  if (error.message) {
    return error.message;
  }

  if (typeof error === 'object') {
    try {
      const stringified = JSON.stringify(error);
      if (stringified && stringified !== '{}') {
        return stringified;
      }
    } catch (e) {
      // Silently handle JSON stringify errors
    }
  }

  return 'Unknown error';
}

async function withRetry(operation, options = {}) {
  const {
    maxRetries = 3,
    baseDelay = 1000,
    maxDelay = 10000,
    backoffFactor = 2,
    onRetry = null
  } = options;

  let lastError;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await operation();
    } catch (error) {
      const classifiedError = classifyError(error);
      lastError = classifiedError;

      if (attempt === maxRetries || !classifiedError.retryable) {
        throw classifiedError;
      }

      const delay = Math.min(baseDelay * Math.pow(backoffFactor, attempt), maxDelay);

      if (onRetry) {
        onRetry(attempt + 1, delay, classifiedError);
      }

      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

async function executeOperation(operation, options = {}) {
  const {
    loadingElement = null,
    successMessage = null,
    showRetryProgress = false,
    onProgress = null
  } = options;

  if (loadingElement) {
    setButtonLoading(loadingElement, true);
  }

  try {
    const result = await withRetry(operation, {
      ...options,
      onRetry: (attempt, delay, error) => {
        if (showRetryProgress && onProgress) {
          onProgress(`Retrying... (${attempt}/${options.maxRetries || 3})`);
        }
        if (options.onRetry) {
          options.onRetry(attempt, delay, error);
        }
      }
    });

    if (successMessage) {
      showToast(successMessage, 'success');
    }

    return result;
  } catch (error) {
    showToast(error.userMessage || error.message, 'error');
    throw error;
  } finally {
    if (loadingElement) {
      setButtonLoading(loadingElement, false);
    }
  }
}

window.CryptoVaultError = CryptoVaultError;
window.ERROR_CODES = ERROR_CODES;
window.classifyError = classifyError;
window.getErrorMessage = getErrorMessage;
window.withRetry = withRetry;
window.executeOperation = executeOperation;
