/**
 * Modern error handling utilities leveraging ES2024 features
 */

/**
 * Type guard to check if an error has a message property
 * Uses ES2024 Object.hasOwn for better property checking
 */
export function isErrorWithMessage(error: unknown): error is Error {
  return (
    typeof error === "object" &&
    error !== null &&
    Object.hasOwn(error, "message") &&
    typeof (error as Record<string, unknown>).message === "string"
  );
}

/**
 * Enhanced error type with common error properties
 */
interface EnhancedError extends Error {
  code?: string;
  statusCode?: number;
  cause?: unknown;
}

/**
 * Type guard for enhanced error objects
 */
export function isEnhancedError(error: unknown): error is EnhancedError {
  return (
    error instanceof Error &&
    (Object.hasOwn(error, "code") ||
      Object.hasOwn(error, "statusCode") ||
      Object.hasOwn(error, "cause"))
  );
}

/**
 * Safely get error message from unknown error with ES2024 features
 * PRODUCTION-SAFE: Filters out sensitive information
 */
export function getErrorMessage(error: unknown): string {
  if (isErrorWithMessage(error)) {
    return sanitizeErrorMessage(error.message);
  }

  if (typeof error === "string") {
    return sanitizeErrorMessage(error);
  }

  // Use ES2024 Object.hasOwn for better property checking
  if (typeof error === "object" && error !== null) {
    const maybeToString = (error as { toString?: () => string }).toString;
    if (typeof maybeToString === "function") {
      try {
        const result = maybeToString.call(error);
        if (typeof result === "string") {
          return sanitizeErrorMessage(result);
        }
      } catch {
        // ignore
      }
    }
  }

  // Fallback for other types
  return "An unknown error occurred";
}

/**
 * PRODUCTION-SAFE: Get error message safe for client consumption
 * Removes sensitive information and stack traces
 */
export function getClientSafeErrorMessage(error: unknown): string {
  const message = getErrorMessage(error);

  // Generic messages for common sensitive errors
  if (
    message.toLowerCase().includes("password") ||
    message.toLowerCase().includes("token") ||
    message.toLowerCase().includes("secret") ||
    message.toLowerCase().includes("key") ||
    message.toLowerCase().includes("credential")
  ) {
    return "Authentication failed";
  }

  if (
    message.toLowerCase().includes("database") ||
    message.toLowerCase().includes("connection") ||
    message.toLowerCase().includes("sql")
  ) {
    return "Service temporarily unavailable";
  }

  if (
    message.toLowerCase().includes("internal") ||
    message.toLowerCase().includes("stack") ||
    message.includes("at ")
  ) {
    return "Internal server error";
  }

  // Return sanitized message if it seems safe
  return sanitizeErrorMessage(message);
}

/**
 * Remove sensitive patterns from error messages
 */
function sanitizeErrorMessage(message: string): string {
  return (
    message
      // Remove file paths
      .replace(/\/[a-zA-Z0-9._\-/]+\.(js|ts|py|php)/g, "[file]")
      // Remove stack trace patterns
      .replace(/\s+at\s+.*/g, "")
      // Remove absolute paths
      .replace(/[a-zA-Z]:[\\/][^:]+/g, "[path]")
      // Remove IP addresses
      .replace(/\b(?:\d{1,3}\.){3}\d{1,3}\b/g, "[ip]")
      // Remove URLs with credentials
      .replace(/https?:\/\/[^:]+:[^@]+@[^\s]+/g, "[url]")
      // Remove potential secrets (long alphanumeric strings)
      .replace(/\b[A-Za-z0-9]{32,}\b/g, "[token]")
      .trim()
  );
}

/**
 * Convert unknown error to Error instance with enhanced details
 */
export function toError(error: unknown): EnhancedError {
  if (error instanceof Error) {
    return error as EnhancedError;
  }

  const message = getErrorMessage(error);
  const enhancedError: EnhancedError = new Error(message);

  // Preserve original error as cause if it's an object
  if (typeof error === "object" && error !== null) {
    enhancedError.cause = error;
  }

  return enhancedError;
}

/**
 * ES2024 Promise.withResolvers for better async error handling
 * (This creates a promise with external resolve/reject functions)
 */
export function createDeferredPromise<T>(): {
  promise: Promise<T>;
  resolve: (value: T | PromiseLike<T>) => void;
  reject: (reason?: unknown) => void;
} {
  // Use native Promise.withResolvers if available (ES2024)
  if (
    "withResolvers" in Promise &&
    typeof (Promise as unknown as { withResolvers?: <_U>() => unknown }).withResolvers ===
      "function"
  ) {
    return (
      Promise as unknown as {
        withResolvers: <U>() => {
          promise: Promise<U>;
          resolve: (value: U | PromiseLike<U>) => void;
          reject: (reason?: unknown) => void;
        };
      }
    ).withResolvers<T>();
  }

  // Fallback polyfill
  let resolve!: (value: T | PromiseLike<T>) => void;
  let reject!: (reason?: unknown) => void;

  const promise = new Promise<T>((res, rej) => {
    resolve = res;
    reject = rej;
  });

  return { promise, resolve, reject };
}

/**
 * Safe async operation wrapper with proper error handling
 */
export async function safeAsync<T>(
  operation: () => Promise<T>,
  fallback?: T
): Promise<{ data: T | null; error: EnhancedError | null }> {
  try {
    const data = await operation();
    return { data, error: null };
  } catch (unknownError) {
    const error = toError(unknownError);
    return { data: fallback ?? null, error };
  }
}
