import { prisma } from "../../prisma.js";

type RecorridoRow = {
  id: number | bigint;
  tiempo_segundos: string | number;
  descalificado: number | bigint;
};

type AplicadaRow = {
  puntos: number | bigint;
  cantidad: number | bigint;
};

const num = (v: unknown): number => {
  if (v === null || v === undefined) return 0;
  // tiempo_segundos / puntaje vienen como Prisma.Decimal (objeto). Number()
  // lo convierte bien; sin esto, `Decimal + 0` concatena como string (×10).
  return Number(v);
};

/**
 * Puntaje = tiempo + Σ(penalización × cantidad) − Σ(bonificación × cantidad).
 * Menor gana.
 *
 * Descalificado → puntaje_final NULL (queda fuera del ranking).
 */
export async function recomputeScore(recorridoId: number): Promise<number | null> {
  const [r] = await prisma.$queryRaw<RecorridoRow[]>`
    SELECT id, tiempo_segundos, descalificado
    FROM todoterreno_recorridos WHERE id = ${recorridoId}
  `;
  if (!r) return null;
  if (num(r.descalificado) > 0) {
    await prisma.$executeRaw`
      UPDATE todoterreno_recorridos SET puntaje_final = NULL WHERE id = ${recorridoId}
    `;
    return null;
  }

  const pens = await prisma.$queryRaw<AplicadaRow[]>`
    SELECT t.puntos, p.cantidad
    FROM todoterreno_penalizaciones p
    JOIN todoterreno_penalizacion_tipos t ON t.id = p.tipo_id
    WHERE p.recorrido_id = ${recorridoId}
  `;
  const bons = await prisma.$queryRaw<AplicadaRow[]>`
    SELECT t.puntos, b.cantidad
    FROM todoterreno_bonificaciones b
    JOIN todoterreno_bonificacion_tipos t ON t.id = b.tipo_id
    WHERE b.recorrido_id = ${recorridoId}
  `;

  const sumaPen = pens.reduce((acc, p) => acc + num(p.puntos) * num(p.cantidad), 0);
  const sumaBon = bons.reduce((acc, b) => acc + num(b.puntos) * num(b.cantidad), 0);

  const puntaje = num(r.tiempo_segundos) + sumaPen - sumaBon;

  await prisma.$executeRaw`
    UPDATE todoterreno_recorridos SET puntaje_final = ${puntaje} WHERE id = ${recorridoId}
  `;
  return puntaje;
}

/**
 * Re-puntea todos los recorridos no descalificados.
 * Se usa cuando cambia un valor de catálogo y el usuario eligió "aplicar a registros existentes".
 */
export async function recomputeAllScores(): Promise<number> {
  const rows = await prisma.$queryRaw<{ id: number | bigint }[]>`
    SELECT id FROM todoterreno_recorridos WHERE descalificado = FALSE
  `;
  for (const r of rows) await recomputeScore(num(r.id));
  return rows.length;
}
