/**
 * Cache Coordinator Service
 *
 * Provides utilities for coordinating multiple cache invalidation operations.
 * Executes operations in parallel and aggregates errors without throwing.
 *
 * Features:
 * - Parallel execution via Promise.allSettled()
 * - Error aggregation (logs but never throws)
 * - Metrics tracking for observability
 *
 * Usage:
 * Domain services inject this coordinator along with their specific cache services,
 * then use `invalidateMultiple()` to coordinate invalidations.
 *
 * Example:
 * ```typescript
 * await this.cacheCoordinator.invalidateMultiple([
 *   { name: 'order', execute: () => this.ordersCache.invalidateOrder(id) },
 *   { name: 'accountOrders', execute: () => this.ordersCache.invalidateAccountOrders(accountId) },
 * ]);
 * ```
 */

import { Injectable, Inject } from "@nestjs/common";
import { Logger } from "nestjs-pino";
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
import type {
  CacheOperation,
  CacheInvalidationResult,
  CacheInvalidationError,
  CacheCoordinatorMetrics,
} from "./cache.types.js";

@Injectable()
export class CacheCoordinatorService {
  private readonly metrics: CacheCoordinatorMetrics = {
    totalInvalidations: 0,
    totalOperations: 0,
    totalErrors: 0,
  };

  constructor(@Inject(Logger) private readonly logger: Logger) {}

  /**
   * Execute multiple cache operations in parallel.
   *
   * All operations are executed concurrently using Promise.allSettled().
   * Errors are logged but never thrown - cache invalidation failures
   * should not block business operations.
   *
   * @param operations - Array of cache operations to execute
   * @returns Result containing success status, counts, and any errors
   */
  async invalidateMultiple(operations: CacheOperation[]): Promise<CacheInvalidationResult> {
    return this.executeOperations(operations);
  }

  /**
   * Get coordinator metrics for monitoring
   */
  getMetrics(): CacheCoordinatorMetrics {
    return { ...this.metrics };
  }

  // ============================================================================
  // Private Helpers
  // ============================================================================

  private async executeOperations(operations: CacheOperation[]): Promise<CacheInvalidationResult> {
    if (operations.length === 0) {
      return {
        success: true,
        attempted: 0,
        succeeded: 0,
        failed: 0,
        errors: [],
      };
    }

    const results = await Promise.allSettled(operations.map(async op => op.execute()));

    const errors: CacheInvalidationError[] = [];
    let succeeded = 0;

    for (const [i, result] of results.entries()) {
      const operation = operations[i];
      if (!operation || !result) continue;

      if (result.status === "fulfilled") {
        succeeded++;
      } else {
        const errorMessage = extractErrorMessage(result.reason);
        errors.push({
          operation: operation.name,
          error: result.reason,
          message: errorMessage,
        });

        this.logger.warn("Cache invalidation operation failed", {
          operation: operation.name,
          error: errorMessage,
        });
      }
    }

    // Update metrics
    this.metrics.totalInvalidations++;
    this.metrics.totalOperations += operations.length;
    this.metrics.totalErrors += errors.length;

    const success = errors.length === 0;

    if (success && operations.length > 0) {
      this.logger.debug("Cache invalidation completed", {
        operations: operations.length,
      });
    }

    return {
      success,
      attempted: operations.length,
      succeeded,
      failed: errors.length,
      errors,
    };
  }
}
