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

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

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

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

const num = (v: string | number | bigint | null | undefined): number => {
  if (v === null || v === undefined) return 0;
  if (typeof v === "bigint") return Number(v);
  if (typeof v === "string") return Number(v);
  return 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, ajuste_puntos
    FROM drones_recorridos WHERE id = ${recorridoId}
  `;
  if (!r) return null;
  if (num(r.descalificado) > 0) {
    await prisma.$executeRaw`
      UPDATE drones_recorridos SET puntaje_final = NULL WHERE id = ${recorridoId}
    `;
    return null;
  }

  const pens = await prisma.$queryRaw<PenalizacionAplicadaRow[]>`
    SELECT t.puntos, p.cantidad
    FROM drones_penalizaciones p
    JOIN drones_penalizacion_tipos t ON t.id = p.tipo_id
    WHERE p.recorrido_id = ${recorridoId}
  `;
  const bons = await prisma.$queryRaw<BonificacionAplicadaRow[]>`
    SELECT t.puntos, b.cantidad
    FROM drones_bonificaciones b
    JOIN drones_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 tiempo = Number(num(r.tiempo_segundos));
  const ajuste = Number(num(r.ajuste_puntos));
  // Forzamos a Number en cada operando y redondeamos para evitar que
  // Prisma reciba un string (DECIMAL viene como string y JS hace concat).
  const puntaje = Number((tiempo + sumaPen - sumaBon + ajuste).toFixed(2));

  await prisma.$executeRaw`
    UPDATE drones_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 drones_recorridos WHERE descalificado = FALSE
  `;
  for (const r of rows) await recomputeScore(num(r.id));
  return rows.length;
}
