/**
 * UnifiedOddsProvider — orchestrator.
 *
 * Fan-out to every adapter via Promise.allSettled, normalize, attach
 * cross-source signals, run processing engines, return a structured
 * `sources` array describing what contributed.
 *
 * Never throws. Adapter failures are surfaced per-source so the API caller
 * can see exactly which upstreams contributed to a given refresh.
 */

const espn = require('./adapters/ESPNAdapter');
const pinnacle = require('./adapters/PinnacleAdapter');
const draftkings = require('./adapters/DraftKingsAdapter');
const fanduel = require('./adapters/FanDuelAdapter');
const betmgm = require('./adapters/BetMGMAdapter');
const caesars = require('./adapters/CaesarsAdapter');
const prizepicks = require('./adapters/PrizePicksAdapter');
const covers = require('./adapters/CoversAdapter');
const rotowire = require('./adapters/RotowireAdapter');

const lineShopping = require('./processing/LineShoppingEngine');
const middles = require('./processing/MiddlesDetector');
const ev = require('./processing/EVCalculator');

const { shouldCollect, isActiveSport } = require('../config/sports');
const rateLimiter = require('./rateLimiter');
const breaker = require('./circuitBreaker');

const ADAPTERS = [espn, pinnacle, draftkings, fanduel, betmgm, caesars, prizepicks, covers, rotowire];

function settleToResult(name, settled) {
  if (settled.status === 'fulfilled') {
    const value = settled.value;
    const count = Array.isArray(value) ? value.length : (value?.projections?.length ?? 0);
    return { source: name, ok: true, count };
  }
  const err = settled.reason;
  return {
    source: name,
    ok: false,
    error: err?.code === 'NOT_IMPLEMENTED' ? 'not_implemented'
         : err?.code === 'BREAKER_OPEN'    ? 'breaker_open'
         : err?.code === 'RATE_LIMIT_TIMEOUT' ? 'rate_limited'
         : (err?.message || 'unknown'),
  };
}

async function fullRefresh(sport, { gradedProps = [] } = {}) {
  if (!shouldCollect(sport)) {
    return {
      sport,
      collected: false,
      reason: 'sport not in collection set',
      sources: [],
      data: { games: [], props: [], shopped: [], middles: [] },
      refreshed_at: new Date().toISOString(),
    };
  }

  const results = await Promise.allSettled([
    espn.getGames(sport),
    pinnacle.getGames(sport),
    draftkings.getPlayerProps(sport),
    fanduel.getPlayerProps(sport),
    betmgm.getPlayerProps(sport),
    caesars.getPlayerProps(sport),
    prizepicks.getPlayerProps(sport),
    covers.getConsensus?.(sport) ?? Promise.resolve([]),
    rotowire.getProjections(sport),
  ]);

  const sources = [
    settleToResult('espn',       results[0]),
    settleToResult('pinnacle',   results[1]),
    settleToResult('draftkings', results[2]),
    settleToResult('fanduel',    results[3]),
    settleToResult('betmgm',     results[4]),
    settleToResult('caesars',    results[5]),
    settleToResult('prizepicks', results[6]),
    settleToResult('covers',     results[7]),
    settleToResult('rotowire',   results[8]),
  ];

  const games = results[0].status === 'fulfilled' ? results[0].value : [];

  // Merge every adapter's player-prop payload (those that returned arrays).
  const props = [];
  for (const idx of [2, 3, 4, 5, 6]) {
    if (results[idx].status === 'fulfilled' && Array.isArray(results[idx].value)) {
      props.push(...results[idx].value);
    }
  }

  const shopped = lineShopping.process(props);
  const middlesFound = middles.detect(shopped);

  // EV labels for any already-graded props the caller fed in.
  const evRows = (gradedProps || []).map((g) => ({
    key: g.key,
    grade: g.grade,
    odds: g.odds,
    ev: ev.calculate({ grade: g.grade, odds: g.odds }),
  }));

  return {
    sport,
    active: isActiveSport(sport),
    sources,
    data: {
      games,
      props,
      shopped,
      middles: middlesFound,
      ev: evRows,
    },
    refreshed_at: new Date().toISOString(),
  };
}

function status() {
  return {
    rate_limiters: rateLimiter.snapshot(),
    breakers: breaker.snapshot(),
    adapters: ADAPTERS.map((a) => a.name),
  };
}

module.exports = { fullRefresh, status };
