'use strict';

/**
 * PropLine adapter (Session 30).
 *
 * PropLine returns The-Odds-API-COMPATIBLE responses (an array of game
 * objects, each with `bookmakers[].markets[].outcomes[]` carrying
 * name/description/price/point). So this adapter is THIN: fetch + hand the
 * raw array to the shared `oddsNormalizer` — no bespoke parsing.
 *
 * Differences from The Odds API:
 *   - Auth: `?apiKey=` query param (not x-api-key header)
 *   - Base: https://api.prop-line.com/v1/sports
 *   - THREE free keys rotate for 3,000 req/day combined (1,000 each)
 *   - Sport keys match odds-api (baseball_mlb, basketball_nba, …)
 *
 * Two layers of quota:
 *   - Gateway/quotaTracker counts TOTAL propline calls (3,000/day cap).
 *   - This adapter rotates which PHYSICAL key serves each call so no
 *     single key exceeds its 1,000/day. Per-key usage is tracked in Redis
 *     (`propline:usage:{i}:{utcDate}`), with an in-memory fallback.
 */

const axios = require('axios');
const gateway = require('../providerGateway');
const { normalizeProps, extractSpreads } = require('../../utils/oddsNormalizer');
const { getRedisClient, isDegraded } = require('../../utils/redis');

const BASE = 'https://api.prop-line.com/v1/sports';
const HTTP_TIMEOUT_MS = 10_000;
const PER_KEY_DAILY_LIMIT = 1000;
const ROTATE_THRESHOLD = 900; // rotate off a key once it hits 90%

// Internal sport → PropLine sport key (mirrors oddsService.SPORT_KEYS).
const SPORT_KEYS = {
  nba: 'basketball_nba',
  wnba: 'basketball_wnba',
  mlb: 'baseball_mlb',
  nfl: 'football_nfl',
  nhl: 'hockey_nhl',
  ncaab: 'basketball_ncaab',
};

// Markets to request per sport (comma-joined). Spreads requested too so
// extractSpreads has data.
const MARKETS = {
  nba: ['player_points', 'player_rebounds', 'player_assists', 'player_threes', 'player_blocks', 'player_steals'],
  wnba: ['player_points', 'player_rebounds', 'player_assists', 'player_threes'],
  // Session 56 audit — expanded from 6 to 12 markets. runs/walks/earned_runs/
  // hits_allowed were already fully supported downstream (whitelist + features +
  // outcomes) but never requested; doubles/outs are wired this session. All ride
  // the SAME request (no extra quota) → materially more graded props per slate.
  mlb: ['batter_hits', 'batter_home_runs', 'batter_total_bases', 'batter_rbis', 'batter_stolen_bases', 'batter_runs', 'batter_walks', 'batter_doubles', 'pitcher_strikeouts', 'pitcher_earned_runs', 'pitcher_hits_allowed', 'pitcher_outs'],
  nfl: ['player_pass_yds', 'player_rush_yds', 'player_reception_yds', 'player_receptions', 'player_anytime_td', 'player_pass_tds'],
  nhl: ['player_goals', 'player_shots_on_goal', 'goalie_saves'],
  ncaab: ['player_points', 'player_rebounds', 'player_assists'],
};

// In-memory fallback when Redis is unavailable (resets on process restart;
// acceptable — Redis is the real counter in production).
const memUsage = {};

function utcDate() {
  return new Date().toISOString().split('T')[0];
}

function getKeys() {
  return [
    process.env.PROPLINE_API_KEY_1,
    process.env.PROPLINE_API_KEY_2,
    process.env.PROPLINE_API_KEY_3,
  ].map((k) => (k && k.trim() ? k.trim() : null));
}

function hasKeys() {
  return getKeys().some(Boolean);
}

function usageKey(i) {
  return `propline:usage:${i}:${utcDate()}`;
}

async function getUsage(i) {
  if (!(isDegraded && isDegraded())) {
    try {
      const redis = getRedisClient();
      if (redis && typeof redis.get === 'function') {
        const v = await redis.get(usageKey(i));
        if (v != null) return parseInt(v, 10) || 0;
      }
    } catch { /* fall through to memory */ }
  }
  return memUsage[usageKey(i)] || 0;
}

async function incrUsage(i) {
  const key = usageKey(i);
  memUsage[key] = (memUsage[key] || 0) + 1;
  if (isDegraded && isDegraded()) return;
  try {
    const redis = getRedisClient();
    if (redis && typeof redis.incr === 'function') {
      const n = await redis.incr(key);
      if (n === 1 && typeof redis.expire === 'function') await redis.expire(key, 36 * 3600);
    }
  } catch { /* memory already incremented */ }
}

/**
 * Pick the key index with the MOST remaining capacity (least used) that is
 * present and under the rotate threshold. Returns null when every present
 * key is at/over the per-key limit (gateway then falls through to backup).
 */
async function pickKey(keys) {
  let best = null;
  for (let i = 0; i < keys.length; i += 1) {
    if (!keys[i]) continue;
    const used = await getUsage(i);
    if (used >= PER_KEY_DAILY_LIMIT) continue;
    const remaining = PER_KEY_DAILY_LIMIT - used;
    // Prefer keys under the rotate threshold; among those, most remaining.
    const score = used < ROTATE_THRESHOLD ? remaining + PER_KEY_DAILY_LIMIT : remaining;
    if (best === null || score > best.score) best = { index: i, key: keys[i], score };
  }
  return best;
}

function buildUrl(sportKey) {
  return `${BASE}/${sportKey}/odds`;
}

/**
 * Fetch the raw PropLine game array for a sport. Returns null when the
 * sport is unsupported, no keys exist, or every key is exhausted —
 * letting the caller fall through to the backup provider.
 */
async function fetchRaw(sport) {
  const sportKey = SPORT_KEYS[sport];
  if (!sportKey) return null;
  const keys = getKeys();
  if (!keys.some(Boolean)) return null;

  const picked = await pickKey(keys);
  if (!picked) return null; // all keys exhausted today

  const markets = (MARKETS[sport] || []).join(',');
  const url = buildUrl(sportKey);

  const res = await gateway.fetch(
    'propline',
    () => axios.get(url, {
      params: { apiKey: picked.key, ...(markets ? { markets } : {}) },
      timeout: HTTP_TIMEOUT_MS,
    }),
    { capability: 'props', sport },
  );
  await incrUsage(picked.index);

  const body = res && res.data;
  if (Array.isArray(body)) return body;
  // PropLine occasionally wraps in { data: [...] } — tolerate it.
  if (Array.isArray(body && body.data)) return body.data;
  return [];
}

/**
 * Fetch + normalize props for a sport. Returns { props, spreads, source }
 * on success, or null on failure / no data (caller falls back).
 */
async function getProps(sport) {
  try {
    const raw = await fetchRaw(sport);
    if (!Array.isArray(raw)) return null;
    const props = normalizeProps(raw);
    const spreads = extractSpreads(raw);
    return { props, spreads, source: 'propline' };
  } catch (err) {
    console.warn('[propline] getProps failed:', err.message);
    return null;
  }
}

module.exports = {
  getProps,
  fetchRaw,
  hasKeys,
  pickKey,
  __internals: {
    SPORT_KEYS, MARKETS, PER_KEY_DAILY_LIMIT, ROTATE_THRESHOLD,
    getKeys, getUsage, incrUsage, utcDate, buildUrl, usageKey, memUsage,
  },
};
