const express = require('express');
const { getOdds } = require('../services/oddsService');
const { MARKET_MAP, ALLOWED_BOOKS } = require('../utils/oddsNormalizer');
const { createRateLimit } = require('../middleware/rateLimit');

const router = express.Router();
// Session 32 — public throttle. /odds hits PropLine/odds-api upstream on a
// cache miss, so it gets the tighter 30/min bucket. Independent per-IP
// bucket scoped to this router (createRateLimit allocates its own Map).
router.use(createRateLimit({ windowMs: 60_000, max: 30 }));

const VALID_STAT_TYPES = new Set(Object.values(MARKET_MAP));
const VALID_BOOKS = ALLOWED_BOOKS;

// NCAAB is in-season November through April
function isNcaabSeason() {
  const month = new Date().getUTCMonth() + 1; // 1-indexed
  return month >= 11 || month <= 4;
}

function validateQueryParams(query) {
  const errors = [];

  if (query.stat_type && !VALID_STAT_TYPES.has(query.stat_type)) {
    errors.push(`Invalid stat_type: ${query.stat_type}`);
  }

  if (query.book && !VALID_BOOKS.has(query.book)) {
    errors.push(`Invalid book: ${query.book}`);
  }

  return errors;
}

function filterProps(props, query) {
  let filtered = props;

  if (query.stat_type) {
    filtered = filtered.filter((p) => p.stat_type === query.stat_type);
  }

  if (query.player) {
    const search = query.player.toLowerCase();
    filtered = filtered.filter((p) => p.player.toLowerCase().includes(search));
  }

  if (query.book) {
    filtered = filtered.filter((p) => p.book === query.book);
  }

  return filtered;
}

// Group flat props into the response format: grouped by player+stat with nested lines
function groupProps(flatProps) {
  const grouped = {};

  for (const prop of flatProps) {
    const key = `${prop.player}::${prop.stat_type}::${prop.game_time}`;
    if (!grouped[key]) {
      grouped[key] = {
        player: prop.player,
        home_team: prop.home_team,
        away_team: prop.away_team,
        game_time: prop.game_time,
        stat_type: prop.stat_type,
        lines: [],
      };
    }
    grouped[key].lines.push({
      book: prop.book,
      line: prop.line,
      over_odds: prop.over_odds,
      under_odds: prop.under_odds,
      fetched_at: prop.fetched_at,
    });
  }

  return Object.values(grouped);
}

router.get('/nba', async (req, res) => {
  const errors = validateQueryParams(req.query);
  if (errors.length > 0) {
    return res.status(400).json({ error: errors.join('; ') });
  }

  try {
    const result = await getOdds('nba');
    const filtered = filterProps(result.props, req.query);
    const props = groupProps(filtered);

    if (result.stale) {
      res.set('X-VYNDR-Stale', 'true');
    }

    const response = {
      sport: 'nba',
      updated_at: result.updated_at,
      source: result.source,
      quota_remaining: result.quota_remaining,
      props,
    };

    if (result.movements && result.movements.length > 0) {
      response.movements = result.movements;
    }

    return res.json(response);
  } catch (err) {
    const status = err.statusCode || 500;
    return res.status(status).json({ error: err.message });
  }
});

// Session 14 — WNBA + MLB. Same pattern as /nba: validate query,
// fetch via cached oddsService, project to the {sport, props}
// envelope the Slate consumes. odds-api may return empty during
// off-season; we still return 200 with an empty `props` array so
// the Slate can render its empty-state UX.
function buildSportRoute(sport) {
  return async (req, res) => {
    const errors = validateQueryParams(req.query);
    if (errors.length > 0) {
      return res.status(400).json({ error: errors.join('; ') });
    }
    try {
      const result = await getOdds(sport);
      const filtered = filterProps(result.props || [], req.query);
      const props = groupProps(filtered);
      if (result.stale) res.set('X-VYNDR-Stale', 'true');
      return res.json({
        sport,
        updated_at: result.updated_at,
        source: result.source,
        quota_remaining: result.quota_remaining,
        props,
      });
    } catch (err) {
      const status = err.statusCode || 500;
      return res.status(status).json({ error: err.message });
    }
  };
}

router.get('/wnba', buildSportRoute('wnba'));
router.get('/mlb',  buildSportRoute('mlb'));

router.get('/ncaab', async (req, res) => {
  if (!isNcaabSeason()) {
    return res.json({
      sport: 'ncaab',
      updated_at: new Date().toISOString(),
      source: 'none',
      quota_remaining: null,
      props: [],
      message: 'NCAAB is off-season. Props return in November.',
    });
  }

  const errors = validateQueryParams(req.query);
  if (errors.length > 0) {
    return res.status(400).json({ error: errors.join('; ') });
  }

  try {
    const result = await getOdds('ncaab');
    const filtered = filterProps(result.props, req.query);
    const props = groupProps(filtered);

    if (result.stale) {
      res.set('X-VYNDR-Stale', 'true');
    }

    return res.json({
      sport: 'ncaab',
      updated_at: result.updated_at,
      source: result.source,
      quota_remaining: result.quota_remaining,
      props,
    });
  } catch (err) {
    const status = err.statusCode || 500;
    return res.status(status).json({ error: err.message });
  }
});

// Session 7j — soccer odds route. League is a path segment so each
// league has its own cache key (`odds:soccer_wc:2026-06-15` etc.) and
// queries don't cross-pollute. Falls through to getOdds → odds-api on
// demand; cached 15min like every other sport.
const { SOCCER_SPORT_KEYS } = require('../services/oddsService');
const SOCCER_KEY_SET = new Set(SOCCER_SPORT_KEYS);

router.get('/soccer/:league', async (req, res) => {
  const leagueKey = `soccer_${String(req.params.league || '').toLowerCase()}`;
  if (!SOCCER_KEY_SET.has(leagueKey)) {
    return res.status(400).json({
      error: `Unknown soccer league. Valid: ${SOCCER_SPORT_KEYS.map((k) => k.replace('soccer_', '')).join(', ')}.`,
    });
  }
  const errors = validateQueryParams(req.query);
  if (errors.length > 0) {
    return res.status(400).json({ error: errors.join('; ') });
  }
  try {
    const result = await getOdds(leagueKey);
    const filtered = filterProps(result.props || [], req.query);
    const props = groupProps(filtered);

    if (result.stale) res.set('X-VYNDR-Stale', 'true');

    return res.json({
      sport: leagueKey,
      updated_at: result.updated_at,
      source: result.source,
      quota_remaining: result.quota_remaining,
      props,
    });
  } catch (err) {
    const status = err.statusCode || 500;
    return res.status(status).json({ error: err.message });
  }
});

module.exports = router;
