import { Injectable } from "@nestjs/common";
import { CacheService } from "@bff/infra/cache/cache.service.js";
import { CacheStrategyBase } from "@bff/infra/cache/cache-strategy.base.js";
import type { CacheBucketMetrics } from "@bff/infra/cache/cache.types.js";
import type { OrderDetails, OrderSummary } from "@customer-portal/domain/orders";

interface OrdersCacheMetrics {
  summaries: CacheBucketMetrics;
  details: CacheBucketMetrics;
  invalidations: number;
}

/**
 * Orders cache service
 *
 * Uses CDC (Change Data Capture) for real-time cache invalidation.
 * No TTL - cache entries persist until explicitly invalidated by CDC events.
 *
 * Features:
 * - Request coalescing: Prevents thundering herd on cache miss
 * - Granular invalidation: Account-level and order-level invalidation
 * - Metrics tracking: Monitors hits, misses, and invalidations
 */
@Injectable()
export class OrdersCacheService extends CacheStrategyBase<"summaries" | "details"> {
  constructor(cache: CacheService) {
    super(cache, {
      summaries: { ttlSeconds: null },
      details: { ttlSeconds: null, allowNull: true },
    });
  }

  /**
   * Get order summaries for an account (CDC-driven cache)
   */
  async getOrderSummaries(
    sfAccountId: string,
    fetcher: () => Promise<OrderSummary[]>
  ): Promise<OrderSummary[]> {
    const key = this.buildAccountKey(sfAccountId);
    return this.getOrSet("summaries", key, fetcher);
  }

  /**
   * Get order details by ID (CDC-driven cache)
   */
  async getOrderDetails(
    orderId: string,
    fetcher: () => Promise<OrderDetails | null>
  ): Promise<OrderDetails | null> {
    const key = this.buildOrderKey(orderId);
    return this.getOrSet("details", key, fetcher);
  }

  /**
   * Invalidate all cached orders for an account
   */
  async invalidateAccountOrders(sfAccountId: string): Promise<void> {
    const key = this.buildAccountKey(sfAccountId);
    await this.invalidate(key);
  }

  /**
   * Invalidate cached order details
   */
  async invalidateOrder(orderId: string): Promise<void> {
    const key = this.buildOrderKey(orderId);
    await this.invalidate(key);
  }

  /**
   * Get cache metrics for monitoring
   */
  getMetrics(): OrdersCacheMetrics {
    return {
      summaries: this.getBucketMetrics("summaries"),
      details: this.getBucketMetrics("details"),
      invalidations: this.getInvalidationCount(),
    };
  }

  private buildAccountKey(sfAccountId: string): string {
    return `orders:account:${sfAccountId}`;
  }

  private buildOrderKey(orderId: string): string {
    return `orders:detail:${orderId}`;
  }
}
