/**
 * Tank01 NBA adapter (Session 9) — RapidAPI-hosted, live in-game stats.
 *
 * Sits behind the same RAPID_API_KEY as `footApiAdapter`. Free tier
 * is 1,000 req/month — much more generous than FootApi's daily cap,
 * so we don't keep a tight in-process counter here; we lean on cache
 * TTLs to bound consumption.
 *
 * Env:
 *   RAPID_API_KEY     — shared RapidAPI marketplace key
 *   TANK01_NBA_HOST   — host (default `tank01-fantasy-stats.p.rapidapi.com`)
 *
 * TTL policy — box scores are special:
 *   - Mid-game (status not Final)   → 5 min cache so live stats refresh
 *   - Post-game (status Final)      → 24 h cache so we stop pulling
 *   - Games-for-date                → 1 h
 *   - Betting odds                  → 15 min
 *
 * Wired into computeFeatures via Session 9 cascade — Tank01 box-score
 * data is preferred when ESPN's scoreboard is sparse (e.g. early in
 * the day before tip-off has populated minute-by-minute stats).
 */

const axios = require('axios');
const { cacheGet, cacheSet } = require('../../utils/redis');
// Session 21 — every Tank01 hit flows through the provider gateway
// so the monthly counter advances and the WARN/BLOCK thresholds
// catch us before RapidAPI 429s. The cache layer below still
// short-circuits before the gateway in the common path.
const gateway = require('../providerGateway');

const HTTP_TIMEOUT_MS = 8_000;
const DEFAULT_HOST = 'tank01-fantasy-stats.p.rapidapi.com';

const TTL = Object.freeze({
  boxScoreLive:  5 * 60,        // 5min while in-game
  boxScoreFinal: 24 * 3600,     // 24h once final
  games:         1 * 3600,      // 1h
  odds:          15 * 60,       // 15min
});

function getHost() {
  return process.env.TANK01_NBA_HOST || DEFAULT_HOST;
}

function hasApiKey() {
  return !!process.env.RAPID_API_KEY;
}

// Centralized fetch — RapidAPI auth + cache + stale-while-revalidate.
async function fetchWithCache(path, cacheKey, ttl) {
  const fresh = await cacheGet(cacheKey);
  if (fresh !== null) return fresh;
  if (!hasApiKey()) return null;

  try {
    const host = getHost();
    const res = await gateway.fetch(
      'tank01',
      () => axios.get(`https://${host}${path}`, {
        headers: {
          'x-rapidapi-key': process.env.RAPID_API_KEY,
          'x-rapidapi-host': host,
        },
        timeout: HTTP_TIMEOUT_MS,
      }),
      { capability: 'box_scores', sport: 'nba' },
    );
    const body = res.data;
    if (body && typeof body === 'object') {
      await cacheSet(cacheKey, body, ttl);
      await cacheSet(`${cacheKey}:stale`, body, ttl * 4);
    }
    return body;
  } catch (err) {
    console.warn('[tank01NBA] fetch failed:', path, err.message);
    const stale = await cacheGet(`${cacheKey}:stale`);
    if (stale !== null) return stale;
    return null;
  }
}

/**
 * getNBABoxScore — live per-player stats for a single game.
 *
 * Status-aware caching: a mid-game refresh that pulls "in-progress"
 * data caches for 5min so the next read isn't stale; a "Final" pull
 * caches for 24h because nothing is changing. The status field varies
 * by Tank01 schema version — we check `gameStatus` first, fall back
 * to looking for "Final" anywhere in a top-level string field.
 */
async function getNBABoxScore(gameId) {
  if (!gameId) return null;
  // We don't yet know if the game is final, so request with the live
  // TTL — if the response reports Final we re-cache under the long TTL.
  const cacheKey = `tank01:nba:boxscore:${gameId}`;
  const data = await fetchWithCache(
    `/getNBABoxScore?gameID=${encodeURIComponent(gameId)}`,
    cacheKey,
    TTL.boxScoreLive,
  );
  if (data === null) return null;
  const body = data?.body || data;
  const isFinal = (() => {
    const s = body?.gameStatus || body?.status || '';
    return typeof s === 'string' && /final/i.test(s);
  })();
  // Upgrade the cache TTL on Final.
  if (isFinal) {
    await cacheSet(cacheKey, data, TTL.boxScoreFinal);
  }
  // Project: top-level `playerStats` is a map keyed by playerID.
  const players = body?.playerStats || body?.playerStatistics || {};
  const out = [];
  for (const [id, p] of Object.entries(players)) {
    out.push({
      playerId: id,
      name: p.longName || p.shortName || null,
      team: p.teamAbv || null,
      mins: p.mins ?? null,
      pts: numOr(p.pts),
      reb: numOr(p.reb),
      ast: numOr(p.ast),
      stl: numOr(p.stl),
      blk: numOr(p.blk),
      tov: numOr(p.TOV ?? p.tov),
      threes: numOr(p.tptfgm),
      fga: numOr(p.fga),
      fgm: numOr(p.fgm),
      fta: numOr(p.fta),
      ftm: numOr(p.ftm),
      _final: isFinal,
    });
  }
  return out;
}

function numOr(v, fallback = 0) {
  if (v == null) return fallback;
  const n = Number(v);
  return Number.isFinite(n) ? n : fallback;
}

/**
 * getNBAGamesForDate — schedule + scoreline. Date format is YYYYMMDD
 * per Tank01's schema (NOT ISO).
 */
async function getNBAGamesForDate(date) {
  if (!date) return null;
  const ymd = String(date).replace(/-/g, '');
  const data = await fetchWithCache(
    `/getNBAGamesForDate?gameDate=${ymd}`,
    `tank01:nba:games:${ymd}`,
    TTL.games,
  );
  if (data === null) return null;
  const list = data?.body;
  if (!Array.isArray(list)) return [];
  return list.map((g) => ({
    gameId: g.gameID ?? null,
    homeTeam: g.home ?? null,
    awayTeam: g.away ?? null,
    gameTime: g.gameTime_epoch ?? g.gameTime ?? null,
    gameStatus: g.gameStatus ?? null,
    homeScore: numOr(g.homePts, null),
    awayScore: numOr(g.awayPts, null),
  }));
}

/**
 * getNBABettingOdds — Tank01's odds feed (book-by-book). Useful as a
 * sanity-check signal alongside our odds-api primary.
 */
async function getNBABettingOdds(date) {
  if (!date) return null;
  const ymd = String(date).replace(/-/g, '');
  const data = await fetchWithCache(
    `/getNBABettingOdds?gameDate=${ymd}`,
    `tank01:nba:odds:${ymd}`,
    TTL.odds,
  );
  if (data === null) return null;
  return data?.body || data;
}

module.exports = {
  getNBABoxScore,
  getNBAGamesForDate,
  getNBABettingOdds,
  hasApiKey,
  __internals: {
    TTL,
    DEFAULT_HOST,
    getHost,
    numOr,
  },
};
