/**
 * Bracket Simulator
 *
 * Wraps the existing Monte Carlo bracket simulator (app/services/bracket-simulator.ts)
 * to conform to the Simulator interface. Loads current participant Elo ratings
 * from participantExpectedValues and runs 100k simulations.
 *
 * The existing bracket simulator uses Elo ratings derived from futures odds
 * (via the probability engine pipeline). These should be imported via the
 * admin "Futures Odds" page before running simulation.
 */

import { database } from "~/database/context";
import { participantExpectedValues } from "~/database/schema";
import { eq } from "drizzle-orm";
import { simulateBracket } from "~/services/bracket-simulator";
import { convertFuturesToElo } from "~/services/probability-engine";
import type { Simulator, SimulationResult } from "./types";

export class BracketSimulator implements Simulator {
  async simulate(sportsSeasonId: string): Promise<SimulationResult[]> {
    const db = database();

    // Load all participants with their current EVs (which hold probability distributions)
    const evRows = await db
      .select({
        participantId: participantExpectedValues.participantId,
        probFirst: participantExpectedValues.probFirst,
        sourceOdds: participantExpectedValues.sourceOdds,
      })
      .from(participantExpectedValues)
      .where(eq(participantExpectedValues.sportsSeasonId, sportsSeasonId));

    if (evRows.length === 0) {
      throw new Error(
        `No participant EVs found for sports season ${sportsSeasonId}. ` +
        `Import futures odds first via Admin → Futures Odds.`
      );
    }

    // Build Elo ratings from source odds (American odds format) if available,
    // otherwise fall back to using probFirst as a proxy for win probability.
    let eloMap: Map<string, number>;

    const hasOdds = evRows.some((r) => r.sourceOdds !== null);

    if (hasOdds) {
      const oddsInput = evRows
        .filter((r) => r.sourceOdds !== null)
        .map((r) => ({ participantId: r.participantId, odds: r.sourceOdds ?? 0 }));
      eloMap = convertFuturesToElo(oddsInput);
    } else {
      // Fall back: treat probFirst (as %) as championship win probability,
      // convert to a rough Elo by mapping [min, max] prob → [1250, 1750]
      const probs = evRows.map((r) => parseFloat(r.probFirst));
      const minProb = Math.min(...probs);
      const maxProb = Math.max(...probs);
      const range = maxProb - minProb || 1;

      eloMap = new Map(
        evRows.map((r) => {
          const prob = parseFloat(r.probFirst);
          const normalised = (prob - minProb) / range;
          const elo = 1250 + normalised * 500;
          return [r.participantId, elo];
        })
      );
    }

    const teamsForSimulation = evRows
      .filter((r) => eloMap.has(r.participantId))
      .map((r) => ({
        participantId: r.participantId,
        elo: eloMap.get(r.participantId) ?? 1500,
      }));

    if (teamsForSimulation.length === 0) {
      throw new Error(`Could not build Elo ratings for sports season ${sportsSeasonId}.`);
    }

    const probMap = await simulateBracket(teamsForSimulation);

    return Array.from(probMap.entries()).map(([participantId, probs]) => ({
      participantId,
      probabilities: {
        probFirst: probs[0],
        probSecond: probs[1],
        probThird: probs[2],
        probFourth: probs[3],
        probFifth: probs[4],
        probSixth: probs[5],
        probSeventh: probs[6],
        probEighth: probs[7],
      },
      source: "bracket_monte_carlo",
    }));
  }
}
