import { NextRequest, NextResponse } from 'next/server';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { db } from '@/lib/db';
import { createBookingSchema } from '@smashpoint/shared';
import { Decimal } from '@prisma/client/runtime/library';

// Helper function to check if a time is premium (after 18:00 or weekend)
function isPremiumTime(date: Date, timeMinutes: number): boolean {
  const dayOfWeek = date.getDay(); // 0 = Sunday, 6 = Saturday
  const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
  const isEveningTime = timeMinutes >= 18 * 60; // After 18:00
  return isWeekend || isEveningTime;
}

// GET /api/bookings - List bookings with filters
export async function GET(request: NextRequest) {
  try {
    const session = await getServerSession(authOptions);

    if (!session?.user) {
      return NextResponse.json(
        { error: 'No autorizado' },
        { status: 401 }
      );
    }

    const { searchParams } = new URL(request.url);
    const siteId = searchParams.get('siteId');
    const date = searchParams.get('date');
    const status = searchParams.get('status');
    const clientId = searchParams.get('clientId');
    const courtId = searchParams.get('courtId');

    // Build the where clause
    interface BookingWhereClause {
      site: {
        organizationId: string;
        id?: string;
      };
      status?: 'PENDING' | 'CONFIRMED' | 'CANCELLED' | 'COMPLETED' | 'NO_SHOW';
      clientId?: string;
      courtId?: string;
      startTime?: {
        gte: Date;
        lt: Date;
      };
    }

    const whereClause: BookingWhereClause = {
      site: {
        organizationId: session.user.organizationId,
      },
    };

    // Filter by site
    if (siteId) {
      whereClause.site.id = siteId;
    } else if (session.user.siteId) {
      whereClause.site.id = session.user.siteId;
    }

    // Filter by status
    if (status) {
      const validStatuses = ['PENDING', 'CONFIRMED', 'CANCELLED', 'COMPLETED', 'NO_SHOW'] as const;
      if (validStatuses.includes(status.toUpperCase() as typeof validStatuses[number])) {
        whereClause.status = status.toUpperCase() as typeof validStatuses[number];
      }
    }

    // Filter by client
    if (clientId) {
      whereClause.clientId = clientId;
    }

    // Filter by court
    if (courtId) {
      whereClause.courtId = courtId;
    }

    // Filter by date
    if (date) {
      const dateRegex = /^\d{4}-\d{2}-\d{2}$/;
      if (dateRegex.test(date)) {
        const startOfDay = new Date(date);
        startOfDay.setHours(0, 0, 0, 0);
        const endOfDay = new Date(date);
        endOfDay.setHours(23, 59, 59, 999);

        whereClause.startTime = {
          gte: startOfDay,
          lt: new Date(endOfDay.getTime() + 1),
        };
      }
    }

    const bookings = await db.booking.findMany({
      where: whereClause,
      include: {
        court: {
          select: {
            id: true,
            name: true,
            type: true,
            pricePerHour: true,
          },
        },
        client: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
            email: true,
            phone: true,
          },
        },
        site: {
          select: {
            id: true,
            name: true,
            timezone: true,
          },
        },
        createdBy: {
          select: {
            id: true,
            firstName: true,
            lastName: true,
          },
        },
      },
      orderBy: [
        { startTime: 'asc' },
      ],
    });

    return NextResponse.json(bookings);
  } catch (error) {
    console.error('Error fetching bookings:', error);
    return NextResponse.json(
      { error: 'Error al obtener las reservas' },
      { status: 500 }
    );
  }
}

// POST /api/bookings - Create a new booking
export async function POST(request: NextRequest) {
  try {
    const session = await getServerSession(authOptions);

    if (!session?.user) {
      return NextResponse.json(
        { error: 'No autorizado' },
        { status: 401 }
      );
    }

    const body = await request.json();

    // Validate input with Zod schema
    const validationResult = createBookingSchema.safeParse(body);
    if (!validationResult.success) {
      return NextResponse.json(
        {
          error: 'Datos de reserva inválidos',
          details: validationResult.error.flatten().fieldErrors,
        },
        { status: 400 }
      );
    }

    const { courtId, clientId, startTime, endTime, paymentType, notes, participants } = validationResult.data;

    // Check court exists and belongs to user's organization
    const court = await db.court.findFirst({
      where: {
        id: courtId,
        site: {
          organizationId: session.user.organizationId,
        },
      },
      include: {
        site: {
          select: {
            id: true,
            name: true,
            organizationId: true,
          },
        },
      },
    });

    if (!court) {
      return NextResponse.json(
        { error: 'Cancha no encontrada o no pertenece a su organización' },
        { status: 404 }
      );
    }

    if (court.status !== 'AVAILABLE' || !court.isActive) {
      return NextResponse.json(
        { error: 'La cancha no está disponible para reservas' },
        { status: 400 }
      );
    }

    // Check client exists and belongs to user's organization
    const client = await db.client.findFirst({
      where: {
        id: clientId,
        organizationId: session.user.organizationId,
      },
      include: {
        memberships: {
          where: {
            status: 'ACTIVE',
            endDate: {
              gte: new Date(),
            },
          },
          include: {
            plan: true,
          },
          orderBy: {
            endDate: 'desc',
          },
          take: 1,
        },
      },
    });

    if (!client) {
      return NextResponse.json(
        { error: 'Cliente no encontrado o no pertenece a su organización' },
        { status: 404 }
      );
    }

    // Check availability - no existing booking at that time
    const bookingStartTime = new Date(startTime);
    const bookingEndTime = new Date(endTime);

    const existingBooking = await db.booking.findFirst({
      where: {
        courtId,
        status: {
          in: ['PENDING', 'CONFIRMED'],
        },
        OR: [
          // New booking starts during existing booking
          {
            startTime: { lte: bookingStartTime },
            endTime: { gt: bookingStartTime },
          },
          // New booking ends during existing booking
          {
            startTime: { lt: bookingEndTime },
            endTime: { gte: bookingEndTime },
          },
          // New booking encompasses existing booking
          {
            startTime: { gte: bookingStartTime },
            endTime: { lte: bookingEndTime },
          },
        ],
      },
    });

    if (existingBooking) {
      return NextResponse.json(
        { error: 'Ya existe una reserva en ese horario. Por favor, seleccione otro horario.' },
        { status: 409 }
      );
    }

    // Calculate duration in minutes
    const durationMs = bookingEndTime.getTime() - bookingStartTime.getTime();
    const durationMinutes = durationMs / (1000 * 60);
    const durationHours = durationMinutes / 60;

    // Calculate price (regular vs premium)
    const startMinutes = bookingStartTime.getHours() * 60 + bookingStartTime.getMinutes();
    const isPremium = isPremiumTime(bookingStartTime, startMinutes);
    const basePrice = Number(court.pricePerHour);
    const premiumMultiplier = 1.25;
    const hourlyRate = isPremium ? basePrice * premiumMultiplier : basePrice;
    let totalPrice = hourlyRate * durationHours;

    // Check client membership for discounts
    const activeMembership = client.memberships[0];
    let usedFreeHours = false;
    let appliedDiscount = 0;
    let finalPaymentType = paymentType.toUpperCase() as 'CASH' | 'CARD' | 'TRANSFER' | 'MEMBERSHIP' | 'FREE';

    if (activeMembership) {
      const plan = activeMembership.plan;

      // Check if membership has free hours remaining
      if (plan.courtHours && activeMembership.remainingHours && activeMembership.remainingHours > 0) {
        // Client has free hours, use them
        const hoursToUse = Math.min(activeMembership.remainingHours, durationHours);

        if (hoursToUse >= durationHours) {
          // Fully covered by free hours
          totalPrice = 0;
          usedFreeHours = true;
          finalPaymentType = 'FREE';
        } else {
          // Partial coverage - calculate remaining
          const remainingHours = durationHours - hoursToUse;
          totalPrice = hourlyRate * remainingHours;
          usedFreeHours = true;
        }
      } else if (plan.discountPercent) {
        // Apply booking discount percentage
        const discountPercent = Number(plan.discountPercent);
        appliedDiscount = (totalPrice * discountPercent) / 100;
        totalPrice = totalPrice - appliedDiscount;
        finalPaymentType = 'MEMBERSHIP';
      }
    }

    // Round price to 2 decimal places
    totalPrice = Math.round(totalPrice * 100) / 100;

    // Create booking with transaction (to update membership hours if needed)
    const booking = await db.$transaction(async (tx) => {
      // Update membership hours if free hours were used
      if (usedFreeHours && activeMembership) {
        const hoursUsed = Math.min(activeMembership.remainingHours || 0, durationHours);
        await tx.membership.update({
          where: { id: activeMembership.id },
          data: {
            remainingHours: {
              decrement: hoursUsed,
            },
          },
        });
      }

      // Create the booking
      const newBooking = await tx.booking.create({
        data: {
          siteId: court.siteId,
          courtId,
          clientId,
          createdById: session.user.id,
          startTime: bookingStartTime,
          endTime: bookingEndTime,
          status: 'PENDING',
          paymentType: finalPaymentType,
          totalPrice: new Decimal(totalPrice),
          paidAmount: new Decimal(0),
          notes: notes || null,
          playerNames: participants?.map(p => p.name) || [],
          isRecurring: false,
        },
        include: {
          court: {
            select: {
              id: true,
              name: true,
              type: true,
              pricePerHour: true,
            },
          },
          client: {
            select: {
              id: true,
              firstName: true,
              lastName: true,
              email: true,
              phone: true,
            },
          },
          site: {
            select: {
              id: true,
              name: true,
              timezone: true,
            },
          },
        },
      });

      return newBooking;
    });

    return NextResponse.json(booking, { status: 201 });
  } catch (error) {
    console.error('Error creating booking:', error);
    return NextResponse.json(
      { error: 'Error al crear la reserva' },
      { status: 500 }
    );
  }
}
