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

export const bootcampRouter = Router();

bootcampRouter.use(requireAuth);

type BootcampListRow = {
  id: number;
  nombre: string;
  fecha: Date | null;
  ubicacion: string | null;
  departamento: string | null;
  previstos: bigint;
  asistieron: bigint;
  adicionales: bigint;
};

type AsistenciaRow = {
  estudiante_id: number;
  nombre_completo: string;
  correo: string | null;
  colegio: string | null;
  categoria: string | null;
  kit_id: number | null;
  kit_codigo: string | null;
  nombre_robot: string | null;
  kit_estado: string | null;
  asistio: number | bigint | boolean | null;
};

const mapAsistente = (r: AsistenciaRow) => ({
  estudiante_id: Number(r.estudiante_id),
  nombre_completo: r.nombre_completo,
  correo: r.correo,
  colegio: r.colegio,
  categoria: r.categoria,
  kit_id: r.kit_id === null ? null : Number(r.kit_id),
  kit_codigo: r.kit_codigo,
  nombre_robot: r.nombre_robot,
  kit_estado: r.kit_estado,
  asistio: Boolean(Number(r.asistio ?? 0)),
});

bootcampRouter.get("/", async (_req, res, next) => {
  try {
    const rows = await prisma.$queryRaw<BootcampListRow[]>`
      SELECT
        b.id,
        b.nombre,
        b.fecha,
        b.ubicacion,
        d.nombre AS departamento,
        (
          SELECT COUNT(*) FROM crea_inscripciones ci
          WHERE ci.bootcamp_id = b.id
        ) AS previstos,
        (
          SELECT COUNT(*) FROM asistencia_bootcamp ab
          JOIN crea_inscripciones ci2 ON ci2.estudiante_id = ab.estudiante_id
          WHERE ab.bootcamp_id = b.id AND ab.asistio = 1
            AND ci2.bootcamp_id = b.id
        ) AS asistieron,
        (
          SELECT COUNT(*) FROM asistencia_bootcamp ab
          WHERE ab.bootcamp_id = b.id AND ab.asistio = 1
            AND NOT EXISTS (
              SELECT 1 FROM crea_inscripciones ci3
              WHERE ci3.estudiante_id = ab.estudiante_id
                AND ci3.bootcamp_id = b.id
            )
        ) AS adicionales
      FROM bootcamps b
      LEFT JOIN departamentos d ON d.id = b.departamento_id
      ORDER BY b.id
    `;

    res.json({
      data: rows.map((r) => ({
        id: Number(r.id),
        nombre: r.nombre,
        fecha: r.fecha,
        ubicacion: r.ubicacion,
        departamento: r.departamento,
        previstos: Number(r.previstos),
        asistieron: Number(r.asistieron),
        adicionales: Number(r.adicionales),
      })),
    });
  } catch (err) {
    next(err);
  }
});

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

    const bootcamp = await prisma.bootcamps.findUnique({ where: { id } });
    if (!bootcamp) throw new HttpError(404, "Bootcamp no encontrado");

    const previstos = await prisma.$queryRaw<AsistenciaRow[]>`
      SELECT
        e.id AS estudiante_id,
        CONCAT_WS(' ', e.nombres, e.apellido_1, e.apellido_2) AS nombre_completo,
        e.correo,
        col.nombre AS colegio,
        ci.categoria_codigo AS categoria,
        k.id AS kit_id,
        k.codigo AS kit_codigo,
        k.nombre_robot,
        k.estado AS kit_estado,
        COALESCE(ab.asistio, 0) AS asistio
      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 asistencia_bootcamp ab
        ON ab.estudiante_id = e.id AND ab.bootcamp_id = ${id}
      WHERE ci.bootcamp_id = ${id}
      ORDER BY nombre_completo
    `;

    const adicionales = await prisma.$queryRaw<AsistenciaRow[]>`
      SELECT
        e.id AS estudiante_id,
        CONCAT_WS(' ', e.nombres, e.apellido_1, e.apellido_2) AS nombre_completo,
        e.correo,
        col.nombre AS colegio,
        ci.categoria_codigo AS categoria,
        k.id AS kit_id,
        k.codigo AS kit_codigo,
        k.nombre_robot,
        k.estado AS kit_estado,
        ab.asistio AS asistio
      FROM asistencia_bootcamp ab
      JOIN estudiantes e ON e.id = ab.estudiante_id
      LEFT JOIN colegios col ON col.id = e.colegio_id
      LEFT JOIN crea_inscripciones ci ON ci.estudiante_id = e.id
      LEFT JOIN kits k ON k.id = ci.kit_id
      WHERE ab.bootcamp_id = ${id}
        AND NOT EXISTS (
          SELECT 1 FROM crea_inscripciones ci2
          WHERE ci2.estudiante_id = e.id AND ci2.bootcamp_id = ${id}
        )
      ORDER BY nombre_completo
    `;

    const previstosMapped = previstos.map(mapAsistente);
    const adicionalesMapped = adicionales.map(mapAsistente);

    res.json({
      bootcamp: {
        id: Number(bootcamp.id),
        nombre: bootcamp.nombre,
        fecha: bootcamp.fecha,
        ubicacion: bootcamp.ubicacion,
      },
      previstos: previstosMapped,
      adicionales: adicionalesMapped,
      counters: {
        previstos: previstosMapped.length,
        asistieron: previstosMapped.filter((p) => p.asistio).length,
        adicionales: adicionalesMapped.filter((a) => a.asistio).length,
      },
    });
  } catch (err) {
    next(err);
  }
});

const setAsistenciaSchema = z.object({
  estudiante_id: z.coerce.number().int().positive(),
  asistio: z.boolean(),
});

bootcampRouter.post("/:id/asistencia", async (req, res, next) => {
  try {
    const id = z.coerce.number().int().positive().parse(req.params.id);
    const { estudiante_id, asistio } = setAsistenciaSchema.parse(req.body);

    const bootcamp = await prisma.bootcamps.findUnique({ where: { id } });
    if (!bootcamp) throw new HttpError(404, "Bootcamp no encontrado");

    const estudiante = await prisma.estudiantes.findUnique({
      where: { id: estudiante_id },
    });
    if (!estudiante) throw new HttpError(404, "Estudiante no encontrado");

    await prisma.asistencia_bootcamp.upsert({
      where: {
        bootcamp_id_estudiante_id: { bootcamp_id: id, estudiante_id },
      },
      create: { bootcamp_id: id, estudiante_id, asistio },
      update: { asistio },
    });

    res.json({ ok: true });
  } catch (err) {
    next(err);
  }
});

bootcampRouter.delete("/:id/asistencia/:estudianteId", async (req, res, next) => {
  try {
    const id = z.coerce.number().int().positive().parse(req.params.id);
    const estudianteId = z.coerce
      .number()
      .int()
      .positive()
      .parse(req.params.estudianteId);

    await prisma.asistencia_bootcamp.deleteMany({
      where: { bootcamp_id: id, estudiante_id: estudianteId },
    });

    res.json({ ok: true });
  } catch (err) {
    next(err);
  }
});

const buscarSchema = z.object({
  search: z.string().trim().min(1).max(120),
});

bootcampRouter.get("/:id/buscar", async (req, res, next) => {
  try {
    const id = z.coerce.number().int().positive().parse(req.params.id);
    const { search } = buscarSchema.parse(req.query);
    const term = `%${search}%`;

    const rows = await prisma.$queryRaw<AsistenciaRow[]>`
      SELECT
        e.id AS estudiante_id,
        CONCAT_WS(' ', e.nombres, e.apellido_1, e.apellido_2) AS nombre_completo,
        e.correo,
        col.nombre AS colegio,
        ci.categoria_codigo AS categoria,
        k.id AS kit_id,
        k.codigo AS kit_codigo,
        k.nombre_robot,
        k.estado AS kit_estado,
        0 AS asistio
      FROM estudiantes e
      LEFT JOIN colegios col ON col.id = e.colegio_id
      LEFT JOIN crea_inscripciones ci ON ci.estudiante_id = e.id
      LEFT JOIN kits k ON k.id = ci.kit_id
      WHERE (
          CONCAT_WS(' ', e.nombres, e.apellido_1, e.apellido_2) LIKE ${term}
          OR e.correo LIKE ${term}
        )
        AND NOT EXISTS (
          SELECT 1 FROM crea_inscripciones ci2
          WHERE ci2.estudiante_id = e.id AND ci2.bootcamp_id = ${id}
        )
      ORDER BY nombre_completo
      LIMIT 20
    `;

    res.json({ data: rows.map(mapAsistente) });
  } catch (err) {
    next(err);
  }
});
