import { Router } from 'express';
import { prisma } from '../config/database.js';
import { authenticate } from '../middleware/auth.js';
import { validate } from '../middleware/validate.js';
import { createBookingSchema, rejectBookingSchema, cancelBookingSchema } from '../validators/booking.js';
import { AppError } from '../middleware/errorHandler.js';
import { checkAvailability, calculateCancellationRefund, autoTransitionBooking } from '../utils/rental.js';
import { getPlatformConfig } from '../utils/moderation.js';

const router = Router();

const bookingInclude = {
  rentalListing: {
    select: {
      id: true, title: true, category: true, location: true, cancellationPolicy: true,
      images: { take: 1, orderBy: { order: 'asc' as const } },
    },
  },
  tenant: { select: { id: true, fullName: true, nickname: true, avatar: true } },
  landlord: { select: { id: true, fullName: true, nickname: true, avatar: true } },
  payout: true,
  review: true,
};

// --- List bookings ---
router.get('/', authenticate, async (req, res, next) => {
  try {
    const { role = 'tenant', status } = req.query;
    const where: Record<string, unknown> = role === 'landlord'
      ? { landlordId: req.userId }
      : { tenantId: req.userId };

    if (status && typeof status === 'string') {
      where.status = status;
    }

    const bookings = await prisma.booking.findMany({
      where,
      include: bookingInclude,
      orderBy: { createdAt: 'desc' },
    });

    // Auto-transition stale bookings
    const updated = await Promise.all(bookings.map(b => autoTransitionBooking(b)));

    res.json(updated);
  } catch (error) {
    next(error);
  }
});

// --- Get single booking ---
router.get('/:id', authenticate, async (req, res, next) => {
  try {
    const booking = await prisma.booking.findUnique({
      where: { id: req.params.id },
      include: bookingInclude,
    });
    if (!booking) throw new AppError(404, 'Booking not found');
    if (booking.tenantId !== req.userId && booking.landlordId !== req.userId) {
      throw new AppError(403, 'Not authorized');
    }

    const transitioned = await autoTransitionBooking(booking);
    res.json(transitioned);
  } catch (error) {
    next(error);
  }
});

// --- Create booking request ---
router.post('/', authenticate, validate(createBookingSchema), async (req, res, next) => {
  try {
    const { rentalListingId, periodType, startDate: startStr, endDate: endStr, message } = req.body;

    const rental = await prisma.rentalListing.findUnique({ where: { id: rentalListingId } });
    if (!rental) throw new AppError(404, 'Rental not found');
    if (rental.status !== 'ACTIVE') throw new AppError(400, 'Rental is not active');
    if (rental.landlordId === req.userId) throw new AppError(400, 'Cannot book your own rental');

    const startDate = new Date(startStr);
    const endDate = new Date(endStr);
    if (startDate >= endDate) throw new AppError(400, 'End date must be after start date');
    if (startDate < new Date()) throw new AppError(400, 'Start date must be in the future');

    // Check price exists for period type
    if (periodType === 'DAILY' && !rental.dailyPrice) throw new AppError(400, 'Daily rental not available');
    if (periodType === 'MONTHLY' && !rental.monthlyPrice) throw new AppError(400, 'Monthly rental not available');

    // Check availability
    const available = await checkAvailability(rentalListingId, startDate, endDate);
    if (!available) throw new AppError(409, 'Selected dates are not available');

    // Calculate pricing
    const pricePerPeriod = periodType === 'DAILY' ? rental.dailyPrice! : rental.monthlyPrice!;
    let totalPeriods: number;
    if (periodType === 'DAILY') {
      totalPeriods = Math.ceil((endDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1000));
    } else {
      totalPeriods = Math.ceil((endDate.getTime() - startDate.getTime()) / (30 * 24 * 60 * 60 * 1000));
    }
    if (totalPeriods < 1) totalPeriods = 1;

    // Min/max checks
    if (periodType === 'DAILY') {
      if (rental.minDays && totalPeriods < rental.minDays) throw new AppError(400, `Minimum ${rental.minDays} days required`);
      if (rental.maxDays && totalPeriods > rental.maxDays) throw new AppError(400, `Maximum ${rental.maxDays} days allowed`);
    } else {
      if (rental.minMonths && totalPeriods < rental.minMonths) throw new AppError(400, `Minimum ${rental.minMonths} months required`);
      if (rental.maxMonths && totalPeriods > rental.maxMonths) throw new AppError(400, `Maximum ${rental.maxMonths} months allowed`);
    }

    const config = await getPlatformConfig();
    const subtotal = pricePerPeriod * totalPeriods;
    const commissionRate = config.rentalCommissionPercent;
    const commissionAmount = subtotal * (commissionRate / 100);
    const depositAmount = rental.depositAmount || 0;
    const totalAmount = subtotal + depositAmount;

    const booking = await prisma.booking.create({
      data: {
        rentalListingId,
        tenantId: req.userId!,
        landlordId: rental.landlordId,
        periodType,
        startDate,
        endDate,
        pricePerPeriod,
        totalPeriods,
        subtotal,
        commissionRate,
        commissionAmount,
        depositAmount,
        totalAmount,
        message,
        expiresAt: new Date(Date.now() + config.bookingExpiryHours * 60 * 60 * 1000),
      },
      include: bookingInclude,
    });

    // Notify landlord
    const tenant = await prisma.user.findUnique({ where: { id: req.userId }, select: { fullName: true } });
    const notification = await prisma.notification.create({
      data: {
        userId: rental.landlordId,
        type: 'BOOKING_REQUEST',
        title: 'New Booking Request',
        body: `${tenant?.fullName || 'Someone'} requested to book "${rental.title}"`,
        data: { bookingId: booking.id, rentalListingId },
      },
    });

    const io = req.app.get('io');
    if (io) {
      io.to(`user:${rental.landlordId}`).emit('new_notification', notification);
    }

    res.status(201).json(booking);
  } catch (error) {
    next(error);
  }
});

// --- Confirm booking (landlord) ---
router.patch('/:id/confirm', authenticate, async (req, res, next) => {
  try {
    const booking = await prisma.booking.findUnique({ where: { id: req.params.id } });
    if (!booking) throw new AppError(404, 'Booking not found');
    if (booking.landlordId !== req.userId) throw new AppError(403, 'Not authorized');
    if (booking.status !== 'PENDING') throw new AppError(400, 'Can only confirm pending bookings');

    // Check if expired
    if (booking.expiresAt && booking.expiresAt < new Date()) {
      await prisma.booking.update({ where: { id: booking.id }, data: { status: 'EXPIRED' } });
      throw new AppError(400, 'Booking has expired');
    }

    const updated = await prisma.booking.update({
      where: { id: req.params.id },
      data: { status: 'CONFIRMED' },
      include: bookingInclude,
    });

    // Notify tenant
    const notification = await prisma.notification.create({
      data: {
        userId: booking.tenantId,
        type: 'BOOKING_CONFIRMED',
        title: 'Booking Confirmed',
        body: `Your booking has been confirmed! Please proceed with payment.`,
        data: { bookingId: booking.id },
      },
    });

    const io = req.app.get('io');
    if (io) {
      io.to(`user:${booking.tenantId}`).emit('new_notification', notification);
    }

    res.json(updated);
  } catch (error) {
    next(error);
  }
});

// --- Reject booking (landlord) ---
router.patch('/:id/reject', authenticate, validate(rejectBookingSchema), async (req, res, next) => {
  try {
    const booking = await prisma.booking.findUnique({ where: { id: req.params.id } });
    if (!booking) throw new AppError(404, 'Booking not found');
    if (booking.landlordId !== req.userId) throw new AppError(403, 'Not authorized');
    if (booking.status !== 'PENDING') throw new AppError(400, 'Can only reject pending bookings');

    const updated = await prisma.booking.update({
      where: { id: req.params.id },
      data: { status: 'REJECTED', rejectionReason: req.body.reason },
      include: bookingInclude,
    });

    const notification = await prisma.notification.create({
      data: {
        userId: booking.tenantId,
        type: 'BOOKING_REJECTED',
        title: 'Booking Rejected',
        body: `Your booking was rejected. Reason: ${req.body.reason}`,
        data: { bookingId: booking.id },
      },
    });

    const io = req.app.get('io');
    if (io) {
      io.to(`user:${booking.tenantId}`).emit('new_notification', notification);
    }

    res.json(updated);
  } catch (error) {
    next(error);
  }
});

// --- Cancel booking (either side) ---
router.patch('/:id/cancel', authenticate, validate(cancelBookingSchema), async (req, res, next) => {
  try {
    const booking = await prisma.booking.findUnique({
      where: { id: req.params.id },
      include: { rentalListing: true },
    });
    if (!booking) throw new AppError(404, 'Booking not found');
    if (booking.tenantId !== req.userId && booking.landlordId !== req.userId) {
      throw new AppError(403, 'Not authorized');
    }

    const isTenant = booking.tenantId === req.userId;
    const cancelStatus = isTenant ? 'CANCELLED_BY_TENANT' : 'CANCELLED_BY_LANDLORD';

    if (!['PENDING', 'CONFIRMED', 'ACTIVE'].includes(booking.status)) {
      throw new AppError(400, 'Cannot cancel this booking');
    }

    // Calculate refund if already paid
    let refundAmount = 0;
    let depositRefund = booking.depositAmount;
    if (booking.status === 'CONFIRMED' || booking.status === 'ACTIVE') {
      const refund = calculateCancellationRefund(
        booking.rentalListing.cancellationPolicy,
        booking.startDate,
        booking.subtotal,
        booking.depositAmount,
      );
      refundAmount = refund.refundAmount;
      depositRefund = refund.depositRefund;
    }

    const updated = await prisma.booking.update({
      where: { id: req.params.id },
      data: {
        status: cancelStatus as any,
        cancellationReason: req.body.reason,
      },
      include: bookingInclude,
    });

    // Notify other party
    const recipientId = isTenant ? booking.landlordId : booking.tenantId;
    const notification = await prisma.notification.create({
      data: {
        userId: recipientId,
        type: 'BOOKING_CANCELLED',
        title: 'Booking Cancelled',
        body: `A booking has been cancelled. Reason: ${req.body.reason}`,
        data: { bookingId: booking.id, refundAmount, depositRefund },
      },
    });

    const io = req.app.get('io');
    if (io) {
      io.to(`user:${recipientId}`).emit('new_notification', notification);
    }

    res.json({ ...updated, refundAmount, depositRefund });
  } catch (error) {
    next(error);
  }
});

// --- Complete booking (landlord) ---
router.patch('/:id/complete', authenticate, async (req, res, next) => {
  try {
    const booking = await prisma.booking.findUnique({ where: { id: req.params.id } });
    if (!booking) throw new AppError(404, 'Booking not found');
    if (booking.landlordId !== req.userId) throw new AppError(403, 'Not authorized');
    if (booking.status !== 'ACTIVE' && booking.status !== 'CONFIRMED') {
      throw new AppError(400, 'Can only complete active/confirmed bookings');
    }

    const updated = await prisma.booking.update({
      where: { id: req.params.id },
      data: { status: 'COMPLETED' },
      include: bookingInclude,
    });

    // Create payout
    const netAmount = booking.subtotal - booking.commissionAmount;
    await prisma.payout.create({
      data: {
        bookingId: booking.id,
        landlordId: booking.landlordId,
        grossAmount: booking.subtotal,
        commissionAmount: booking.commissionAmount,
        netAmount,
        status: 'PENDING',
      },
    });

    // Notify tenant
    const notification = await prisma.notification.create({
      data: {
        userId: booking.tenantId,
        type: 'BOOKING_COMPLETED',
        title: 'Booking Completed',
        body: 'Your booking has been completed. Please leave a review!',
        data: { bookingId: booking.id },
      },
    });

    const io = req.app.get('io');
    if (io) {
      io.to(`user:${booking.tenantId}`).emit('new_notification', notification);
    }

    res.json(updated);
  } catch (error) {
    next(error);
  }
});

export default router;
