import { Injectable, Inject, OnModuleInit, OnModuleDestroy } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { ConfigService } from "@nestjs/config";
import { CacheService } from "@bff/infra/cache/cache.service";
export interface SalesforceQueueMetrics {
  totalRequests: number;
  completedRequests: number;
  failedRequests: number;
  queueSize: number;
  pendingRequests: number;
  averageWaitTime: number;
  averageExecutionTime: number;
  dailyApiUsage: number;
  lastRequestTime?: Date;
  lastErrorTime?: Date;
  lastRateLimitTime?: Date;
  dailyApiLimit?: number;
  dailyUsagePercent?: number;
  routeBreakdown?: Record<string, SalesforceRouteMetricsSnapshot>;
  degradation?: SalesforceDegradationSnapshot;
}

export interface SalesforceRequestOptions {
  priority?: number; // Higher number = higher priority (0-10)
  timeout?: number; // Request timeout in ms
  retryAttempts?: number; // Number of retry attempts
  retryDelay?: number; // Base delay between retries in ms
  isLongRunning?: boolean; // Mark as long-running request (>20s expected)
  label?: string; // Logical label for route-level metrics
}

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

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

interface SalesforceDegradationSnapshot {
  degraded: boolean;
  reason: "rate-limit" | "usage-threshold" | "queue-pressure" | null;
  cooldownExpiresAt?: Date;
  usagePercent: number;
}

/**
 * Salesforce Request Queue Service
 *
 * Manages concurrent requests to Salesforce API to prevent:
 * - Daily API limit exhaustion (100,000 + 1,000 per user)
 * - Concurrent request limit violations (25 long-running requests)
 * - Rate limit violations and 429 errors
 * - Optimal resource utilization
 *
 * Based on Salesforce documentation:
 * - Daily limit: 100,000 + (1,000 × users) per 24h
 * - Concurrent limit: 25 long-running requests (>20s)
 * - Timeout: 10 minutes per request
 * - Rate limiting: Conservative 120 requests per minute (2 RPS)
 */
type PQueueCtor = new (options: {
  concurrency?: number;
  interval?: number;
  intervalCap?: number;
  timeout?: number;
  throwOnTimeout?: boolean;
  carryoverConcurrencyCount?: boolean;
}) => PQueueInstance;

interface PQueueInstance {
  add<T>(fn: () => Promise<T>, options?: { priority?: number }): Promise<T>;
  clear(): void;
  onIdle(): Promise<void>;
  on(event: "add" | "next" | "idle" | "error", listener: (...args: unknown[]) => void): void;
  size: number;
  pending: number;
}

@Injectable()
export class SalesforceRequestQueueService implements OnModuleInit, OnModuleDestroy {
  private pQueueCtor: PQueueCtor | null = null;
  private standardQueue: PQueueInstance | null = null;
  private longRunningQueue: PQueueInstance | null = null;
  private readonly metrics: SalesforceQueueMetrics = {
    totalRequests: 0,
    completedRequests: 0,
    failedRequests: 0,
    queueSize: 0,
    pendingRequests: 0,
    averageWaitTime: 0,
    averageExecutionTime: 0,
    dailyApiUsage: 0,
  };

  private readonly waitTimes: number[] = [];
  private readonly executionTimes: number[] = [];
  private readonly maxMetricsHistory = 100;
  private dailyUsageResetTime: Date;
  private readonly routeMetrics = new Map<string, SalesforceRouteMetricsInternal>();
  private readonly usageWarningLevels = [0.7, 0.85, 0.95];
  private highestUsageWarningIssued = 0;
  private readonly dailyApiLimit: number;
  private readonly rateLimitCooldownMs: number;
  private readonly queueSizeDegradeThreshold: number;
  private readonly queuePendingDegradeThreshold: number;
  private readonly averageWaitDegradeMs: number;
  private readonly dailyUsageCacheKey = "salesforce:queue:daily-usage";
  private degradeState: {
    until: Date | null;
    reason: "rate-limit" | "usage-threshold" | "queue-pressure" | null;
  } = { until: null, reason: null };

  constructor(
    @Inject(Logger) private readonly logger: Logger,
    private readonly configService: ConfigService,
    private readonly cache: CacheService
  ) {
    this.dailyUsageResetTime = this.getNextDayReset();
    this.dailyApiLimit = this.resolveDailyApiLimit();
    this.rateLimitCooldownMs =
      this.parseNumericConfig(this.configService.get("SF_RATE_LIMIT_COOLDOWN_MS")) ?? 60000;
    this.queueSizeDegradeThreshold =
      this.parseNumericConfig(this.configService.get("SF_QUEUE_PRESSURE_SIZE_THRESHOLD")) ?? 120;
    this.queuePendingDegradeThreshold =
      this.parseNumericConfig(this.configService.get("SF_QUEUE_PRESSURE_PENDING_THRESHOLD")) ?? 45;
    this.averageWaitDegradeMs =
      this.parseNumericConfig(this.configService.get("SF_QUEUE_PRESSURE_WAIT_MS")) ?? 1500;
  }

  private async loadPQueue(): Promise<PQueueCtor> {
    if (!this.pQueueCtor) {
      const module = await import("p-queue");
      this.pQueueCtor = module.default as PQueueCtor;
    }
    return this.pQueueCtor;
  }

  private async ensureQueuesInitialized(): Promise<{
    standardQueue: PQueueInstance;
    longRunningQueue: PQueueInstance;
  }> {
    if (!this.standardQueue || !this.longRunningQueue) {
      const PQueue = await this.loadPQueue();
      const concurrency = this.configService.get<number>("SF_QUEUE_CONCURRENCY", 15);
      const longRunningConcurrency = this.configService.get<number>(
        "SF_QUEUE_LONG_RUNNING_CONCURRENCY",
        22
      );
      const intervalCap = this.configService.get<number>("SF_QUEUE_INTERVAL_CAP", 600);
      const timeout = this.configService.get<number>("SF_QUEUE_TIMEOUT_MS", 30000);
      const longRunningTimeout = this.configService.get<number>(
        "SF_QUEUE_LONG_RUNNING_TIMEOUT_MS",
        600000
      );

      // Configurable Salesforce requests queue
      this.standardQueue = new PQueue({
        concurrency, // Max concurrent standard requests
        interval: 60000, // Per minute
        intervalCap, // Max requests per minute
        timeout, // Request timeout
        throwOnTimeout: true,
        carryoverConcurrencyCount: true,
      });

      // Long-running requests queue (separate to respect 25 concurrent limit)
      this.longRunningQueue = new PQueue({
        concurrency: longRunningConcurrency, // Max concurrent long-running requests
        timeout: longRunningTimeout, // Timeout for long-running requests
        throwOnTimeout: true,
        carryoverConcurrencyCount: true,
      });

      // Set up queue event listeners
      this.setupQueueListeners();
    }

    const standardQueue = this.standardQueue;
    const longRunningQueue = this.longRunningQueue;

    if (!standardQueue || !longRunningQueue) {
      throw new Error("Failed to initialize Salesforce queues");
    }

    return { standardQueue, longRunningQueue };
  }

  async onModuleInit() {
    await this.ensureQueuesInitialized();
    await this.restoreDailyUsageFromCache();
    const concurrency = this.configService.get<number>("SF_QUEUE_CONCURRENCY", 15);
    const longRunningConcurrency = this.configService.get<number>(
      "SF_QUEUE_LONG_RUNNING_CONCURRENCY",
      22
    );
    const intervalCap = this.configService.get<number>("SF_QUEUE_INTERVAL_CAP", 600);
    const timeout = this.configService.get<number>("SF_QUEUE_TIMEOUT_MS", 30000);
    const longRunningTimeout = this.configService.get<number>(
      "SF_QUEUE_LONG_RUNNING_TIMEOUT_MS",
      600000
    );

    this.logger.log("Salesforce Request Queue initialized", {
      standardConcurrency: concurrency,
      longRunningConcurrency,
      rateLimit: `${intervalCap} requests/minute (${(intervalCap / 60).toFixed(1)} RPS)`,
      standardTimeout: `${timeout / 1000} seconds`,
      longRunningTimeout: `${longRunningTimeout / 60000} minutes`,
    });
  }

  async onModuleDestroy() {
    const standardQueue = this.standardQueue;
    const longRunningQueue = this.longRunningQueue;

    if (!standardQueue || !longRunningQueue) {
      this.logger.debug("Salesforce Request Queue destroyed before initialization");
      return;
    }

    this.logger.log("Shutting down Salesforce Request Queue", {
      standardPending: standardQueue.pending,
      standardQueueSize: standardQueue.size,
      longRunningPending: longRunningQueue.pending,
      longRunningQueueSize: longRunningQueue.size,
    });

    // Wait for pending requests to complete (with timeout)
    try {
      await Promise.all([standardQueue.onIdle(), longRunningQueue.onIdle()]);
    } catch (error) {
      this.logger.warn("Some Salesforce requests may not have completed during shutdown", {
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  /**
   * Execute a Salesforce API request through the appropriate queue
   */
  async execute<T>(
    requestFn: () => Promise<T>,
    options: SalesforceRequestOptions = {}
  ): Promise<T> {
    const { standardQueue, longRunningQueue } = await this.ensureQueuesInitialized();
    // Check daily usage
    this.checkDailyUsage();

    const startTime = Date.now();
    const requestId = this.generateRequestId();
    const isLongRunning = options.isLongRunning || false;
    const queue = isLongRunning ? longRunningQueue : standardQueue;
    const label = this.normalizeLabel(options.label);

    try {
      const result = (await queue.add(
        async () => {
          this.metrics.totalRequests++;
          this.metrics.dailyApiUsage++;
          void this.persistDailyUsage();
          this.recordRouteStart(label);
          this.updateQueueMetrics();
          this.maybeWarnOnUsage();

          this.logger.debug("Executing Salesforce request", {
            requestId,
            isLongRunning,
            queueSize: queue.size,
            pending: queue.pending,
            priority: options.priority || 0,
            dailyUsage: this.metrics.dailyApiUsage,
            label,
          });

          const waitTime = Date.now() - startTime;
          this.recordWaitTime(waitTime);

          const executionStart = Date.now();

          try {
            const response = await this.executeWithRetry(requestFn, options);

            const executionTime = Date.now() - executionStart;
            this.recordExecutionTime(executionTime);
            this.metrics.completedRequests++;
            this.metrics.lastRequestTime = new Date();
            this.recordRouteSuccess(label);

            this.logger.debug("Salesforce request completed", {
              requestId,
              isLongRunning,
              waitTime,
              executionTime,
              totalTime: Date.now() - startTime,
              label,
            });

            return response;
          } catch (error) {
            const executionTime = Date.now() - executionStart;
            this.recordExecutionTime(executionTime);
            this.metrics.failedRequests++;
            this.metrics.lastErrorTime = new Date();
            this.recordRouteFailure(label);

            // Check if it's a rate limit error
            if (this.isRateLimitError(error)) {
              this.metrics.lastRateLimitTime = new Date();
              this.activateDegradeWindow("rate-limit");
              this.logger.warn("Salesforce rate limit encountered", {
                requestId,
                dailyUsage: this.metrics.dailyApiUsage,
                label,
              });
            }

            this.logger.error("Salesforce request failed", {
              requestId,
              isLongRunning,
              waitTime,
              executionTime,
              error: error instanceof Error ? error.message : String(error),
              label,
            });

            throw error;
          }
        },
        {
          priority: options.priority ?? 0,
        }
      )) as T;

      return result;
    } finally {
      this.updateQueueMetrics();
    }
  }

  /**
   * Execute high-priority Salesforce request (jumps queue)
   */
  async executeHighPriority<T>(
    requestFn: () => Promise<T>,
    options: SalesforceRequestOptions = {}
  ): Promise<T> {
    return this.execute(requestFn, {
      ...options,
      priority: options.priority ?? 10,
    });
  }

  /**
   * Execute long-running Salesforce request (uses separate queue)
   */
  async executeLongRunning<T>(
    requestFn: () => Promise<T>,
    options: SalesforceRequestOptions = {}
  ): Promise<T> {
    return this.execute(requestFn, {
      ...options,
      isLongRunning: true,
      timeout: options.timeout ?? 600000,
    });
  }

  /**
   * Get current queue metrics
   */
  getMetrics(): SalesforceQueueMetrics {
    this.updateQueueMetrics();
    const dailyUsagePercent =
      this.dailyApiLimit > 0 ? this.metrics.dailyApiUsage / this.dailyApiLimit : 0;

    return {
      ...this.metrics,
      dailyApiLimit: this.dailyApiLimit,
      dailyUsagePercent,
      routeBreakdown: this.getRouteMetricsSnapshot(),
      degradation: this.getDegradationState(),
    };
  }

  /**
   * Get queue health status
   */
  getHealthStatus(): {
    status: "healthy" | "degraded" | "unhealthy";
    queueSize: number;
    pendingRequests: number;
    errorRate: number;
    dailyUsagePercent: number;
    averageWaitTime: number;
  } {
    this.updateQueueMetrics();

    const errorRate =
      this.metrics.totalRequests > 0 ? this.metrics.failedRequests / this.metrics.totalRequests : 0;

    const effectiveDailyLimit = this.dailyApiLimit > 0 ? this.dailyApiLimit : 150000;
    const dailyUsagePercent =
      effectiveDailyLimit > 0 ? this.metrics.dailyApiUsage / effectiveDailyLimit : 0;

    let status: "healthy" | "degraded" | "unhealthy" = "healthy";

    // Adjusted thresholds for higher throughput (15 concurrent, 10 RPS)
    if (this.metrics.queueSize > 200 || errorRate > 0.1 || dailyUsagePercent > 0.9) {
      status = "unhealthy";
    } else if (this.metrics.queueSize > 80 || errorRate > 0.05 || dailyUsagePercent > 0.7) {
      status = "degraded";
    }

    return {
      status,
      queueSize: this.metrics.queueSize,
      pendingRequests: this.metrics.pendingRequests,
      errorRate,
      dailyUsagePercent,
      averageWaitTime: this.metrics.averageWaitTime,
    };
  }

  /**
   * Get daily API usage information
   */
  getDailyUsage(): {
    usage: number;
    resetTime: Date;
    hoursUntilReset: number;
    limit: number;
    usagePercent: number;
  } {
    const limit = this.dailyApiLimit;
    const usagePercent = limit > 0 ? this.metrics.dailyApiUsage / limit : 0;
    return {
      usage: this.metrics.dailyApiUsage,
      resetTime: this.dailyUsageResetTime,
      hoursUntilReset: Math.ceil(
        (this.dailyUsageResetTime.getTime() - Date.now()) / (1000 * 60 * 60)
      ),
      limit,
      usagePercent,
    };
  }

  /**
   * Clear the queues (emergency use only)
   */
  async clearQueues(): Promise<void> {
    const { standardQueue, longRunningQueue } = await this.ensureQueuesInitialized();

    this.logger.warn("Clearing Salesforce request queues", {
      standardQueueSize: standardQueue.size,
      standardPending: standardQueue.pending,
      longRunningQueueSize: longRunningQueue.size,
      longRunningPending: longRunningQueue.pending,
    });

    standardQueue.clear();
    longRunningQueue.clear();

    await Promise.all([standardQueue.onIdle(), longRunningQueue.onIdle()]);
  }

  private async executeWithRetry<T>(
    requestFn: () => Promise<T>,
    options: SalesforceRequestOptions
  ): Promise<T> {
    const maxAttempts = options.retryAttempts || 3;
    const baseDelay = options.retryDelay || 1000;

    let lastError: Error | undefined;

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      try {
        const result = await requestFn();
        return result;
      } catch (error) {
        lastError = error instanceof Error ? error : new Error(String(error));

        if (attempt === maxAttempts) {
          break;
        }

        // Special handling for rate limit errors
        let delay = baseDelay * Math.pow(2, attempt - 1);

        if (this.isRateLimitError(error)) {
          // Longer delay for rate limit errors
          delay = Math.max(delay, 30000); // At least 30 seconds
        }

        // Add jitter
        delay += Math.random() * 1000;

        this.logger.debug("Salesforce request failed, retrying", {
          attempt,
          maxAttempts,
          delay,
          isRateLimit: this.isRateLimitError(error),
          error: lastError.message,
        });

        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }

    throw lastError instanceof Error ? lastError : new Error(String(lastError));
  }

  private isRateLimitError(error: unknown): boolean {
    if (error instanceof Error) {
      const message = error.message.toLowerCase();
      return (
        message.includes("rate limit") ||
        message.includes("too many requests") ||
        message.includes("429") ||
        message.includes("request limit exceeded")
      );
    }
    return false;
  }

  private checkDailyUsage(): void {
    const now = new Date();

    // Reset daily usage if we've passed the reset time
    if (now >= this.dailyUsageResetTime) {
      this.metrics.dailyApiUsage = 0;
      this.dailyUsageResetTime = this.getNextDayReset();
      this.highestUsageWarningIssued = 0;
      this.degradeState = { until: null, reason: null };

      this.logger.log("Daily Salesforce API usage reset", {
        resetTime: this.dailyUsageResetTime,
        dailyApiLimit: this.dailyApiLimit,
      });
      void this.persistDailyUsage();
    }
  }

  private getNextDayReset(): Date {
    const tomorrow = new Date();
    tomorrow.setDate(tomorrow.getDate() + 1);
    tomorrow.setHours(0, 0, 0, 0);
    return tomorrow;
  }

  private resolveDailyApiLimit(): number {
    const configuredLimit = this.parseNumericConfig(this.configService.get("SF_DAILY_API_LIMIT"));
    if (configuredLimit && configuredLimit > 0) {
      return configuredLimit;
    }

    const userCount =
      this.parseNumericConfig(this.configService.get("SF_FULL_USER_COUNT")) ||
      this.parseNumericConfig(this.configService.get("SF_USERS")) ||
      50;

    return 100000 + userCount * 1000;
  }

  private parseNumericConfig(value: unknown): number | undefined {
    if (typeof value === "number" && Number.isFinite(value)) {
      return value;
    }
    if (typeof value === "string" && value.trim().length > 0) {
      const parsed = Number(value);
      if (!Number.isNaN(parsed)) {
        return parsed;
      }
    }
    return undefined;
  }

  private setupQueueListeners(): void {
    if (!this.standardQueue || !this.longRunningQueue) {
      return;
    }

    this.standardQueue.on("add", () => this.updateQueueMetrics());
    this.standardQueue.on("next", () => this.updateQueueMetrics());
    this.standardQueue.on("idle", () => {
      this.logger.debug("Salesforce standard queue is idle");
      this.updateQueueMetrics();
    });
    this.standardQueue.on("error", (error: unknown) => {
      this.logger.error("Salesforce standard queue error", {
        error: error instanceof Error ? error.message : String(error),
      });
    });

    this.longRunningQueue.on("add", () => this.updateQueueMetrics());
    this.longRunningQueue.on("next", () => this.updateQueueMetrics());
    this.longRunningQueue.on("idle", () => {
      this.logger.debug("Salesforce long-running queue is idle");
      this.updateQueueMetrics();
    });
    this.longRunningQueue.on("error", (error: unknown) => {
      this.logger.error("Salesforce long-running queue error", {
        error: error instanceof Error ? error.message : String(error),
      });
    });
  }

  private updateQueueMetrics(): void {
    if (!this.standardQueue || !this.longRunningQueue) {
      return;
    }

    this.clearDegradeWindowIfElapsed();

    this.metrics.queueSize = this.standardQueue.size + this.longRunningQueue.size;
    this.metrics.pendingRequests = this.standardQueue.pending + this.longRunningQueue.pending;

    // Calculate averages
    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;
    }

    this.maybeTriggerQueuePressure();
  }

  private recordWaitTime(waitTime: number): void {
    this.waitTimes.push(waitTime);
    if (this.waitTimes.length > this.maxMetricsHistory) {
      this.waitTimes.shift();
    }
  }

  private recordExecutionTime(executionTime: number): void {
    this.executionTimes.push(executionTime);
    if (this.executionTimes.length > this.maxMetricsHistory) {
      this.executionTimes.shift();
    }
  }

  private generateRequestId(): string {
    return `sf_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }

  private normalizeLabel(label?: string): string {
    if (!label) {
      return "generic";
    }
    const trimmed = label.trim();
    return trimmed.length > 0 ? trimmed : "generic";
  }

  private recordRouteStart(label: string): void {
    const metric = this.getOrCreateRouteMetric(label);
    metric.totalRequests += 1;
  }

  private recordRouteSuccess(label: string): void {
    const metric = this.getOrCreateRouteMetric(label);
    metric.lastSuccessTime = new Date();
  }

  private recordRouteFailure(label: string): void {
    const metric = this.getOrCreateRouteMetric(label);
    metric.failedRequests += 1;
    metric.lastErrorTime = new Date();
  }

  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;
  }

  private 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;
  }

  private maybeWarnOnUsage(): void {
    if (this.dailyApiLimit <= 0) {
      return;
    }

    const usagePercent = this.metrics.dailyApiUsage / this.dailyApiLimit;
    if (usagePercent >= 0.95) {
      this.activateDegradeWindow("usage-threshold");
    }

    const threshold = this.usageWarningLevels
      .slice()
      .reverse()
      .find(level => usagePercent >= level && level > this.highestUsageWarningIssued);

    if (threshold !== undefined) {
      this.highestUsageWarningIssued = threshold;
      this.logger.warn("Salesforce daily API usage approaching limit", {
        usage: this.metrics.dailyApiUsage,
        limit: this.dailyApiLimit,
        usagePercent,
      });
    }
  }

  getDegradationState(): SalesforceDegradationSnapshot {
    this.clearDegradeWindowIfElapsed();
    const usagePercent =
      this.dailyApiLimit > 0 ? this.metrics.dailyApiUsage / this.dailyApiLimit : 0;
    return {
      degraded: this.degradeState.until !== null,
      reason: this.degradeState.reason,
      cooldownExpiresAt: this.degradeState.until ?? undefined,
      usagePercent,
    };
  }

  isDegraded(): boolean {
    return this.getDegradationState().degraded;
  }

  shouldThrottleLowPriority(): boolean {
    return this.isDegraded();
  }

  private activateDegradeWindow(reason: "rate-limit" | "usage-threshold" | "queue-pressure"): void {
    if (this.rateLimitCooldownMs <= 0) {
      return;
    }

    const now = Date.now();
    const newUntil = new Date(now + this.rateLimitCooldownMs);
    const active = this.degradeState.until && now < this.degradeState.until.getTime();

    if (!active || this.degradeState.reason !== reason) {
      this.logger.warn("Salesforce circuit breaker engaged", {
        reason,
        cooldownMs: this.rateLimitCooldownMs,
        until: newUntil,
      });
    }

    this.degradeState = { until: newUntil, reason };
  }

  private clearDegradeWindowIfElapsed(): void {
    if (this.degradeState.until && Date.now() >= this.degradeState.until.getTime()) {
      this.degradeState = { until: null, reason: null };
    }
  }

  private maybeTriggerQueuePressure(): void {
    const queueSize = this.metrics.queueSize;
    const pending = this.metrics.pendingRequests;
    const avgWait = this.metrics.averageWaitTime;

    const sizeExceeded = queueSize >= this.queueSizeDegradeThreshold;
    const pendingExceeded = pending >= this.queuePendingDegradeThreshold;
    const waitExceeded = avgWait >= this.averageWaitDegradeMs;

    if (sizeExceeded || pendingExceeded || waitExceeded) {
      const now = Date.now();
      const queuePressureActive =
        this.degradeState.reason === "queue-pressure" &&
        this.degradeState.until !== null &&
        now < this.degradeState.until.getTime();

      if (!queuePressureActive) {
        this.logger.warn("Salesforce queue pressure detected", {
          queueSize,
          pending,
          averageWaitMs: Math.round(avgWait),
          sizeThreshold: this.queueSizeDegradeThreshold,
          pendingThreshold: this.queuePendingDegradeThreshold,
          waitThresholdMs: this.averageWaitDegradeMs,
        });
      }

      this.activateDegradeWindow("queue-pressure");
    }
  }

  private async restoreDailyUsageFromCache(): Promise<void> {
    try {
      const snapshot = await this.cache.get<SalesforceDailyUsageCacheEntry | null>(
        this.dailyUsageCacheKey
      );

      if (!snapshot) {
        await this.persistDailyUsage();
        return;
      }

      const resetTime =
        typeof snapshot.resetTime === "string" ? new Date(snapshot.resetTime) : null;
      const resetTimestamp = resetTime?.getTime();

      if (
        !resetTime ||
        resetTimestamp === undefined ||
        Number.isNaN(resetTimestamp) ||
        Date.now() >= resetTimestamp
      ) {
        this.metrics.dailyApiUsage = 0;
        this.dailyUsageResetTime = this.getNextDayReset();
        this.highestUsageWarningIssued = 0;
        await this.persistDailyUsage();
        return;
      }

      this.metrics.dailyApiUsage =
        typeof snapshot.usage === "number" && snapshot.usage >= 0 ? snapshot.usage : 0;
      this.dailyUsageResetTime = resetTime;
      await this.persistDailyUsage();
    } catch (error) {
      this.logger.warn("Failed to restore Salesforce daily usage snapshot", {
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  private async persistDailyUsage(): Promise<void> {
    try {
      const ttlSeconds = this.calculateDailyUsageTtlSeconds();
      await this.cache.set(
        this.dailyUsageCacheKey,
        {
          usage: this.metrics.dailyApiUsage,
          resetTime: this.dailyUsageResetTime.toISOString(),
        },
        ttlSeconds
      );
    } catch (error) {
      this.logger.warn("Failed to persist Salesforce daily usage snapshot", {
        error: error instanceof Error ? error.message : String(error),
      });
    }
  }

  private calculateDailyUsageTtlSeconds(): number {
    const millisUntilReset = this.dailyUsageResetTime.getTime() - Date.now();
    if (millisUntilReset <= 0) {
      return 60;
    }
    return Math.max(60, Math.ceil(millisUntilReset / 1000));
  }
}

interface SalesforceDailyUsageCacheEntry {
  usage: number;
  resetTime: string;
}
