/**
 * Standardized error classes and utilities for model operations
 * Provides consistent error handling across all models
 */

class ModelError extends Error {
  constructor(message, code = 'MODEL_ERROR', context = {}) {
    super(message);
    this.name = 'ModelError';
    this.code = code;
    this.context = context;
    this.timestamp = new Date().toISOString();
  }

  toJSON() {
    return {
      name: this.name,
      message: this.message,
      code: this.code,
      context: this.context,
      timestamp: this.timestamp
    };
  }
}

class ValidationError extends ModelError {
  constructor(message, field = null, value = null) {
    super(message, 'VALIDATION_ERROR', { field, value });
    this.name = 'ValidationError';
  }
}

class NotFoundError extends ModelError {
  constructor(resource, id = null) {
    const message = id ? `${resource} with id '${id}' not found` : `${resource} not found`;
    super(message, 'NOT_FOUND', { resource, id });
    this.name = 'NotFoundError';
  }
}

class DatabaseError extends ModelError {
  constructor(operation, originalError) {
    const message = `Database operation '${operation}' failed: ${originalError.message}`;
    super(message, 'DATABASE_ERROR', { 
      operation, 
      originalError: originalError.message,
      stack: originalError.stack 
    });
    this.name = 'DatabaseError';
  }
}

class DuplicateError extends ModelError {
  constructor(resource, field, value) {
    const message = `${resource} with ${field} '${value}' already exists`;
    super(message, 'DUPLICATE_ERROR', { resource, field, value });
    this.name = 'DuplicateError';
  }
}

class AuthenticationError extends ModelError {
  constructor(message = 'Authentication failed') {
    super(message, 'AUTHENTICATION_ERROR');
    this.name = 'AuthenticationError';
  }
}

class AuthorizationError extends ModelError {
  constructor(action = 'perform this action') {
    super(`Not authorized to ${action}`, 'AUTHORIZATION_ERROR');
    this.name = 'AuthorizationError';
  }
}

/**
 * Error logging utility with context
 */
function logModelError(error, operation, model, additionalContext = {}) {
  const errorInfo = {
    timestamp: new Date().toISOString(),
    model,
    operation,
    error: {
      name: error.name,
      message: error.message,
      code: error.code,
      context: error.context
    },
    ...additionalContext
  };

  console.error(`[MODEL_ERROR] ${model}.${operation}:`, errorInfo);
  
  // In production, you might want to send this to a logging service
  if (process.env.NODE_ENV === 'production') {
    // Production logging logic here
    console.error('Production error logged:', JSON.stringify(errorInfo, null, 2));
  }
}

/**
 * Wrapper for async model operations with consistent error handling
 */
async function withErrorHandling(operation, errorContext) {
  try {
    return await operation();
  } catch (error) {
    // Always log the error for debugging
    logModelError(error, errorContext.operation, errorContext.model, errorContext);
    
    // If it's already one of our custom errors, just rethrow
    if (error instanceof ModelError) {
      throw error;
    }

    // For backward compatibility, rethrow the original error
    // This ensures existing tests and code continue to work
    throw error;
  }
}

/**
 * Create error context object for consistent logging
 */
function createErrorContext(model, operation, additional = {}) {
  return {
    model,
    operation,
    timestamp: new Date().toISOString(),
    ...additional
  };
}

module.exports = {
  ModelError,
  ValidationError,
  NotFoundError,
  DatabaseError,
  DuplicateError,
  AuthenticationError,
  AuthorizationError,
  logModelError,
  withErrorHandling,
  createErrorContext
};