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 { SupportCaseList, CaseMessageList } from "@customer-portal/domain/support";

/**
 * Cache TTL configuration for support cases
 *
 * Unlike orders (which use CDC events), support cases use TTL-based caching
 * because Salesforce Case changes don't trigger platform events in our setup.
 */
const CACHE_TTL = {
  /** Case list TTL: 2 minutes - customers checking status periodically */
  CASE_LIST: 120,
  /** Case messages TTL: 1 minute - fresher for active conversations */
  CASE_MESSAGES: 60,
} as const;

interface SupportCacheMetrics {
  caseList: CacheBucketMetrics;
  messages: CacheBucketMetrics;
  invalidations: number;
}

/**
 * Support cases cache service
 *
 * Uses TTL-based caching (not CDC) because:
 * - Cases don't trigger platform events in our Salesforce setup
 * - Customers can refresh to see updates (no real-time requirement)
 * - Short TTLs (1-2 min) ensure reasonable freshness
 *
 * Features:
 * - Request coalescing: Prevents duplicate API calls on cache miss
 * - Write-through invalidation: Cache is cleared after customer adds comment
 * - Metrics tracking: Monitors hits, misses, and invalidations
 */
@Injectable()
export class SupportCacheService extends CacheStrategyBase<"caseList" | "messages"> {
  constructor(cache: CacheService) {
    super(cache, {
      caseList: { ttlSeconds: CACHE_TTL.CASE_LIST },
      messages: { ttlSeconds: CACHE_TTL.CASE_MESSAGES },
    });
  }

  /**
   * Get cached case list for an account
   */
  async getCaseList(
    sfAccountId: string,
    fetcher: () => Promise<SupportCaseList>
  ): Promise<SupportCaseList> {
    const key = this.buildCaseListKey(sfAccountId);
    return this.getOrSet("caseList", key, fetcher);
  }

  /**
   * Get cached messages for a case
   */
  async getCaseMessages(
    caseId: string,
    fetcher: () => Promise<CaseMessageList>
  ): Promise<CaseMessageList> {
    const key = this.buildMessagesKey(caseId);
    return this.getOrSet("messages", key, fetcher);
  }

  /**
   * Invalidate case list cache for an account
   * Called after customer creates a new case
   */
  async invalidateCaseList(sfAccountId: string): Promise<void> {
    const key = this.buildCaseListKey(sfAccountId);
    await this.invalidate(key);
  }

  /**
   * Invalidate messages cache for a case
   * Called after customer adds a comment
   */
  async invalidateCaseMessages(caseId: string): Promise<void> {
    const key = this.buildMessagesKey(caseId);
    await this.invalidate(key);
  }

  /**
   * Invalidate all caches for an account's cases
   * Called after any write operation to ensure fresh data
   */
  async invalidateAllForAccount(sfAccountId: string, caseId?: string): Promise<void> {
    await this.invalidateCaseList(sfAccountId);
    if (caseId) {
      await this.invalidateCaseMessages(caseId);
    }
  }

  /**
   * Get cache metrics for monitoring
   */
  getMetrics(): SupportCacheMetrics {
    return {
      caseList: this.getBucketMetrics("caseList"),
      messages: this.getBucketMetrics("messages"),
      invalidations: this.getInvalidationCount(),
    };
  }

  private buildCaseListKey(sfAccountId: string): string {
    return `support:cases:${sfAccountId}`;
  }

  private buildMessagesKey(caseId: string): string {
    return `support:messages:${caseId}`;
  }
}
