// src/services/sentry.server.ts
/**
 * Sentry SDK initialization for error tracking.
 * Implements ADR-015: Application Performance Monitoring and Error Tracking.
 *
 * This module configures @sentry/node to send errors to our self-hosted
 * Bugsink instance, which is Sentry-compatible.
 *
 * IMPORTANT: This module should be imported and initialized at the very top
 * of server.ts, before any other imports, to ensure all errors are captured.
 *
 * Note: Uses Sentry SDK v8+ API which differs significantly from v7.
 */
import * as Sentry from '@sentry/node';
import type { Request, Response, NextFunction, ErrorRequestHandler } from 'express';
import { config, isSentryConfigured, isProduction, isTest } from '../config/env';
import { logger } from './logger.server';

/**
 * Initializes the Sentry SDK with the configured DSN.
 * Should be called once at application startup.
 */
export function initSentry(): void {
  if (!isSentryConfigured) {
    logger.info('[Sentry] Error tracking disabled (SENTRY_DSN not configured)');
    return;
  }

  // Don't initialize Sentry in test environment
  if (isTest) {
    logger.debug('[Sentry] Skipping initialization in test environment');
    return;
  }

  Sentry.init({
    dsn: config.sentry.dsn,
    environment: config.sentry.environment || config.server.nodeEnv,
    debug: config.sentry.debug,

    // Performance monitoring - disabled for now to keep it simple
    tracesSampleRate: 0,

    // Before sending an event, add additional context
    beforeSend(event, hint) {
      // In development, log errors to console as well
      if (!isProduction && hint.originalException) {
        logger.error(
          { err: hint.originalException, sentryEventId: event.event_id },
          '[Sentry] Capturing error',
        );
      }
      return event;
    },
  });

  logger.info(
    { environment: config.sentry.environment || config.server.nodeEnv },
    '[Sentry] Error tracking initialized',
  );
}

/**
 * Creates Sentry middleware for Express.
 * Returns the request handler and error handler middleware.
 *
 * In Sentry SDK v8+, the old Handlers.requestHandler and Handlers.errorHandler
 * have been replaced. Request context is now captured automatically via the
 * Express integration. We provide a custom error handler that filters errors.
 */
export function getSentryMiddleware(): {
  requestHandler: (req: Request, res: Response, next: NextFunction) => void;
  errorHandler: ErrorRequestHandler;
} {
  if (!isSentryConfigured || isTest) {
    // Return no-op middleware when Sentry is not configured
    return {
      requestHandler: (_req: Request, _res: Response, next: NextFunction) => next(),
      errorHandler: (_err: Error, _req: Request, _res: Response, next: NextFunction) => next(_err),
    };
  }

  return {
    // In SDK v8+, request context is captured automatically.
    // This middleware is a placeholder for compatibility.
    requestHandler: (_req: Request, _res: Response, next: NextFunction) => next(),

    // Custom error handler that captures errors to Sentry
    errorHandler: (err: Error, _req: Request, _res: Response, next: NextFunction) => {
      // Only send 5xx errors to Sentry by default
      const statusCode =
        (err as Error & { statusCode?: number }).statusCode ||
        (err as Error & { status?: number }).status ||
        500;

      if (statusCode >= 500) {
        Sentry.captureException(err);
      }

      // Pass the error to the next error handler
      next(err);
    },
  };
}

/**
 * Captures an exception and sends it to Sentry.
 * Use this for errors that are caught and handled gracefully.
 */
export function captureException(error: Error, context?: Record<string, unknown>): string | null {
  if (!isSentryConfigured || isTest) {
    return null;
  }

  if (context) {
    Sentry.setContext('additional', context);
  }

  return Sentry.captureException(error);
}

/**
 * Captures a message and sends it to Sentry.
 * Use this for non-exception events that should be tracked.
 */
export function captureMessage(
  message: string,
  level: Sentry.SeverityLevel = 'info',
): string | null {
  if (!isSentryConfigured || isTest) {
    return null;
  }

  return Sentry.captureMessage(message, level);
}

/**
 * Sets the user context for all subsequent events.
 * Call this after user authentication.
 */
export function setUser(user: { id: string; email?: string; username?: string } | null): void {
  if (!isSentryConfigured || isTest) {
    return;
  }

  Sentry.setUser(user);
}

/**
 * Adds a breadcrumb to the current scope.
 * Breadcrumbs are logged actions that led up to an error.
 */
export function addBreadcrumb(breadcrumb: Sentry.Breadcrumb): void {
  if (!isSentryConfigured || isTest) {
    return;
  }

  Sentry.addBreadcrumb(breadcrumb);
}

// Re-export Sentry for advanced usage
export { Sentry };
