/**
 * PropOdds — player-prop specialist. Consensus source #2 alongside SharpAPI.
 *
 * Strict free-tier monthly limits — use sparingly. Specialized for the exact
 * lane we live in: player props.
 *
 * When called: during grading, AFTER SharpAPI, to get a second consensus
 * data point. Three-way consensus (SharpAPI + PropOdds + OddsPapi) is a
 * stronger signal than two-way for the line-divergence trap.
 */

const axios = require('axios');
const { createLimiter, createCircuitBreaker } = require('../../utils/rateLimiter');
const { cacheGet, cacheSet } = require('../../utils/redis');
const { devig } = require('../../utils/odds');

const SOURCE = 'propodds';
const HTTP_TIMEOUT_MS = 10_000;
const CACHE_TTL_SECONDS = 90;        // odds are time-sensitive but rarer fetch
const STALE_CACHE_TTL_SECONDS = 300;

const BASE_URL = process.env.PROPODDS_BASE_URL || 'https://api.prop-odds.com/v1';

// 3 req/min — strict because the free monthly cap is low.
const limiter = createLimiter({ tokensPerInterval: 3, interval: 60_000 });
const breaker = createCircuitBreaker({ failureThreshold: 3, resetTimeout: 60_000 });

const SPORT_KEYS = Object.freeze({
  nba:    'nba',
  wnba:   'wnba',
  mlb:    'mlb',
  nfl:    'nfl',
  nhl:    'nhl',
  ncaab:  'ncaab',
  ncaafb: 'ncaaf',
});

function configured() {
  return !!process.env.PROPODDS_KEY;
}

function sportKey(sport) {
  const key = SPORT_KEYS[sport];
  if (!key) throw new Error(`Unsupported sport: ${sport}`);
  return key;
}

async function fetchWithGuards(url, params, cacheKey) {
  if (!configured()) return null;
  const cached = await cacheGet(cacheKey);
  if (cached && !cached.stale) return cached;

  await limiter.waitForToken();
  try {
    const data = await breaker.call(async () => {
      const res = await axios.get(url, {
        params: { ...params, api_key: process.env.PROPODDS_KEY },
        timeout: HTTP_TIMEOUT_MS,
        validateStatus: (s) => (s >= 200 && s < 300) || s === 429,
      });
      if (res.status === 429) {
        const err = new Error('propodds rate limited');
        err.code = 'PROPODDS_429';
        throw err;
      }
      return res.data;
    });
    await cacheSet(cacheKey, data, CACHE_TTL_SECONDS);
    return data;
  } catch (err) {
    if (err?.code === 'PROPODDS_429' && cached) {
      const stale = { ...cached, stale: true };
      await cacheSet(cacheKey, stale, STALE_CACHE_TTL_SECONDS);
      return stale;
    }
    if (err?.code === 'CIRCUIT_OPEN') return cached ? { ...cached, stale: true } : null;
    console.warn(`[${SOURCE}] fetch failed for ${cacheKey}:`, err?.message);
    return cached ? { ...cached, stale: true } : null;
  }
}

function normalizeProp(raw) {
  const overOdds = raw.over_odds ?? raw.over_price ?? null;
  const underOdds = raw.under_odds ?? raw.under_price ?? null;
  const fair = (overOdds != null && underOdds != null) ? devig(overOdds, underOdds) : null;
  return {
    book: raw.book ?? raw.bookmaker ?? null,
    player: raw.player ?? raw.player_name ?? null,
    statType: raw.market ?? raw.stat_type ?? null,
    line: Number(raw.line ?? raw.point ?? null),
    overOdds,
    underOdds,
    fairOver: fair?.fairOver ?? null,
    fairUnder: fair?.fairUnder ?? null,
  };
}

async function getPlayerProps(sport, gameId, player, statType) {
  const key = sportKey(sport);
  const cacheKey = `propodds:${sport}:${gameId}:${player || 'all'}:${statType || 'all'}`;
  const data = await fetchWithGuards(
    `${BASE_URL}/sports/${key}/games/${gameId}/odds`,
    { player, market: statType },
    cacheKey
  );
  if (!data) return [];
  const raw = data.props || data.markets || data.data || [];
  const normalized = Array.isArray(raw) ? raw.map(normalizeProp) : [];
  return data.stale ? Object.assign(normalized, { stale: true }) : normalized;
}

module.exports = {
  configured,
  getPlayerProps,
  __internals: { limiter, breaker, SPORT_KEYS, BASE_URL, normalizeProp },
};
