import { Router } from 'express';
import { z } from 'zod';
import type { Prisma, PrismaClient } from '../generated/prisma/client.js';
import { categorySchema, dateSchema, eventRoomSchema, eventSchema, roomSchema, sessionSchema, speakerSchema } from '../shared/contracts.js';
import { required } from './errors.js';
import { createAssignment, deleteAssignment, deleteSession, eventBounds, saveEvent, saveSession } from './scheduling.js';
import { eventDto, eventRoomDto, eventRoomInclude, roomDto, sessionDto, sessionInclude, speakerDto } from './serialize.js';

const positive = z.coerce.number().int().positive();
const querySchema = z.object({ page: positive.default(1), pageSize: positive.max(100).default(20), q: z.string().max(300).default(''), eventId: positive.optional(), roomId: positive.optional(), date: dateSchema.optional() });
export function adminResources(db: PrismaClient) {
  const router = Router();
  router.get('/overview', async (_req, res) => {
    const today = new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Jakarta', year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date());
    const bounds = eventBounds(today, today);
    const [events, rooms, speakers, sessions] = await Promise.all([
      db.event.count(), db.room.count(), db.speaker.count(),
      db.session.findMany({ where: { startsAt: { lt: bounds.end }, endsAt: { gt: bounds.start } }, orderBy: { startsAt: 'asc' }, include: { ...sessionInclude, eventRoom: { include: eventRoomInclude } } }),
    ]);
    res.json({ events, rooms, speakers, today, sessions: sessions.map(s => ({ ...sessionDto(s), eventRoom: eventRoomDto(s.eventRoom) })) });
  });
  router.get('/rooms', async (req, res) => {
    const { page, pageSize, q } = querySchema.parse(req.query);
    const where = { OR: [{ name: { contains: q } }, { code: { contains: q } }] };
    const [items, total] = await Promise.all([db.room.findMany({ where, orderBy: { code: 'asc' }, skip: (page - 1) * pageSize, take: pageSize }), db.room.count({ where })]);
    res.json({ items: items.map(roomDto), total, page, pageSize });
  });
  router.post('/rooms', async (req, res) => res.status(201).json(roomDto(await db.room.create({ data: roomSchema.parse(req.body) }))));
  router.get('/rooms/:id', async (req, res) => res.json(roomDto(required(await db.room.findUnique({ where: { id: positive.parse(req.params.id) } })))));
  router.put('/rooms/:id', async (req, res) => res.json(roomDto(await db.room.update({ where: { id: positive.parse(req.params.id) }, data: roomSchema.parse(req.body) }))));
  router.delete('/rooms/:id', async (req, res) => { await db.room.delete({ where: { id: positive.parse(req.params.id) } }); res.status(204).end(); });

  router.get('/speakers', async (req, res) => {
    const { page, pageSize, q } = querySchema.parse(req.query);
    const where = { OR: [{ name: { contains: q } }, { institution: { contains: q } }] };
    const [items, total] = await Promise.all([db.speaker.findMany({ where, orderBy: { name: 'asc' }, skip: (page - 1) * pageSize, take: pageSize }), db.speaker.count({ where })]);
    res.json({ items: items.map(speakerDto), total, page, pageSize });
  });
  router.post('/speakers', async (req, res) => res.status(201).json(speakerDto(await db.speaker.create({ data: speakerSchema.parse(req.body) }))));
  router.get('/speakers/:id', async (req, res) => res.json(speakerDto(required(await db.speaker.findUnique({ where: { id: positive.parse(req.params.id) } })))));
  router.put('/speakers/:id', async (req, res) => res.json(speakerDto(await db.speaker.update({ where: { id: positive.parse(req.params.id) }, data: speakerSchema.parse(req.body) }))));
  router.delete('/speakers/:id', async (req, res) => { await db.speaker.delete({ where: { id: positive.parse(req.params.id) } }); res.status(204).end(); });

  router.get('/events', async (req, res) => {
    const { page, pageSize, q } = querySchema.parse(req.query);
    const where = { name: { contains: q } };
    const [items, total] = await Promise.all([db.event.findMany({ where, orderBy: [{ startDate: 'desc' }, { id: 'asc' }], skip: (page - 1) * pageSize, take: pageSize }), db.event.count({ where })]);
    res.json({ items: items.map(eventDto), total, page, pageSize });
  });
  router.post('/events', async (req, res) => res.status(201).json(eventDto(await saveEvent(db, eventSchema.parse(req.body)))));
  router.get('/events/:id', async (req, res) => res.json(eventDto(required(await db.event.findUnique({ where: { id: positive.parse(req.params.id) } })))));
  router.put('/events/:id', async (req, res) => res.json(eventDto(await saveEvent(db, eventSchema.parse(req.body), positive.parse(req.params.id)))));
  router.delete('/events/:id', async (req, res) => { await db.event.delete({ where: { id: positive.parse(req.params.id) } }); res.status(204).end(); });

  router.get('/event-rooms', async (req, res) => {
    const { page, pageSize, q, eventId, roomId } = querySchema.parse(req.query);
    const where = { eventId, roomId, room: { name: { contains: q } } };
    const [items, total] = await Promise.all([db.eventRoom.findMany({ where, include: eventRoomInclude, orderBy: [{ eventId: 'desc' }, { room: { code: 'asc' } }], skip: (page - 1) * pageSize, take: pageSize }), db.eventRoom.count({ where })]);
    res.json({ items: items.map(eventRoomDto), total, page, pageSize });
  });
  router.post('/event-rooms', async (req, res) => res.status(201).json(eventRoomDto(await createAssignment(db, eventRoomSchema.parse(req.body)))));
  router.get('/event-rooms/:id', async (req, res) => res.json(eventRoomDto(required(await db.eventRoom.findUnique({ where: { id: positive.parse(req.params.id) }, include: eventRoomInclude })))));
  router.put('/event-rooms/:id', async (req, res) => res.json(eventRoomDto(await db.eventRoom.update({ where: { id: positive.parse(req.params.id) }, data: categorySchema.strict().parse(req.body), include: eventRoomInclude }))));
  router.delete('/event-rooms/:id', async (req, res) => { await deleteAssignment(db, positive.parse(req.params.id)); res.status(204).end(); });

  router.get('/sessions', async (req, res) => {
    const { page, pageSize, q, eventId, roomId, date } = querySchema.parse(req.query);
    const bounds = date ? eventBounds(date, date) : null;
    const where: Prisma.SessionWhereInput = { title: { contains: q }, eventRoom: { eventId, roomId }, ...(bounds ? { startsAt: { lt: bounds.end }, endsAt: { gt: bounds.start } } : {}) };
    const [items, total] = await Promise.all([db.session.findMany({ where, include: { ...sessionInclude, eventRoom: { include: eventRoomInclude } }, orderBy: [{ startsAt: 'asc' }, { id: 'asc' }], skip: (page - 1) * pageSize, take: pageSize }), db.session.count({ where })]);
    res.json({ items: items.map(s => ({ ...sessionDto(s), eventRoom: eventRoomDto(s.eventRoom) })), total, page, pageSize });
  });
  router.post('/sessions', async (req, res) => res.status(201).json(sessionDto(await saveSession(db, sessionSchema.parse(req.body)))));
  router.get('/sessions/:id', async (req, res) => res.json(sessionDto(required(await db.session.findUnique({ where: { id: positive.parse(req.params.id) }, include: sessionInclude })))));
  router.put('/sessions/:id', async (req, res) => res.json(sessionDto(await saveSession(db, sessionSchema.parse(req.body), positive.parse(req.params.id)))));
  router.delete('/sessions/:id', async (req, res) => { await deleteSession(db, positive.parse(req.params.id)); res.status(204).end(); });
  return router;
}

export function publicResources(db: PrismaClient) {
  const router = Router();
  router.get('/events', async (_req, res) => res.json((await db.event.findMany({ orderBy: [{ startDate: 'asc' }, { id: 'asc' }] })).map(eventDto)));
  router.get('/events/:eventId/rooms', async (req, res) => {
    const eventId = positive.parse(req.params.eventId);
    const event = required(await db.event.findUnique({ where: { id: eventId } }));
    const assignments = await db.eventRoom.findMany({ where: { eventId }, include: { room: true, sessions: { include: sessionInclude, orderBy: [{ startsAt: 'asc' }, { id: 'asc' }] } }, orderBy: { room: { code: 'asc' } } });
    res.json({ event: eventDto(event), rooms: assignments.map(a => ({ ...roomDto(a.room), category: a.category, sessions: a.sessions.map(sessionDto) })) });
  });
  router.get('/events/:eventId/rooms/:roomCode/schedule', async (req, res) => {
    const eventId = positive.parse(req.params.eventId);
    const a = required(await db.eventRoom.findFirst({ where: { eventId, room: { code: String(req.params.roomCode).toUpperCase() } }, include: { event: true, room: true, sessions: { orderBy: [{ startsAt: 'asc' }, { id: 'asc' }], include: sessionInclude } } }));
    res.json({ event: eventDto(a.event), room: roomDto(a.room), category: a.category, sessions: a.sessions.map(sessionDto) });
  });
  return router;
}
