import { ExecutionError } from '../../../execution/engine/base/errors'

/**
 * Better Stack Logger (Simplified)
 *
 * Production-only error logging using Sentry SDK pointing to Better Stack.
 * Provides domain-specific context that global error handler cannot infer.
 *
 * **Design Philosophy:**
 * - Global error handler (setup-error-handler.ts) catches ALL errors automatically
 * - bslog functions only add value when they provide context the global handler can't capture
 * - WebSocket, SSE, Database, and Critical errors are already handled by global error handler
 * - Only use these functions for external services and execution failures that need special context
 *
 * **Naming Convention:** Prefixed with "bslog" to distinguish from other logging systems:
 * - Fastify Logger: request.log.info/error (HTTP lifecycle)
 * - Execution Logger: executionLogger.logError (AI execution tracking)
 * - Better Stack Logger: bslog*() (production error tracking & alerts)
 *
 * **When to Use:**
 * - External service failures (OpenAI, WorkOS, OAuth) - global handler can't infer service name
 * - Execution failures - global handler can't infer execution phase/resource type
 * - System errors only (use isSystemError() to filter 5xx vs 4xx)
 *
 * **When NOT to Use:**
 * - User errors (4xx validation, auth checks) - too noisy
 * - WebSocket/SSE/Database errors - global handler already logs these
 * - Generic errors - global handler already logs these
 *
 * All functions are no-ops unless NODE_ENV === 'production' AND API_BETTERSTACK_ERRORS_DSN is set.
 */

/**
 * Log external service failures (OpenAI, WorkOS, OAuth providers)
 *
 * Use this when external API calls fail to add service-specific context
 * that the global error handler cannot infer.
 *
 * @example
 * ```typescript
 * try {
 *   const response = await openai.chat.completions.create(params)
 * } catch (error) {
 *   bslogExternalServiceError('openai', 'chat.completions.create', error, {
 *     model: params.model,
 *     tokens: params.max_tokens,
 *     organizationId: executionContext.organizationId,
 *     statusCode: (error as any).status
 *   })
 *   throw error
 * }
 * ```
 */
export async function bslogExternalServiceError(
  service: 'openai' | 'workos' | 'google-oauth' | 'notion-oauth' | 'attio-oauth' | string,
  operation: string,
  error: Error,
  context?: {
    statusCode?: number
    response?: string
    endpoint?: string
    organizationId?: string
    userId?: string
    [key: string]: unknown
  }
): Promise<void> {
  if (process.env.NODE_ENV === 'production' && process.env.API_BETTERSTACK_ERRORS_DSN) {
    // Sentry is optional - only loaded in production
    try {
      const Sentry = await import('@sentry/node')
      Sentry.captureException(error, {
        tags: {
          layer: 'integration',
          service,
          operation,
          ...(context?.statusCode && { status_code: context.statusCode })
        },
        user: context?.userId
          ? {
              id: context.userId
            }
          : undefined,
        extra: {
          service,
          operation,
          organizationId: context?.organizationId,
          endpoint: context?.endpoint,
          response: context?.response,
          ...context
        }
      })
    } catch {
      // Sentry not available - skip logging
    }
  }
}

/**
 * Log workflow/agent execution failures
 *
 * Use this for critical business logic failures to add execution-specific
 * context that the global error handler cannot infer.
 *
 * @example
 * ```typescript
 * try {
 *   return await this.runExecution(params)
 * } catch (error) {
 *   bslogExecutionFailure({
 *     executionId: execution.id,
 *     resourceType: params.resourceType,
 *     resourceId: params.resourceId,
 *     organizationId: params.organizationId,
 *     userId: params.userId,
 *     inputType: typeof params.input,
 *     errorPhase: 'execution'
 *   }, error as Error)
 *   throw error
 * }
 * ```
 */
export async function bslogExecutionFailure(
  executionContext: {
    executionId?: string
    resourceType: 'workflow' | 'agent'
    resourceId: string
    organizationId: string
    userId?: string
    inputType?: string
    errorPhase?: 'initialization' | 'execution' | 'completion' | 'cleanup'
  },
  error: Error
): Promise<void> {
  if (process.env.NODE_ENV === 'production' && process.env.API_BETTERSTACK_ERRORS_DSN) {
    try {
      const Sentry = await import('@sentry/node')
      const domainTags =
        error instanceof ExecutionError
          ? {
              error_type_domain: error.type,
              error_severity: error.severity,
              error_category: error.category,
              is_retryable: String(error.isRetryable())
            }
          : {}

      Sentry.captureException(error, {
        tags: {
          layer: 'execution',
          resource_type: executionContext.resourceType,
          organization_id: executionContext.organizationId,
          error_phase: executionContext.errorPhase || 'execution',
          ...domainTags
        },
        user: executionContext.userId
          ? {
              id: executionContext.userId
            }
          : undefined,
        extra: {
          executionId: executionContext.executionId,
          resourceType: executionContext.resourceType,
          resourceId: executionContext.resourceId,
          organizationId: executionContext.organizationId,
          inputType: executionContext.inputType,
          errorPhase: executionContext.errorPhase
        }
      })
    } catch {
      // Sentry not available - skip logging
    }
  }
}

/**
 * Helper: Check if error should be logged to Better Stack
 *
 * Returns true for system errors (5xx), false for user errors (4xx)
 *
 * Use this to filter out validation errors and auth failures that
 * would create noise in Better Stack.
 *
 * @example
 * ```typescript
 * catch (error) {
 *   if (isSystemError(error)) {
 *     bslogExternalServiceError('openai', 'completion', error, { ... })
 *   }
 *   throw error
 * }
 * ```
 */
export function isSystemError(error: unknown): boolean {
  // Check if error has a status code property (common in HTTP errors)
  if (error && typeof error === 'object') {
    const statusCode =
      ('statusCode' in error && typeof error.statusCode === 'number' && error.statusCode) ||
      ('status' in error && typeof error.status === 'number' && error.status) ||
      ('code' in error && typeof error.code === 'number' && error.code)

    if (statusCode) {
      // 5xx = system error (log it)
      // 4xx = user error (don't log)
      return statusCode >= 500
    }
  }

  // If no status code, assume it's a system error (better to over-log than under-log)
  return true
}
