/**
 * RIE — Signal Collector
 *
 * Parallel collection of all applicable signals for a matchup.
 * Each signal runs independently with its own timeout.
 * Failed signals are marked unavailable (graceful degradation).
 */

import { SignalResult, MatchupContext } from './types';
import { getSignalRegistry } from './signal-registry';

const PER_SIGNAL_TIMEOUT_MS = 8000;

async function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {
  return new Promise<T>((resolve, reject) => {
    const timer = setTimeout(() => reject(new Error(`Signal ${label} timed out after ${ms}ms`)), ms);
    promise.then(
      val => { clearTimeout(timer); resolve(val); },
      err => { clearTimeout(timer); reject(err); },
    );
  });
}

/**
 * Collect all applicable signals for a matchup in parallel.
 */
export async function collectSignals(ctx: MatchupContext): Promise<SignalResult[]> {
  const registry = getSignalRegistry();
  const applicableSignals = registry.getForLeague(ctx.league);

  if (applicableSignals.length === 0) {
    return [];
  }

  const settled = await Promise.allSettled(
    applicableSignals.map(signal =>
      withTimeout(signal.collect(ctx), PER_SIGNAL_TIMEOUT_MS, signal.id)
    )
  );

  return settled.map((result, i) => {
    if (result.status === 'fulfilled') {
      return result.value;
    }
    // Signal failed — return unavailable placeholder
    return {
      signalId: applicableSignals[i].id,
      score: 0.5,
      weight: 0,
      available: false,
      rawData: { error: result.reason?.message || 'Unknown error' },
      metadata: {
        latencyMs: PER_SIGNAL_TIMEOUT_MS,
        source: 'cache' as const,
        freshness: '',
      },
    };
  });
}
