import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import * as Sentry from '@sentry/node';
import { nodeProfilingIntegration } from '@sentry/profiling-node';

export enum ErrorSeverity {
  FATAL = 'fatal',
  ERROR = 'error',
  WARNING = 'warning',
  INFO = 'info',
  DEBUG = 'debug',
}

export enum ErrorCategory {
  // Authentication & Authorization
  AUTH_FAILED = 'auth_failed',
  AUTH_TOKEN_EXPIRED = 'auth_token_expired',
  AUTH_DEVICE_NOT_TRUSTED = 'auth_device_not_trusted',

  // AI Service Errors
  AI_PROVIDER_FAILED = 'ai_provider_failed',
  AI_RATE_LIMIT = 'ai_rate_limit',
  AI_INVALID_RESPONSE = 'ai_invalid_response',
  AI_CONTEXT_TOO_LARGE = 'ai_context_too_large',

  // Database Errors
  DATABASE_CONNECTION = 'database_connection',
  DATABASE_QUERY_TIMEOUT = 'database_query_timeout',
  DATABASE_CONSTRAINT_VIOLATION = 'database_constraint_violation',

  // API Errors
  API_VALIDATION_ERROR = 'api_validation_error',
  API_RATE_LIMIT = 'api_rate_limit',
  API_EXTERNAL_SERVICE = 'api_external_service',

  // Business Logic Errors
  FAMILY_SIZE_EXCEEDED = 'family_size_exceeded',
  CHILD_NOT_FOUND = 'child_not_found',
  ACTIVITY_VALIDATION = 'activity_validation',

  // System Errors
  MEMORY_EXCEEDED = 'memory_exceeded',
  DISK_SPACE_LOW = 'disk_space_low',
  SERVICE_UNAVAILABLE = 'service_unavailable',
}

export interface ErrorContext {
  userId?: string;
  familyId?: string;
  childId?: string;
  activityId?: string;
  conversationId?: string;
  requestId?: string;
  endpoint?: string;
  method?: string;
  userAgent?: string;
  ipAddress?: string;
  [key: string]: any;
}

export interface ErrorTrackingConfig {
  dsn?: string;
  environment: string;
  release?: string;
  sampleRate: number;
  tracesSampleRate: number;
  profilesSampleRate: number;
  enabled: boolean;
}

/**
 * Error Tracking Service - Sentry Integration
 *
 * Features:
 * - Error and exception tracking with Sentry
 * - Performance monitoring and profiling
 * - User context and breadcrumbs
 * - Custom error categorization
 * - Alerting integration
 * - Error aggregation and deduplication
 *
 * Usage:
 * ```typescript
 * this.errorTracking.captureError(error, {
 *   category: ErrorCategory.AI_PROVIDER_FAILED,
 *   severity: ErrorSeverity.ERROR,
 *   context: { userId, provider: 'azure' }
 * });
 * ```
 */
@Injectable()
export class ErrorTrackingService implements OnModuleInit {
  private readonly logger = new Logger('ErrorTrackingService');
  private config: ErrorTrackingConfig;
  private initialized = false;

  constructor(private configService: ConfigService) {
    this.config = {
      dsn: this.configService.get<string>('SENTRY_DSN'),
      environment: this.configService.get<string>('NODE_ENV', 'development'),
      release: this.configService.get<string>('APP_VERSION', '1.0.0'),
      sampleRate: parseFloat(
        this.configService.get<string>('SENTRY_SAMPLE_RATE', '1.0'),
      ),
      tracesSampleRate: parseFloat(
        this.configService.get<string>('SENTRY_TRACES_SAMPLE_RATE', '0.1'),
      ),
      profilesSampleRate: parseFloat(
        this.configService.get<string>('SENTRY_PROFILES_SAMPLE_RATE', '0.1'),
      ),
      enabled:
        this.configService.get<string>('SENTRY_ENABLED', 'false') === 'true',
    };
  }

  onModuleInit() {
    if (!this.config.enabled) {
      this.logger.warn('Error tracking disabled - Sentry not configured');
      return;
    }

    if (!this.config.dsn) {
      this.logger.warn('SENTRY_DSN not configured - Error tracking disabled');
      return;
    }

    try {
      Sentry.init({
        dsn: this.config.dsn,
        environment: this.config.environment,
        release: this.config.release,
        sampleRate: this.config.sampleRate,
        tracesSampleRate: this.config.tracesSampleRate,
        profilesSampleRate: this.config.profilesSampleRate,

        // Integrations
        integrations: [
          // Performance monitoring
          nodeProfilingIntegration(),
        ],

        // Before send hook - sanitize sensitive data
        beforeSend: (event) => {
          return this.sanitizeEvent(event);
        },

        // Error filtering
        ignoreErrors: [
          // Ignore expected errors
          'NotFoundException',
          'UnauthorizedException',
          'BadRequestException',
        ],
      });

      this.initialized = true;
      this.logger.log(
        `Sentry initialized: ${this.config.environment} (${this.config.release})`,
      );
    } catch (error) {
      this.logger.error(`Failed to initialize Sentry: ${error.message}`);
    }
  }

  /**
   * Capture an error with context
   */
  captureError(
    error: Error,
    options?: {
      category?: ErrorCategory;
      severity?: ErrorSeverity;
      context?: ErrorContext;
      tags?: Record<string, string>;
      fingerprint?: string[];
    },
  ): string | null {
    if (!this.initialized) {
      this.logger.error(
        `[${options?.category || 'ERROR'}] ${error.message}`,
        error.stack,
      );
      return null;
    }

    try {
      // Set context
      if (options?.context) {
        this.setContext(options.context);
      }

      // Set tags
      if (options?.tags) {
        Object.entries(options.tags).forEach(([key, value]) => {
          Sentry.setTag(key, value);
        });
      }

      // Set category as tag
      if (options?.category) {
        Sentry.setTag('error_category', options.category);
      }

      // Set custom fingerprint for grouping
      if (options?.fingerprint) {
        Sentry.setContext('fingerprint', { fingerprint: options.fingerprint });
      }

      // Capture with severity
      const eventId = Sentry.captureException(error, {
        level: this.convertSeverity(options?.severity || ErrorSeverity.ERROR),
      });

      this.logger.debug(`Error captured in Sentry: ${eventId}`);
      return eventId;
    } catch (captureError) {
      this.logger.error(
        `Failed to capture error in Sentry: ${captureError.message}`,
      );
      return null;
    }
  }

  /**
   * Capture a custom message
   */
  captureMessage(
    message: string,
    options?: {
      severity?: ErrorSeverity;
      context?: ErrorContext;
      tags?: Record<string, string>;
    },
  ): string | null {
    if (!this.initialized) {
      this.logger.log(`[MESSAGE] ${message}`);
      return null;
    }

    try {
      // Set context
      if (options?.context) {
        this.setContext(options.context);
      }

      // Set tags
      if (options?.tags) {
        Object.entries(options.tags).forEach(([key, value]) => {
          Sentry.setTag(key, value);
        });
      }

      const eventId = Sentry.captureMessage(message, {
        level: this.convertSeverity(options?.severity || ErrorSeverity.INFO),
      });

      return eventId;
    } catch (error) {
      this.logger.error(
        `Failed to capture message in Sentry: ${error.message}`,
      );
      return null;
    }
  }

  /**
   * Set user context
   */
  setUser(
    userId: string,
    data?: { email?: string; username?: string; familyId?: string },
  ) {
    if (!this.initialized) return;

    Sentry.setUser({
      id: userId,
      email: data?.email,
      username: data?.username,
      familyId: data?.familyId,
    });
  }

  /**
   * Clear user context (on logout)
   */
  clearUser() {
    if (!this.initialized) return;
    Sentry.setUser(null);
  }

  /**
   * Set custom context data
   */
  setContext(context: ErrorContext) {
    if (!this.initialized) return;

    // User context
    if (context.userId) {
      Sentry.setTag('user_id', context.userId);
    }

    if (context.familyId) {
      Sentry.setTag('family_id', context.familyId);
    }

    // Request context
    if (context.requestId) {
      Sentry.setTag('request_id', context.requestId);
    }

    if (context.endpoint) {
      Sentry.setTag('endpoint', context.endpoint);
    }

    if (context.method) {
      Sentry.setTag('http_method', context.method);
    }

    // Additional context
    const additionalContext = { ...context };
    delete additionalContext.userId;
    delete additionalContext.familyId;
    delete additionalContext.requestId;
    delete additionalContext.endpoint;
    delete additionalContext.method;

    if (Object.keys(additionalContext).length > 0) {
      Sentry.setContext('additional', additionalContext);
    }
  }

  /**
   * Add breadcrumb for debugging
   */
  addBreadcrumb(
    message: string,
    data?: Record<string, any>,
    category?: string,
    level?: ErrorSeverity,
  ) {
    if (!this.initialized) return;

    Sentry.addBreadcrumb({
      message,
      data,
      category: category || 'custom',
      level: this.convertSeverity(level || ErrorSeverity.INFO),
      timestamp: Date.now() / 1000,
    });
  }

  /**
   * Start a transaction for performance monitoring
   */
  startTransaction(name: string, op: string): any {
    if (!this.initialized) return null;

    // Simplified transaction start for newer Sentry SDK
    return Sentry.startSpan({ name, op }, (span) => span);
  }

  /**
   * Sanitize event before sending to Sentry
   */
  private sanitizeEvent(event: Sentry.ErrorEvent): Sentry.ErrorEvent | null {
    // Remove sensitive data from request
    if (event.request) {
      // Remove authorization headers
      if (event.request.headers) {
        delete event.request.headers['authorization'];
        delete event.request.headers['Authorization'];
        delete event.request.headers['cookie'];
        delete event.request.headers['Cookie'];
      }

      // Remove sensitive query parameters
      if (
        event.request.query_string &&
        typeof event.request.query_string === 'string'
      ) {
        event.request.query_string = event.request.query_string.replace(
          /token=[^&]*/gi,
          'token=REDACTED',
        );
        event.request.query_string = event.request.query_string.replace(
          /key=[^&]*/gi,
          'key=REDACTED',
        );
      }
    }

    // Remove sensitive data from extra
    if (event.extra) {
      delete event.extra.password;
      delete event.extra.apiKey;
      delete event.extra.token;
      delete event.extra.secret;
    }

    return event;
  }

  /**
   * Convert internal severity to Sentry severity
   */
  private convertSeverity(severity: ErrorSeverity): Sentry.SeverityLevel {
    switch (severity) {
      case ErrorSeverity.FATAL:
        return 'fatal';
      case ErrorSeverity.ERROR:
        return 'error';
      case ErrorSeverity.WARNING:
        return 'warning';
      case ErrorSeverity.INFO:
        return 'info';
      case ErrorSeverity.DEBUG:
        return 'debug';
      default:
        return 'error';
    }
  }

  /**
   * Check if error tracking is enabled
   */
  isEnabled(): boolean {
    return this.initialized;
  }

  /**
   * Get configuration status
   */
  getStatus(): {
    enabled: boolean;
    environment: string;
    release: string;
    sampleRate: number;
  } {
    return {
      enabled: this.initialized,
      environment: this.config.environment,
      release: this.config.release,
      sampleRate: this.config.sampleRate,
    };
  }
}
