/**
 * Service responsible for collecting and reporting Salesforce queue metrics
 */
import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";

export interface SalesforceRouteMetricsInternal {
  label: string;
  totalRequests: number;
  failedRequests: number;
  lastSuccessTime?: Date | undefined;
  lastErrorTime?: Date | undefined;
}

export interface SalesforceRouteMetricsSnapshot {
  totalRequests: number;
  failedRequests: number;
  successRate: number;
  lastSuccessTime?: Date | undefined;
  lastErrorTime?: Date | undefined;
}

export interface SalesforceQueueMetricsData {
  totalRequests: number;
  completedRequests: number;
  failedRequests: number;
  queueSize: number;
  pendingRequests: number;
  averageWaitTime: number;
  averageExecutionTime: number;
  dailyApiUsage: number;
  lastRequestTime?: Date | undefined;
  lastErrorTime?: Date | undefined;
  lastRateLimitTime?: Date | undefined;
}

@Injectable()
export class SalesforceQueueMetricsService {
  private readonly maxMetricsHistory = 100;
  private readonly waitTimes: number[] = [];
  private readonly executionTimes: number[] = [];
  private readonly routeMetrics = new Map<string, SalesforceRouteMetricsInternal>();

  private metrics: SalesforceQueueMetricsData = {
    totalRequests: 0,
    completedRequests: 0,
    failedRequests: 0,
    queueSize: 0,
    pendingRequests: 0,
    averageWaitTime: 0,
    averageExecutionTime: 0,
    dailyApiUsage: 0,
  };

  constructor(@Inject(Logger) _logger: Logger) {
    // Logger available for future use in metrics logging
    void _logger;
  }

  /**
   * Get current metrics data
   */
  getMetrics(): SalesforceQueueMetricsData {
    return { ...this.metrics };
  }

  /**
   * Get route-level metrics breakdown
   */
  getRouteMetricsSnapshot(): Record<string, SalesforceRouteMetricsSnapshot> {
    const snapshot: Record<string, SalesforceRouteMetricsSnapshot> = {};

    for (const [label, metric] of this.routeMetrics.entries()) {
      const total = metric.totalRequests;
      const failures = metric.failedRequests;
      const successRate = total > 0 ? (total - failures) / total : 1;

      snapshot[label] = {
        totalRequests: total,
        failedRequests: failures,
        successRate,
        lastSuccessTime: metric.lastSuccessTime,
        lastErrorTime: metric.lastErrorTime,
      };
    }

    return snapshot;
  }

  /**
   * Increment total requests counter
   */
  incrementTotalRequests(): void {
    this.metrics.totalRequests++;
  }

  /**
   * Increment completed requests counter
   */
  incrementCompletedRequests(): void {
    this.metrics.completedRequests++;
    this.metrics.lastRequestTime = new Date();
  }

  /**
   * Increment failed requests counter
   */
  incrementFailedRequests(): void {
    this.metrics.failedRequests++;
    this.metrics.lastErrorTime = new Date();
  }

  /**
   * Record rate limit time
   */
  recordRateLimitTime(): void {
    this.metrics.lastRateLimitTime = new Date();
  }

  /**
   * Increment daily API usage counter
   */
  incrementDailyApiUsage(): void {
    this.metrics.dailyApiUsage++;
  }

  /**
   * Set daily API usage (for restoration from cache)
   */
  setDailyApiUsage(usage: number): void {
    this.metrics.dailyApiUsage = usage;
  }

  /**
   * Reset daily API usage counter
   */
  resetDailyApiUsage(): void {
    this.metrics.dailyApiUsage = 0;
  }

  /**
   * Get daily API usage
   */
  getDailyApiUsage(): number {
    return this.metrics.dailyApiUsage;
  }

  /**
   * Update queue size and pending requests
   */
  updateQueueStats(queueSize: number, pendingRequests: number): void {
    this.metrics.queueSize = queueSize;
    this.metrics.pendingRequests = pendingRequests;
    this.recalculateAverages();
  }

  /**
   * Record wait time for a request
   */
  recordWaitTime(waitTime: number): void {
    this.waitTimes.push(waitTime);
    if (this.waitTimes.length > this.maxMetricsHistory) {
      this.waitTimes.shift();
    }
  }

  /**
   * Record execution time for a request
   */
  recordExecutionTime(executionTime: number): void {
    this.executionTimes.push(executionTime);
    if (this.executionTimes.length > this.maxMetricsHistory) {
      this.executionTimes.shift();
    }
  }

  /**
   * Record route start
   */
  recordRouteStart(label: string): void {
    const metric = this.getOrCreateRouteMetric(label);
    metric.totalRequests += 1;
  }

  /**
   * Record route success
   */
  recordRouteSuccess(label: string): void {
    const metric = this.getOrCreateRouteMetric(label);
    metric.lastSuccessTime = new Date();
  }

  /**
   * Record route failure
   */
  recordRouteFailure(label: string): void {
    const metric = this.getOrCreateRouteMetric(label);
    metric.failedRequests += 1;
    metric.lastErrorTime = new Date();
  }

  /**
   * Normalize a label for route metrics
   */
  normalizeLabel(label?: string): string {
    if (!label) {
      return "generic";
    }
    const trimmed = label.trim();
    return trimmed.length > 0 ? trimmed : "generic";
  }

  /**
   * Calculate error rate
   */
  getErrorRate(): number {
    return this.metrics.totalRequests > 0
      ? this.metrics.failedRequests / this.metrics.totalRequests
      : 0;
  }

  /**
   * Get average wait time
   */
  getAverageWaitTime(): number {
    return this.metrics.averageWaitTime;
  }

  private recalculateAverages(): void {
    if (this.waitTimes.length > 0) {
      this.metrics.averageWaitTime =
        this.waitTimes.reduce((sum, time) => sum + time, 0) / this.waitTimes.length;
    }

    if (this.executionTimes.length > 0) {
      this.metrics.averageExecutionTime =
        this.executionTimes.reduce((sum, time) => sum + time, 0) / this.executionTimes.length;
    }
  }

  private getOrCreateRouteMetric(label: string): SalesforceRouteMetricsInternal {
    const existing = this.routeMetrics.get(label);
    if (existing) {
      return existing;
    }

    const metric: SalesforceRouteMetricsInternal = {
      label,
      totalRequests: 0,
      failedRequests: 0,
    };
    this.routeMetrics.set(label, metric);
    return metric;
  }
}
