/**
 * Standardized Error Handling
 * Provides consistent error types and handling patterns across the codebase
 */

export enum ErrorCode {
  // Authentication & Authorization
  UNAUTHENTICATED = 'UNAUTHENTICATED',
  FORBIDDEN = 'FORBIDDEN',
  UNAUTHORIZED = 'UNAUTHORIZED',
  
  // Validation
  BAD_USER_INPUT = 'BAD_USER_INPUT',
  VALIDATION_ERROR = 'VALIDATION_ERROR',
  
  // Not Found
  NOT_FOUND = 'NOT_FOUND',
  RESOURCE_NOT_FOUND = 'RESOURCE_NOT_FOUND',
  TENANT_NOT_FOUND = 'TENANT_NOT_FOUND',
  
  // Business Logic
  QUOTA_EXCEEDED = 'QUOTA_EXCEEDED',
  RESOURCE_CONFLICT = 'RESOURCE_CONFLICT',
  OPERATION_FAILED = 'OPERATION_FAILED',
  
  // External Services
  EXTERNAL_SERVICE_ERROR = 'EXTERNAL_SERVICE_ERROR',
  BLOCKCHAIN_ERROR = 'BLOCKCHAIN_ERROR',
  PROXMOX_ERROR = 'PROXMOX_ERROR',
  
  // System
  INTERNAL_ERROR = 'INTERNAL_ERROR',
  DATABASE_ERROR = 'DATABASE_ERROR',
  NETWORK_ERROR = 'NETWORK_ERROR',
}

export interface AppErrorOptions {
  code: ErrorCode
  message: string
  details?: unknown
  cause?: Error
  statusCode?: number
}

/**
 * Standardized application error
 */
export class AppError extends Error {
  public readonly code: ErrorCode
  public readonly details?: unknown
  public readonly cause?: Error
  public readonly statusCode: number

  constructor(options: AppErrorOptions) {
    super(options.message)
    this.name = 'AppError'
    this.code = options.code
    this.details = options.details
    this.cause = options.cause
    this.statusCode = options.statusCode || this.getDefaultStatusCode(options.code)
    
    // Maintain proper stack trace
    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, AppError)
    }
  }

  private getDefaultStatusCode(code: ErrorCode): number {
    switch (code) {
      case ErrorCode.UNAUTHENTICATED:
        return 401
      case ErrorCode.FORBIDDEN:
      case ErrorCode.UNAUTHORIZED:
        return 403
      case ErrorCode.BAD_USER_INPUT:
      case ErrorCode.VALIDATION_ERROR:
        return 400
      case ErrorCode.NOT_FOUND:
      case ErrorCode.RESOURCE_NOT_FOUND:
      case ErrorCode.TENANT_NOT_FOUND:
        return 404
      case ErrorCode.RESOURCE_CONFLICT:
        return 409
      case ErrorCode.QUOTA_EXCEEDED:
        return 429
      case ErrorCode.EXTERNAL_SERVICE_ERROR:
      case ErrorCode.BLOCKCHAIN_ERROR:
      case ErrorCode.PROXMOX_ERROR:
        return 502
      case ErrorCode.DATABASE_ERROR:
      case ErrorCode.NETWORK_ERROR:
        return 503
      default:
        return 500
    }
  }

  toJSON() {
    return {
      name: this.name,
      code: this.code,
      message: this.message,
      details: this.details,
      statusCode: this.statusCode,
    }
  }
}

/**
 * Helper functions for common error scenarios
 */
export const AppErrors = {
  unauthenticated: (message = 'Authentication required', details?: unknown) =>
    new AppError({ code: ErrorCode.UNAUTHENTICATED, message, details }),

  forbidden: (message = 'Access denied', details?: unknown) =>
    new AppError({ code: ErrorCode.FORBIDDEN, message, details }),

  badInput: (message: string, details?: unknown) =>
    new AppError({ code: ErrorCode.BAD_USER_INPUT, message, details }),

  validation: (message: string, details?: unknown) =>
    new AppError({ code: ErrorCode.VALIDATION_ERROR, message, details }),

  notFound: (resource: string, id?: string) =>
    new AppError({
      code: ErrorCode.RESOURCE_NOT_FOUND,
      message: id ? `${resource} with id ${id} not found` : `${resource} not found`,
      details: { resource, id },
    }),

  tenantNotFound: (tenantId: string) =>
    new AppError({
      code: ErrorCode.TENANT_NOT_FOUND,
      message: `Tenant ${tenantId} not found`,
      details: { tenantId },
    }),

  quotaExceeded: (message: string, details?: unknown) =>
    new AppError({ code: ErrorCode.QUOTA_EXCEEDED, message, details }),

  conflict: (message: string, details?: unknown) =>
    new AppError({ code: ErrorCode.RESOURCE_CONFLICT, message, details }),

  externalService: (service: string, message: string, cause?: Error) =>
    new AppError({
      code: ErrorCode.EXTERNAL_SERVICE_ERROR,
      message: `${service}: ${message}`,
      cause,
      details: { service },
    }),

  blockchain: (message: string, cause?: Error) =>
    new AppError({
      code: ErrorCode.BLOCKCHAIN_ERROR,
      message,
      cause,
    }),

  proxmox: (message: string, cause?: Error) =>
    new AppError({
      code: ErrorCode.PROXMOX_ERROR,
      message,
      cause,
    }),

  database: (message: string, cause?: Error) =>
    new AppError({
      code: ErrorCode.DATABASE_ERROR,
      message,
      cause,
    }),

  internal: (message: string, cause?: Error, details?: unknown) =>
    new AppError({
      code: ErrorCode.INTERNAL_ERROR,
      message,
      cause,
      details,
    }),
}

/**
 * Check if error is an AppError
 */
export function isAppError(error: unknown): error is AppError {
  return error instanceof AppError
}

/**
 * Convert any error to AppError
 */
export function toAppError(error: unknown, defaultMessage = 'An error occurred'): AppError {
  if (isAppError(error)) {
    return error
  }

  if (error instanceof Error) {
    return AppErrors.internal(defaultMessage, error)
  }

  return AppErrors.internal(defaultMessage, undefined, { originalError: error })
}

