/**
 * Custom Database Error class
 * Used for database-related errors
 */
export class DatabaseError extends Error {
  originalError?: any;
  code?: string;

  /**
   * Create a new Database error
   * @param message Error message
   * @param originalError Original error object
   * @param code Optional error code
   */
  constructor(message: string, originalError?: any, code?: string) {
    super(message);
    this.name = this.constructor.name;
    this.originalError = originalError;
    this.code = code || this.extractErrorCode(originalError);
    Error.captureStackTrace(this, this.constructor);
  }

  /**
   * Extract database-specific error code from original error
   * @param error Original error
   * @returns Error code string
   */
  private extractErrorCode(error: any): string {
    if (!error) {
      return 'DATABASE_ERROR';
    }

    // For PostgreSQL errors
    if (error.code) {
      return `PG_${error.code}`;
    }

    // For Knex.js query errors
    if (error.errno) {
      return `DB_${error.errno}`;
    }

    // Default error code
    return 'DATABASE_ERROR';
  }

  /**
   * Check if error is a duplicate key violation
   * @returns Boolean indicating if error is a duplicate key violation
   */
  isDuplicateKeyError(): boolean {
    // PostgreSQL unique violation code
    return this.code === 'PG_23505';
  }

  /**
   * Check if error is a foreign key violation
   * @returns Boolean indicating if error is a foreign key violation
   */
  isForeignKeyError(): boolean {
    // PostgreSQL foreign key violation code
    return this.code === 'PG_23503';
  }

  /**
   * Check if error is a connection error
   * @returns Boolean indicating if error is a connection error
   */
  isConnectionError(): boolean {
    // Various connection error codes
    const connectionErrorCodes = ['PG_ECONNREFUSED', 'PG_ETIMEDOUT', 'PG_ENOTFOUND', 'PG_ECONNRESET'];
    return connectionErrorCodes.includes(this.code || '');
  }

  /**
   * Get a simplified public version of the error (without sensitive details)
   * @returns Public error object
   */
  toPublicError(): { message: string; code: string } {
    return {
      message: this.getPublicMessage(),
      code: this.getPublicCode()
    };
  }

  /**
   * Get a public-facing error message
   * @returns Safe error message for clients
   */
  private getPublicMessage(): string {
    // Return user-friendly messages based on error type
    if (this.isDuplicateKeyError()) {
      return 'This record already exists.';
    }

    if (this.isForeignKeyError()) {
      return 'This operation violates data integrity constraints.';
    }

    if (this.isConnectionError()) {
      return 'Database connection error. Please try again later.';
    }

    // Default message
    return 'A database error occurred. Please try again later.';
  }

  /**
   * Get a public-facing error code
   * @returns Safe error code for clients
   */
  private getPublicCode(): string {
    if (this.isDuplicateKeyError()) {
      return 'DUPLICATE_RECORD';
    }

    if (this.isForeignKeyError()) {
      return 'INTEGRITY_CONSTRAINT_VIOLATION';
    }

    if (this.isConnectionError()) {
      return 'DATABASE_CONNECTION_ERROR';
    }

    return 'DATABASE_ERROR';
  }
}
