import { Router, Request, Response, NextFunction } from 'express';
import { authenticate } from '../../middleware/auth.middleware';
import { requireRole } from '../../middleware/rbac.middleware';
import { validate } from '../../middleware/validate';
import { ticketedEventsService } from './ticketed-events.service';
import { ticketsService } from './tickets.service';
import { ticketEmailService } from './ticket-email.service';
import {
  createEventSchema,
  updateEventSchema,
  createTierSchema,
  updateTierSchema,
} from './ticketed-events.schemas';
import { prisma } from '../../config/database';
import { UserRole } from '@prisma/client';
import { EVENTS_ROLES } from '../../utils/roles';

const router = Router();

/** Middleware: require admin role OR canCreateTicketedEvents permission */
async function requireEventPermission(req: Request, _res: Response, next: NextFunction) {
  if (!req.user) return next(new Error('Auth required'));

  const userRoles = req.user.roles || [req.user.role];
  if (userRoles.some(r => EVENTS_ROLES.includes(r as UserRole))) {
    return next();
  }

  // Check user permissions
  const user = await prisma.user.findUnique({
    where: { id: req.user.id },
    select: { permissions: true },
  });
  const perms = (user?.permissions as Record<string, unknown>) || {};
  if (perms.canCreateTicketedEvents) {
    return next();
  }

  return next({ status: 403, message: 'Insufficient permissions' });
}

/** Middleware: for :id routes, verify non-admin users own the event */
async function requireEventOwnership(req: Request, res: Response, next: NextFunction) {
  const eventId = req.params.id as string;
  if (!eventId) return next();

  const userRoles = req.user!.roles || [req.user!.role];
  const isAdmin = userRoles.some(r => EVENTS_ROLES.includes(r as UserRole));
  if (isAdmin) return next();

  const event = await prisma.ticketedEvent.findUnique({
    where: { id: eventId },
    select: { createdByUserId: true },
  });
  if (!event) {
    res.status(404).json({ error: { message: 'Event not found', code: 'NOT_FOUND' } });
    return;
  }
  if (event.createdByUserId !== req.user!.id) {
    res.status(403).json({ error: { message: 'Forbidden', code: 'FORBIDDEN' } });
    return;
  }
  next();
}

// All routes require auth + event permission
router.use(authenticate, requireEventPermission);

// GET / — list events (admin sees all, users see own)
router.get('/', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const page = parseInt(req.query.page as string) || 1;
    const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
    const status = req.query.status as string | undefined;
    const search = req.query.search as string | undefined;

    const userRoles = req.user!.roles || [req.user!.role];
    const isAdmin = userRoles.some(r => EVENTS_ROLES.includes(r as UserRole));

    const result = await ticketedEventsService.list({
      page,
      limit,
      status: status as never,
      search,
      createdByUserId: isAdmin ? undefined : req.user!.id,
    });
    res.json(result);
  } catch (err) { next(err); }
});

// POST / — create event
router.post('/', validate(createEventSchema), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const event = await ticketedEventsService.create(req.body, req.user!.id);
    res.status(201).json(event);
  } catch (err) { next(err); }
});

// GET /:id — event detail
router.get('/:id', requireEventOwnership, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const event = await ticketedEventsService.findById(req.params.id as string);
    res.json(event);
  } catch (err) { next(err); }
});

// PUT /:id — update event
router.put('/:id', validate(updateEventSchema), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const event = await ticketedEventsService.update(
      req.params.id as string,
      req.body,
      req.user!.id,
      req.user!.role,
    );
    res.json(event);
  } catch (err) { next(err); }
});

// DELETE /:id — delete event (draft only)
router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => {
  try {
    await ticketedEventsService.deleteEvent(req.params.id as string, req.user!.id, req.user!.role);
    res.json({ success: true });
  } catch (err) { next(err); }
});

// POST /:id/publish
router.post('/:id/publish', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const event = await ticketedEventsService.publish(req.params.id as string, req.user!.id, req.user!.role);
    res.json(event);
  } catch (err) { next(err); }
});

// POST /:id/approve (admin only)
router.post('/:id/approve', requireRole(...EVENTS_ROLES), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const event = await ticketedEventsService.approve(req.params.id as string);
    res.json(event);
  } catch (err) { next(err); }
});

// POST /:id/reject (admin only)
router.post('/:id/reject', requireRole(...EVENTS_ROLES), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const event = await ticketedEventsService.reject(req.params.id as string);
    res.json(event);
  } catch (err) { next(err); }
});

// POST /:id/cancel
router.post('/:id/cancel', async (req: Request, res: Response, next: NextFunction) => {
  try {
    const event = await ticketedEventsService.cancel(req.params.id as string, req.user!.id, req.user!.role);
    res.json(event);
  } catch (err) { next(err); }
});

// POST /:id/complete (admin only)
router.post('/:id/complete', requireRole(...EVENTS_ROLES), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const event = await ticketedEventsService.complete(req.params.id as string);
    res.json(event);
  } catch (err) { next(err); }
});

// --- Meeting ---

// POST /:id/meeting-token — generate moderator JWT for Jitsi
router.post('/:id/meeting-token', requireEventOwnership, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const user = await prisma.user.findUnique({
      where: { id: req.user!.id },
      select: { id: true, email: true, name: true },
    });
    if (!user) throw new Error('User not found');
    const result = await ticketedEventsService.getModeratorToken(req.params.id as string, user);
    res.json(result);
  } catch (err) { next(err); }
});

// --- Tiers ---

// POST /:id/tiers
router.post('/:id/tiers', requireEventOwnership, validate(createTierSchema), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tier = await ticketedEventsService.addTier(req.params.id as string, req.body);
    res.status(201).json(tier);
  } catch (err) { next(err); }
});

// PUT /:id/tiers/:tierId
router.put('/:id/tiers/:tierId', requireEventOwnership, validate(updateTierSchema), async (req: Request, res: Response, next: NextFunction) => {
  try {
    const tier = await ticketedEventsService.updateTier(
      req.params.tierId as string,
      req.body,
      req.params.id as string,
    );
    res.json(tier);
  } catch (err) { next(err); }
});

// DELETE /:id/tiers/:tierId
router.delete('/:id/tiers/:tierId', requireEventOwnership, async (req: Request, res: Response, next: NextFunction) => {
  try {
    await ticketedEventsService.deleteTier(req.params.tierId as string, req.params.id as string);
    res.json({ success: true });
  } catch (err) { next(err); }
});

// --- Tickets ---

// GET /:id/tickets
router.get('/:id/tickets', requireEventOwnership, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const page = parseInt(req.query.page as string) || 1;
    const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
    const search = req.query.search as string | undefined;
    const status = req.query.status as string | undefined;
    const result = await ticketedEventsService.getTickets(req.params.id as string, { page, limit, search, status });
    res.json(result);
  } catch (err) { next(err); }
});

// GET /:id/checkins
router.get('/:id/checkins', requireEventOwnership, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const page = parseInt(req.query.page as string) || 1;
    const limit = Math.min(parseInt(req.query.limit as string) || 20, 100);
    const result = await ticketedEventsService.getCheckIns(req.params.id as string, { page, limit });
    res.json(result);
  } catch (err) { next(err); }
});

// GET /:id/stats
router.get('/:id/stats', requireEventOwnership, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const stats = await ticketedEventsService.getEventStats(req.params.id as string);
    res.json(stats);
  } catch (err) { next(err); }
});

// POST /:id/resend-ticket/:ticketId
router.post('/:id/resend-ticket/:ticketId', requireEventOwnership, async (req: Request, res: Response, next: NextFunction) => {
  try {
    const ticket = await prisma.ticket.findUnique({
      where: { id: req.params.ticketId as string },
      include: {
        event: true,
        tier: { select: { name: true } },
      },
    });
    if (!ticket) { res.status(404).json({ error: { message: 'Ticket not found' } }); return; }
    if (ticket.eventId !== req.params.id) { res.status(400).json({ error: { message: 'Ticket does not belong to this event' } }); return; }

    // Re-generate token for the email (token not stored, we need tokenHash lookup)
    // For resend, we regenerate the token and update the hash
    const crypto = await import('crypto');
    const { env: envConfig } = await import('../../config/env');
    const nonce = crypto.randomBytes(16);
    // Use domain-separated key for ticket HMACs (matches tickets.service.ts)
    const ticketKey = crypto.createHmac('sha256', envConfig.ENCRYPTION_KEY).update('ticket-hmac-v1').digest();
    const hmac = crypto.createHmac('sha256', ticketKey);
    hmac.update(ticket.id);
    hmac.update(nonce);
    const token = Buffer.concat([
      Buffer.from(ticket.id),
      Buffer.from(':'),
      nonce,
      Buffer.from(':'),
      hmac.digest(),
    ]).toString('base64url');
    const tokenHash = crypto.createHash('sha256').update(token).digest('hex');

    await prisma.ticket.update({
      where: { id: ticket.id },
      data: { tokenHash },
    });

    await ticketEmailService.sendTicketConfirmation({
      holderEmail: ticket.holderEmail,
      holderName: ticket.holderName,
      ticketCode: ticket.ticketCode,
      token,
      eventTitle: ticket.event.title,
      eventDate: ticket.event.date,
      eventStartTime: ticket.event.startTime,
      eventEndTime: ticket.event.endTime,
      eventSlug: ticket.event.slug,
      venueName: ticket.event.venueName,
      venueAddress: ticket.event.venueAddress,
      tierName: ticket.tier.name,
    });

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

// POST /:id/tickets/:ticketId/cancel
router.post('/:id/tickets/:ticketId/cancel', requireEventOwnership, async (req: Request, res: Response, next: NextFunction) => {
  try {
    await ticketsService.cancelTicket(req.params.ticketId as string);
    res.json({ success: true });
  } catch (err) { next(err); }
});

export { router as ticketedEventsAdminRouter };
