/**
 * Model for EV (Expected Value) Snapshots
 *
 * Historical record of participant EVs and team projected points over time.
 * One snapshot per participant (or team) per sport season per day — upsert on conflict.
 * Snapshots are taken directly from simulation output to avoid partial-capture issues.
 */

import { database } from "~/database/context";
import { participantEvSnapshots, teamEvSnapshots } from "~/database/schema";
import { eq, and, asc } from "drizzle-orm";

// ─── Participant EV Snapshots ─────────────────────────────────────────────────

export interface ParticipantEvSnapshotInput {
  participantId: string;
  sportsSeasonId: string;
  snapshotDate: string; // YYYY-MM-DD
  probFirst: number;
  probSecond: number;
  probThird: number;
  probFourth: number;
  probFifth: number;
  probSixth: number;
  probSeventh: number;
  probEighth: number;
  calculatedEV: number;
  source: string;
}

export interface ParticipantEvHistoryPoint {
  snapshotDate: string;
  calculatedEV: number;
  probFirst: number;
}

/**
 * Upsert a single participant EV snapshot.
 * If a snapshot already exists for this (participantId, sportsSeasonId, snapshotDate),
 * it will be overwritten with the new values.
 */
export async function upsertParticipantEvSnapshot(
  input: ParticipantEvSnapshotInput
): Promise<void> {
  const db = database();
  const now = new Date();

  await db
    .insert(participantEvSnapshots)
    .values({
      participantId: input.participantId,
      sportsSeasonId: input.sportsSeasonId,
      snapshotDate: input.snapshotDate,
      probFirst: input.probFirst.toString(),
      probSecond: input.probSecond.toString(),
      probThird: input.probThird.toString(),
      probFourth: input.probFourth.toString(),
      probFifth: input.probFifth.toString(),
      probSixth: input.probSixth.toString(),
      probSeventh: input.probSeventh.toString(),
      probEighth: input.probEighth.toString(),
      calculatedEV: input.calculatedEV.toString(),
      source: input.source,
      createdAt: now,
      updatedAt: now,
    })
    .onConflictDoUpdate({
      target: [
        participantEvSnapshots.participantId,
        participantEvSnapshots.sportsSeasonId,
        participantEvSnapshots.snapshotDate,
      ],
      set: {
        probFirst: input.probFirst.toString(),
        probSecond: input.probSecond.toString(),
        probThird: input.probThird.toString(),
        probFourth: input.probFourth.toString(),
        probFifth: input.probFifth.toString(),
        probSixth: input.probSixth.toString(),
        probSeventh: input.probSeventh.toString(),
        probEighth: input.probEighth.toString(),
        calculatedEV: input.calculatedEV.toString(),
        source: input.source,
        updatedAt: now,
      },
    });
}

/**
 * Batch upsert participant EV snapshots within a single transaction.
 * All snapshots succeed or all fail together.
 */
export async function batchUpsertParticipantEvSnapshots(
  inputs: ParticipantEvSnapshotInput[]
): Promise<void> {
  if (inputs.length === 0) return;

  const db = database();
  const now = new Date();

  await db.transaction(async (tx) => {
    for (const input of inputs) {
      await tx
        .insert(participantEvSnapshots)
        .values({
          participantId: input.participantId,
          sportsSeasonId: input.sportsSeasonId,
          snapshotDate: input.snapshotDate,
          probFirst: input.probFirst.toString(),
          probSecond: input.probSecond.toString(),
          probThird: input.probThird.toString(),
          probFourth: input.probFourth.toString(),
          probFifth: input.probFifth.toString(),
          probSixth: input.probSixth.toString(),
          probSeventh: input.probSeventh.toString(),
          probEighth: input.probEighth.toString(),
          calculatedEV: input.calculatedEV.toString(),
          source: input.source,
          createdAt: now,
          updatedAt: now,
        })
        .onConflictDoUpdate({
          target: [
            participantEvSnapshots.participantId,
            participantEvSnapshots.sportsSeasonId,
            participantEvSnapshots.snapshotDate,
          ],
          set: {
            probFirst: input.probFirst.toString(),
            probSecond: input.probSecond.toString(),
            probThird: input.probThird.toString(),
            probFourth: input.probFourth.toString(),
            probFifth: input.probFifth.toString(),
            probSixth: input.probSixth.toString(),
            probSeventh: input.probSeventh.toString(),
            probEighth: input.probEighth.toString(),
            calculatedEV: input.calculatedEV.toString(),
            source: input.source,
            updatedAt: now,
          },
        });
    }
  });
}

/**
 * Get EV history for a single participant in a sport season, ordered oldest first.
 * Used for EV trend charts.
 */
export async function getParticipantEvHistory(
  participantId: string,
  sportsSeasonId: string
): Promise<ParticipantEvHistoryPoint[]> {
  const db = database();

  const rows = await db
    .select({
      snapshotDate: participantEvSnapshots.snapshotDate,
      calculatedEV: participantEvSnapshots.calculatedEV,
      probFirst: participantEvSnapshots.probFirst,
    })
    .from(participantEvSnapshots)
    .where(
      and(
        eq(participantEvSnapshots.participantId, participantId),
        eq(participantEvSnapshots.sportsSeasonId, sportsSeasonId)
      )
    )
    .orderBy(asc(participantEvSnapshots.snapshotDate));

  return rows.map((r) => ({
    snapshotDate: r.snapshotDate,
    calculatedEV: parseFloat(r.calculatedEV),
    probFirst: parseFloat(r.probFirst),
  }));
}

// ─── Team EV Snapshots ────────────────────────────────────────────────────────

export interface TeamEvSnapshotInput {
  teamId: string;
  seasonId: string;
  snapshotDate: string; // YYYY-MM-DD
  projectedPoints: number;
  actualPoints: number;
}

export interface TeamEvHistoryPoint {
  snapshotDate: string;
  projectedPoints: number;
  actualPoints: number;
}

/**
 * Upsert a team EV snapshot for a given day.
 */
export async function upsertTeamEvSnapshot(
  input: TeamEvSnapshotInput
): Promise<void> {
  const db = database();
  const now = new Date();

  await db
    .insert(teamEvSnapshots)
    .values({
      teamId: input.teamId,
      seasonId: input.seasonId,
      snapshotDate: input.snapshotDate,
      projectedPoints: input.projectedPoints.toString(),
      actualPoints: input.actualPoints.toString(),
      createdAt: now,
    })
    .onConflictDoUpdate({
      target: [
        teamEvSnapshots.teamId,
        teamEvSnapshots.seasonId,
        teamEvSnapshots.snapshotDate,
      ],
      set: {
        projectedPoints: input.projectedPoints.toString(),
        actualPoints: input.actualPoints.toString(),
      },
    });
}

/**
 * Get projected points history for a team in a fantasy season, ordered oldest first.
 * Used for team trend charts.
 */
export async function getTeamEvHistory(
  teamId: string,
  seasonId: string
): Promise<TeamEvHistoryPoint[]> {
  const db = database();

  const rows = await db
    .select({
      snapshotDate: teamEvSnapshots.snapshotDate,
      projectedPoints: teamEvSnapshots.projectedPoints,
      actualPoints: teamEvSnapshots.actualPoints,
    })
    .from(teamEvSnapshots)
    .where(
      and(
        eq(teamEvSnapshots.teamId, teamId),
        eq(teamEvSnapshots.seasonId, seasonId)
      )
    )
    .orderBy(asc(teamEvSnapshots.snapshotDate));

  return rows.map((r) => ({
    snapshotDate: r.snapshotDate,
    projectedPoints: parseFloat(r.projectedPoints),
    actualPoints: parseFloat(r.actualPoints),
  }));
}
