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 kitsRouter = Router();

kitsRouter.use(requireAuth);

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

const ESTADOS_KIT = [
  "asignado",
  "empacado",
  "enviado",
  "recibido",
  "recogido_en_bootcamp",
] as const;

const METODOS_ENTREGA = ["envio", "bootcamp"] as const;

const filtersSchema = z.object({
  search: z.string().trim().max(120).optional(),
  categoria: z.enum(CATEGORIAS).optional(),
  estado: z.enum(ESTADOS_KIT).optional(),
  talla: z.string().trim().max(20).optional(),
});

const listQuerySchema = filtersSchema.extend({
  page: z.coerce.number().int().min(1).default(1),
  pageSize: z.coerce.number().int().min(1).max(200).default(50),
});

type Filters = z.infer<typeof filtersSchema>;

type KitRow = {
  crea_inscripcion_id: number;
  kit_id: number;
  kit_codigo: string;
  categoria: string;
  modalidad: string;
  nombre_estudiante: string;
  estudiante_id: number;
  colegio: string | null;
  nombre_pareja: string | null;
  talla_playera: string | null;
  talla_pareja: string | null;
  metodo_entrega: string | null;
  direccion_envio: string | null;
  municipio_envio: string | null;
  bootcamp_entrega: string | null;
  estado_kit: string;
  nombre_robot: string | null;
  empacado_en: Date | null;
  estado_pago: string;
};

function buildFilters(q: Filters): Prisma.Sql[] {
  const conditions: Prisma.Sql[] = [Prisma.sql`1=1`];
  if (q.categoria) conditions.push(Prisma.sql`categoria = ${q.categoria}`);
  if (q.estado) conditions.push(Prisma.sql`estado_kit = ${q.estado}`);
  if (q.talla) conditions.push(Prisma.sql`(talla_playera = ${q.talla} OR talla_pareja = ${q.talla})`);
  if (q.search) {
    const term = `%${q.search}%`;
    conditions.push(
      Prisma.sql`(kit_codigo LIKE ${term} OR nombre_estudiante LIKE ${term} OR nombre_pareja LIKE ${term} OR nombre_robot LIKE ${term})`
    );
  }
  return conditions;
}

kitsRouter.get("/", async (req, res, next) => {
  try {
    const query = listQuerySchema.parse(req.query);
    const where = Prisma.join(buildFilters(query), " AND ");
    const offset = (query.page - 1) * query.pageSize;

    const rows = await prisma.$queryRaw<KitRow[]>`
      SELECT *
      FROM v_armado_kits
      WHERE ${where}
      ORDER BY kit_codigo
      LIMIT ${query.pageSize} OFFSET ${offset}
    `;

    const [{ total }] = await prisma.$queryRaw<[{ total: bigint }]>`
      SELECT COUNT(*) AS total
      FROM v_armado_kits
      WHERE ${where}
    `;

    res.json({
      data: rows,
      pagination: {
        page: query.page,
        pageSize: query.pageSize,
        total: Number(total),
        totalPages: Math.ceil(Number(total) / query.pageSize),
      },
    });
  } catch (err) {
    next(err);
  }
});

kitsRouter.get("/progress", async (_req, res, next) => {
  try {
    const porCategoria = await prisma.$queryRaw<
      { categoria: string; estado: string; total: bigint }[]
    >`
      SELECT categoria, estado_kit AS estado, COUNT(*) AS total
      FROM v_armado_kits
      GROUP BY categoria, estado_kit
      ORDER BY categoria, estado_kit
    `;

    const porEstado = await prisma.$queryRaw<
      { estado: string; total: bigint }[]
    >`
      SELECT estado_kit AS estado, COUNT(*) AS total
      FROM v_armado_kits
      GROUP BY estado_kit
    `;

    const [{ total }] = await prisma.$queryRaw<[{ total: bigint }]>`
      SELECT COUNT(*) AS total FROM v_armado_kits
    `;

    res.json({
      total: Number(total),
      por_estado: porEstado.map((r) => ({
        estado: r.estado,
        total: Number(r.total),
      })),
      por_categoria: porCategoria.map((r) => ({
        categoria: r.categoria,
        estado: r.estado,
        total: Number(r.total),
      })),
    });
  } catch (err) {
    next(err);
  }
});

const updateStateSchema = z.object({
  estado: z.enum(ESTADOS_KIT),
});

kitsRouter.patch("/:id", async (req, res, next) => {
  try {
    const id = z.coerce.number().int().positive().parse(req.params.id);
    const { estado } = updateStateSchema.parse(req.body);

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

    const data: { estado: typeof estado; empacado_en?: Date | null } = {
      estado,
    };
    if (estado === "empacado" && !existing.empacado_en) {
      data.empacado_en = new Date();
    }

    const updated = await prisma.kits.update({
      where: { id },
      data,
    });

    res.json({
      kit: {
        id: updated.id,
        codigo: updated.codigo,
        estado: updated.estado,
        empacado_en: updated.empacado_en,
      },
    });
  } catch (err) {
    next(err);
  }
});

const envioSchema = z.object({
  direccion: z.string().trim().min(1).max(300),
  municipio: z.string().trim().max(80).optional().nullable(),
  nombre_receptor: z.string().trim().max(120).optional().nullable(),
  telefono_receptor: z.string().trim().max(30).optional().nullable(),
  indicaciones: z.string().trim().max(500).optional().nullable(),
});

const entregaSchema = z.discriminatedUnion("metodo", [
  z.object({
    metodo: z.literal("envio"),
    envio: envioSchema,
  }),
  z.object({
    metodo: z.literal("bootcamp"),
    bootcamp_id: z.coerce.number().int().positive(),
  }),
]);

kitsRouter.patch("/:id/entrega", async (req, res, next) => {
  try {
    const kitId = z.coerce.number().int().positive().parse(req.params.id);
    const body = entregaSchema.parse(req.body);

    const kit = await prisma.kits.findUnique({ where: { id: kitId } });
    if (!kit) throw new HttpError(404, "Kit no encontrado");

    const inscripciones = await prisma.crea_inscripciones.findMany({
      where: { kit_id: kitId },
    });
    if (inscripciones.length === 0) {
      throw new HttpError(404, "Kit sin inscripciones asociadas");
    }

    await prisma.$transaction(async (tx) => {
      if (body.metodo === "envio") {
        const firstEnvioId = inscripciones.find((i) => i.envio_id)?.envio_id;
        let envioId: number;

        if (firstEnvioId) {
          await tx.envios.update({
            where: { id: firstEnvioId },
            data: {
              direccion: body.envio.direccion,
              municipio: body.envio.municipio ?? null,
              nombre_receptor: body.envio.nombre_receptor ?? null,
              telefono_receptor: body.envio.telefono_receptor ?? null,
              indicaciones: body.envio.indicaciones ?? null,
            },
          });
          envioId = firstEnvioId;
        } else {
          const created = await tx.envios.create({
            data: {
              direccion: body.envio.direccion,
              municipio: body.envio.municipio ?? null,
              nombre_receptor: body.envio.nombre_receptor ?? null,
              telefono_receptor: body.envio.telefono_receptor ?? null,
              indicaciones: body.envio.indicaciones ?? null,
            },
          });
          envioId = created.id;
        }

        await tx.crea_inscripciones.updateMany({
          where: { kit_id: kitId },
          data: { envio_id: envioId, bootcamp_id: null },
        });
      } else {
        const bootcamp = await tx.bootcamps.findUnique({
          where: { id: body.bootcamp_id },
        });
        if (!bootcamp) throw new HttpError(404, "Bootcamp no encontrado");

        await tx.crea_inscripciones.updateMany({
          where: { kit_id: kitId },
          data: { bootcamp_id: body.bootcamp_id, envio_id: null },
        });
      }
    });

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

kitsRouter.get("/filters", async (_req, res, next) => {
  try {
    const tallas = await prisma.$queryRaw<
      { value: string; count: bigint }[]
    >`
      SELECT talla_playera AS value, COUNT(*) AS count
      FROM v_armado_kits
      WHERE talla_playera IS NOT NULL AND talla_playera <> ''
      GROUP BY talla_playera
      ORDER BY
        CASE talla_playera
          WHEN 'XS' THEN 1
          WHEN 'S' THEN 2
          WHEN 'M' THEN 3
          WHEN 'L' THEN 4
          WHEN 'XL' THEN 5
          WHEN 'XXL' THEN 6
          ELSE 99
        END,
        talla_playera
    `;
    res.json({
      tallas: tallas.map((r) => ({ value: r.value, count: Number(r.count) })),
    });
  } catch (err) {
    next(err);
  }
});

kitsRouter.get("/export", async (req, res, next) => {
  try {
    const query = filtersSchema.parse(req.query);
    const where = Prisma.join(buildFilters(query), " AND ");

    const rows = await prisma.$queryRaw<KitRow[]>`
      SELECT *
      FROM v_armado_kits
      WHERE ${where}
      ORDER BY kit_codigo
    `;

    const headers = [
      "kit_codigo",
      "nombre_estudiante",
      "nombre_pareja",
      "colegio",
      "categoria",
      "modalidad",
      "talla_playera",
      "talla_pareja",
      "nombre_robot",
      "metodo_entrega",
      "direccion_envio",
      "municipio_envio",
      "bootcamp_entrega",
      "estado_kit",
      "estado_pago",
      "empacado_en",
    ];

    const csvEscape = (v: unknown): string => {
      if (v === null || v === undefined) return "";
      const s = v instanceof Date ? v.toISOString().slice(0, 10) : String(v);
      if (/[",\n\r]/.test(s)) return `"${s.replace(/"/g, '""')}"`;
      return s;
    };

    const lines = [
      headers.join(","),
      ...rows.map((r) =>
        [
          r.kit_codigo,
          r.nombre_estudiante,
          r.nombre_pareja,
          r.colegio,
          r.categoria,
          r.modalidad,
          r.talla_playera,
          r.talla_pareja,
          r.nombre_robot,
          r.metodo_entrega,
          r.direccion_envio,
          r.municipio_envio,
          r.bootcamp_entrega,
          r.estado_kit,
          r.estado_pago,
          r.empacado_en,
        ]
          .map(csvEscape)
          .join(",")
      ),
    ];

    const csv = "﻿" + lines.join("\r\n") + "\r\n";
    const filename = `kits_${new Date().toISOString().slice(0, 10)}.csv`;
    res.setHeader("Content-Type", "text/csv; charset=utf-8");
    res.setHeader("Content-Disposition", `attachment; filename="${filename}"`);
    res.send(csv);
  } catch (err) {
    next(err);
  }
});

kitsRouter.get("/bootcamps", async (_req, res, next) => {
  try {
    const bootcamps = await prisma.bootcamps.findMany({
      orderBy: { nombre: "asc" },
      select: { id: true, nombre: true },
    });
    res.json({ bootcamps });
  } catch (err) {
    next(err);
  }
});
