import { Router } from "express";
import { Prisma } from "@prisma/client";
import { z } from "zod";
import { prisma } from "../prisma.js";
import { requireAuth } from "../middleware/auth.js";
import { HttpError } from "../lib/http-error.js";

export const registroRouter = Router();

registroRouter.use(requireAuth);

const CATEGORIAS = [
  "STEM_JR",
  "STEM_SR",
  "ROBOFUT",
  "DRONES",
  "TODO_TERRENO",
] as const;

const diaSchema = z.coerce.number().int().refine((v) => v === 1 || v === 2, {
  message: "El día debe ser 1 o 2",
});

const listQuerySchema = z.object({
  dia: diaSchema,
  search: z.string().trim().max(120).optional(),
  categoria: z.enum(CATEGORIAS).optional(),
});

type RegistroRow = {
  crea_inscripcion_id: number;
  estudiante_id: number;
  nombre_completo: string;
  colegio: string | null;
  categoria: string;
  modalidad: string;
  telefono: string | null;
  kit_id: number | null;
  kit_codigo: string | null;
  nombre_robot: string | null;
  pareja_id: number | null;
  pareja_nombre: string | null;
  asistio_dia_1: number;
  asistio_dia_1_en: Date | null;
  asistio_dia_2: number;
  asistio_dia_2_en: Date | null;
};

// SELECT base reutilizable: una fila por crea_inscripcion (cada miembro de pareja
// tiene su propia fila, marcan asistencia por separado).
const SELECT_REGISTRO_ROW = Prisma.sql`
  SELECT
    ci.id                                        AS crea_inscripcion_id,
    e.id                                         AS estudiante_id,
    CONCAT_WS(' ', e.nombres, e.apellido_1, e.apellido_2) AS nombre_completo,
    col.nombre                                   AS colegio,
    ci.categoria_codigo                          AS categoria,
    ci.modalidad                                 AS modalidad,
    e.telefono                                   AS telefono,
    ci.kit_id                                    AS kit_id,
    k.codigo                                     AS kit_codigo,
    k.nombre_robot                               AS nombre_robot,
    ci.pareja_id                                 AS pareja_id,
    CASE WHEN ci.pareja_id IS NOT NULL
         THEN CONCAT_WS(' ', e2.nombres, e2.apellido_1)
         ELSE NULL END                           AS pareja_nombre,
    ci.asistio_dia_1                             AS asistio_dia_1,
    ci.asistio_dia_1_en                          AS asistio_dia_1_en,
    ci.asistio_dia_2                             AS asistio_dia_2,
    ci.asistio_dia_2_en                          AS asistio_dia_2_en
  FROM crea_inscripciones ci
  JOIN estudiantes e        ON e.id = ci.estudiante_id
  LEFT JOIN colegios col    ON col.id = e.colegio_id
  LEFT JOIN kits k          ON k.id = ci.kit_id
  LEFT JOIN parejas pa      ON pa.id = ci.pareja_id
  LEFT JOIN estudiantes e2  ON e2.id = CASE
                                  WHEN pa.estudiante_1_id = e.id THEN pa.estudiante_2_id
                                  ELSE pa.estudiante_1_id
                                END
`;

// ─── LISTADO COMPLETO ────────────────────────────────────────────────────────
registroRouter.get("/", async (req, res, next) => {
  try {
    const q = listQuerySchema.parse(req.query);

    const conditions: Prisma.Sql[] = [];
    if (q.categoria) {
      conditions.push(Prisma.sql`ci.categoria_codigo = ${q.categoria}`);
    }
    if (q.search) {
      const term = `%${q.search}%`;
      conditions.push(
        Prisma.sql`(CONCAT_WS(' ', e.nombres, e.apellido_1, e.apellido_2) LIKE ${term} OR e.correo LIKE ${term} OR k.codigo LIKE ${term} OR k.nombre_robot LIKE ${term})`
      );
    }
    const where =
      conditions.length > 0
        ? Prisma.sql`WHERE ${Prisma.join(conditions, " AND ")}`
        : Prisma.empty;

    const rows = await prisma.$queryRaw<RegistroRow[]>`
      ${SELECT_REGISTRO_ROW}
      ${where}
      ORDER BY nombre_completo
    `;

    res.json({
      dia: q.dia,
      data: rows.map(serialize),
    });
  } catch (err) {
    next(err);
  }
});

// ─── RESUMEN DE CARDS ────────────────────────────────────────────────────────
// Devuelve presentes/esperados global y por categoría para el día solicitado.
registroRouter.get("/summary", async (req, res, next) => {
  try {
    const q = z.object({ dia: diaSchema }).parse(req.query);
    const col = q.dia === 1 ? "asistio_dia_1" : "asistio_dia_2";

    const rows = await prisma.$queryRaw<
      { categoria: string; esperados: bigint; presentes: bigint }[]
    >(Prisma.sql`
      SELECT
        ci.categoria_codigo                   AS categoria,
        COUNT(*)                              AS esperados,
        SUM(CASE WHEN ci.${Prisma.raw(col)} = 1 THEN 1 ELSE 0 END) AS presentes
      FROM crea_inscripciones ci
      GROUP BY ci.categoria_codigo
    `);

    const por_categoria = rows.map((r) => ({
      categoria: r.categoria,
      esperados: Number(r.esperados),
      presentes: Number(r.presentes),
    }));

    const total = {
      esperados: por_categoria.reduce((a, b) => a + b.esperados, 0),
      presentes: por_categoria.reduce((a, b) => a + b.presentes, 0),
    };

    res.json({ dia: q.dia, total, por_categoria });
  } catch (err) {
    next(err);
  }
});

// ─── DETALLE DE UNA INSCRIPCIÓN (para la tarjeta tras búsqueda) ──────────────
registroRouter.get("/:id", async (req, res, next) => {
  try {
    const id = z.coerce.number().int().positive().parse(req.params.id);
    const rows = await prisma.$queryRaw<RegistroRow[]>`
      ${SELECT_REGISTRO_ROW}
      WHERE ci.id = ${id}
      LIMIT 1
    `;
    if (rows.length === 0) {
      throw new HttpError(404, "Inscripción no encontrada");
    }
    res.json({ inscripcion: serialize(rows[0]) });
  } catch (err) {
    next(err);
  }
});

// ─── MARCAR / DESMARCAR ASISTENCIA ───────────────────────────────────────────
const patchAsistenciaSchema = z.object({
  dia: diaSchema,
  asistio: z.boolean(),
});

registroRouter.patch("/:id/asistencia", async (req, res, next) => {
  try {
    const id = z.coerce.number().int().positive().parse(req.params.id);
    const body = patchAsistenciaSchema.parse(req.body);

    const colFlag = body.dia === 1 ? "asistio_dia_1" : "asistio_dia_2";
    const colTs = body.dia === 1 ? "asistio_dia_1_en" : "asistio_dia_2_en";

    const existing = await prisma.crea_inscripciones.findUnique({
      where: { id },
      select: { id: true },
    });
    if (!existing) throw new HttpError(404, "Inscripción no encontrada");

    const tsExpr = body.asistio
      ? Prisma.sql`CURRENT_TIMESTAMP`
      : Prisma.sql`NULL`;

    await prisma.$executeRaw(Prisma.sql`
      UPDATE crea_inscripciones
      SET ${Prisma.raw(colFlag)} = ${body.asistio ? 1 : 0},
          ${Prisma.raw(colTs)} = ${tsExpr}
      WHERE id = ${id}
    `);

    const rows = await prisma.$queryRaw<RegistroRow[]>`
      ${SELECT_REGISTRO_ROW}
      WHERE ci.id = ${id}
      LIMIT 1
    `;
    res.json({ inscripcion: serialize(rows[0]) });
  } catch (err) {
    next(err);
  }
});

function serialize(r: RegistroRow) {
  return {
    crea_inscripcion_id: Number(r.crea_inscripcion_id),
    estudiante_id: Number(r.estudiante_id),
    nombre_completo: r.nombre_completo,
    colegio: r.colegio,
    categoria: r.categoria,
    modalidad: r.modalidad,
    telefono: r.telefono,
    kit_id: r.kit_id !== null ? Number(r.kit_id) : null,
    kit_codigo: r.kit_codigo,
    nombre_robot: r.nombre_robot,
    pareja_id: r.pareja_id !== null ? Number(r.pareja_id) : null,
    pareja_nombre: r.pareja_nombre,
    asistio_dia_1: Number(r.asistio_dia_1) === 1,
    asistio_dia_1_en: r.asistio_dia_1_en,
    asistio_dia_2: Number(r.asistio_dia_2) === 1,
    asistio_dia_2_en: r.asistio_dia_2_en,
  };
}
