import { Response } from 'express';
import { nanoid } from 'nanoid';
import { prisma } from '../config/database';
import { AuthRequest } from '../middleware/auth';
import { generateQRCodeBuffer } from '../utils/qrcode';
import { uploadToS3 } from '../config/s3';
import { fireOnRegistration } from '../utils/email-triggers';
import { io } from '../server';

export async function listAttendees(req: AuthRequest, res: Response): Promise<void> {
  try {
    const { eventId } = req.params;
    const { search, status, category, attendance, page = '1', limit = '20' } = req.query;

    // Verify event ownership
    const event = await prisma.event.findFirst({
      where: { id: eventId, organizerId: req.organizerId },
    });

    if (!event) {
      res.status(404).json({ error: 'Event not found' });
      return;
    }

    const pageNum = Math.max(1, parseInt(page as string, 10));
    const limitNum = Math.max(1, Math.min(100, parseInt(limit as string, 10)));
    const skip = (pageNum - 1) * limitNum;

    const where: any = { eventId };

    if (search) {
      where.OR = [
        { name: { contains: search as string, mode: 'insensitive' } },
        { email: { contains: search as string, mode: 'insensitive' } },
        { mobile: { contains: search as string } },
        { shortId: { contains: search as string, mode: 'insensitive' } },
      ];
    }

    if (status) {
      where.status = status;
    }

    if (category) {
      where.categoryId = category;
    }

    if (attendance) {
      where.attendance = attendance;
    }

    const [attendees, total] = await Promise.all([
      prisma.attendee.findMany({
        where,
        include: {
          category: true,
          scanLogs: {
            include: { checkpoint: { select: { name: true, personName: true } } },
            orderBy: { scannedAt: 'asc' },
          },
        },
        orderBy: { createdAt: 'desc' },
        skip,
        take: limitNum,
      }),
      prisma.attendee.count({ where }),
    ]);

    res.json({
      attendees,
      pagination: {
        page: pageNum,
        limit: limitNum,
        total,
        totalPages: Math.ceil(total / limitNum),
      },
    });
  } catch (error) {
    console.error('listAttendees error:', error);
    res.status(500).json({ error: 'Failed to fetch attendees' });
  }
}

export async function addAttendee(req: AuthRequest, res: Response): Promise<void> {
  try {
    const { eventId } = req.params;

    const event = await prisma.event.findFirst({
      where: { id: eventId, organizerId: req.organizerId },
    });

    if (!event) {
      res.status(404).json({ error: 'Event not found' });
      return;
    }

    const { name, email, mobile, countryCode, companyName, jobTitle, numberOfGuests, categoryId, additionalInfo } = req.body;

    if (!name || !mobile) {
      res.status(400).json({ error: 'Name and mobile are required' });
      return;
    }

    const shortId = nanoid(10).toUpperCase();

    // Generate QR code
    const qrBuffer = await generateQRCodeBuffer(shortId);
    const qrKey = `qrcodes/${eventId}/${shortId}.png`;
    const qrCodeUrl = await uploadToS3(qrBuffer, qrKey, 'image/png');

    const attendee = await prisma.attendee.create({
      data: {
        shortId,
        eventId,
        name,
        email,
        mobile,
        countryCode: countryCode || '+91',
        companyName,
        jobTitle,
        numberOfGuests: numberOfGuests ?? 1,
        categoryId,
        additionalInfo,
        qrCodeUrl,
        status: event.requireApproval ? 'PENDING' : 'APPROVED',
        source: 'direct',
      },
      include: { category: true },
    });

    // Fire email trigger (non-blocking)
    fireOnRegistration(eventId, attendee.id).catch(() => {});

    // Notify frontend
    io.to(`event:${eventId}`).emit('attendees:updated', { eventId, synced: 1 });

    res.status(201).json(attendee);
  } catch (error) {
    console.error('addAttendee error:', error);
    res.status(500).json({ error: 'Failed to add attendee' });
  }
}

export async function updateAttendee(req: AuthRequest, res: Response): Promise<void> {
  try {
    const { eventId, id } = req.params;

    const event = await prisma.event.findFirst({
      where: { id: eventId, organizerId: req.organizerId },
    });

    if (!event) {
      res.status(404).json({ error: 'Event not found' });
      return;
    }

    const existing = await prisma.attendee.findFirst({
      where: { id, eventId },
    });

    if (!existing) {
      res.status(404).json({ error: 'Attendee not found' });
      return;
    }

    const { name, email, mobile, countryCode, companyName, jobTitle, numberOfGuests, categoryId, additionalInfo, status, attendance, approvalNote } = req.body;

    const attendee = await prisma.attendee.update({
      where: { id },
      data: {
        ...(name !== undefined && { name }),
        ...(email !== undefined && { email }),
        ...(mobile !== undefined && { mobile }),
        ...(countryCode !== undefined && { countryCode }),
        ...(companyName !== undefined && { companyName }),
        ...(jobTitle !== undefined && { jobTitle }),
        ...(numberOfGuests !== undefined && { numberOfGuests }),
        ...(categoryId !== undefined && { categoryId }),
        ...(additionalInfo !== undefined && { additionalInfo }),
        ...(status !== undefined && { status }),
        ...(attendance !== undefined && { attendance }),
      },
      include: { category: true },
    });

    // Approval audit: log when status transitions to APPROVED or REJECTED
    if (
      status !== undefined &&
      status !== existing.status &&
      (status === 'APPROVED' || status === 'REJECTED')
    ) {
      await prisma.approvalLog.create({
        data: {
          eventId,
          attendeeId: id,
          organizerId: req.organizerId!,
          action: status,
          previousStatus: existing.status,
          note: typeof approvalNote === 'string' && approvalNote.trim() ? approvalNote.trim() : null,
        },
      });
    }

    res.json(attendee);
  } catch (error) {
    console.error('updateAttendee error:', error);
    res.status(500).json({ error: 'Failed to update attendee' });
  }
}

export async function listApprovalLogs(req: AuthRequest, res: Response): Promise<void> {
  try {
    const { eventId } = req.params;
    const event = await prisma.event.findFirst({ where: { id: eventId, organizerId: req.organizerId } });
    if (!event) {
      res.status(404).json({ error: 'Event not found' });
      return;
    }

    const logs = await prisma.approvalLog.findMany({
      where: { eventId },
      orderBy: { createdAt: 'desc' },
      include: {
        attendee: { select: { id: true, name: true, shortId: true, email: true, mobile: true } },
        organizer: { select: { id: true, email: true, name: true } },
      },
    });

    res.json(logs);
  } catch (error) {
    console.error('listApprovalLogs error:', error);
    res.status(500).json({ error: 'Failed to fetch approval logs' });
  }
}

export async function deleteAttendee(req: AuthRequest, res: Response): Promise<void> {
  try {
    const { eventId, id } = req.params;

    const event = await prisma.event.findFirst({
      where: { id: eventId, organizerId: req.organizerId },
    });

    if (!event) {
      res.status(404).json({ error: 'Event not found' });
      return;
    }

    const existing = await prisma.attendee.findFirst({
      where: { id, eventId },
    });

    if (!existing) {
      res.status(404).json({ error: 'Attendee not found' });
      return;
    }

    await prisma.attendee.delete({ where: { id } });

    res.json({ message: 'Attendee deleted' });
  } catch (error) {
    console.error('deleteAttendee error:', error);
    res.status(500).json({ error: 'Failed to delete attendee' });
  }
}

export async function importCsv(req: AuthRequest, res: Response): Promise<void> {
  try {
    const { eventId } = req.params;

    const event = await prisma.event.findFirst({
      where: { id: eventId, organizerId: req.organizerId },
    });
    if (!event) { res.status(404).json({ error: 'Event not found' }); return; }

    const { rows, defaultCategoryId } = req.body;

    if (!Array.isArray(rows) || rows.length === 0) {
      res.status(400).json({ error: 'No rows to import' });
      return;
    }

    // Find default category if not provided
    let fallbackCategoryId = defaultCategoryId || null;
    if (!fallbackCategoryId) {
      const defaultCat = await prisma.ticketCategory.findFirst({
        where: { eventId, isDefault: true },
      });
      if (defaultCat) fallbackCategoryId = defaultCat.id;
    }

    const status = event.requireApproval ? 'PENDING' : 'APPROVED';
    const results = { created: 0, skipped: 0, errors: [] as string[] };

    for (let i = 0; i < rows.length; i++) {
      const row = rows[i];
      if (!row.name || !row.name.trim()) {
        results.skipped++;
        results.errors.push(`Row ${i + 1}: Name is required`);
        continue;
      }

      try {
        const shortId = nanoid(10).toUpperCase();
        const qrBuffer = await generateQRCodeBuffer(shortId);
        const qrCodeUrl = await uploadToS3(qrBuffer, `qr/${shortId}.png`, 'image/png');

        await prisma.attendee.create({
          data: {
            shortId,
            eventId,
            name: row.name.trim(),
            email: row.email?.trim() || null,
            mobile: row.mobile?.trim() || '',
            companyName: row.companyName?.trim() || null,
            jobTitle: row.jobTitle?.trim() || null,
            numberOfGuests: parseInt(row.numberOfGuests) || 1,
            categoryId: row.categoryId || fallbackCategoryId,
            status,
            source: 'csv',
            qrCodeUrl,
          },
        });
        results.created++;
      } catch (err: any) {
        results.skipped++;
        results.errors.push(`Row ${i + 1}: ${err.message}`);
      }
    }

    res.json(results);
  } catch (error) {
    console.error('importCsv error:', error);
    res.status(500).json({ error: 'Failed to import CSV' });
  }
}

export async function exportAttendees(req: AuthRequest, res: Response): Promise<void> {
  try {
    const { eventId } = req.params;

    const event = await prisma.event.findFirst({
      where: { id: eventId, organizerId: req.organizerId },
    });

    if (!event) {
      res.status(404).json({ error: 'Event not found' });
      return;
    }

    const attendees = await prisma.attendee.findMany({
      where: { eventId },
      include: { category: true },
      orderBy: { createdAt: 'desc' },
    });

    const headers = ['Short ID', 'Name', 'Email', 'Mobile', 'Country Code', 'Company', 'Job Title', 'Guests', 'Category', 'Status', 'Attendance', 'Source', 'Created At'];
    const rows = attendees.map((a) => [
      a.shortId,
      a.name,
      a.email || '',
      a.mobile,
      a.countryCode,
      a.companyName || '',
      a.jobTitle || '',
      a.numberOfGuests.toString(),
      a.category?.name || '',
      a.status,
      a.attendance,
      a.source,
      a.createdAt.toISOString(),
    ]);

    const csvContent = [headers, ...rows]
      .map((row) => row.map((cell) => `"${cell.replace(/"/g, '""')}"`).join(','))
      .join('\n');

    res.setHeader('Content-Type', 'text/csv');
    res.setHeader('Content-Disposition', `attachment; filename="attendees-${eventId}.csv"`);
    res.send(csvContent);
  } catch (error) {
    console.error('exportAttendees error:', error);
    res.status(500).json({ error: 'Failed to export attendees' });
  }
}
