import { BadRequestException, Injectable, UnauthorizedException } from "@nestjs/common";
import { PrismaService } from "../../prisma/prisma.service";
import { BookingStatus } from "@prisma/client";

@Injectable()
export class BookingsService {
  constructor(private prisma: PrismaService) {}

  async listMyBookings(userId: string) {
    const provider = await this.prisma.provider.findFirst({ where: { userId } });

    const where = provider
      ? { OR: [{ clientId: userId }, { providerId: provider.id }] }
      : { clientId: userId };

    return this.prisma.booking.findMany({
      where,
      orderBy: { createdAt: "desc" },
      include: { payments: true, events: true },
      take: 50,
    });
  }

  async getBookingForUser(userId: string, bookingId: string) {
    const provider = await this.prisma.provider.findFirst({ where: { userId } });

    const booking = await this.prisma.booking.findUnique({
      where: { id: bookingId },
      include: { payments: true, events: true, request: true },
    });

    if (!booking) throw new BadRequestException("Not found");

    const allowed =
      booking.clientId === userId || (provider && booking.providerId === provider.id);

    if (!allowed) throw new UnauthorizedException();

    return {
      id: booking.id,
      status: booking.status,
      priceAmount: Number(booking.priceAmount),
      currency: booking.currency,
      recommendedDepositAmount: Number(booking.recommendedDepositAmount),
      minDepositAmount: Number(booking.minDepositAmount),
      maxDepositAmount: Number(booking.maxDepositAmount),
      depositAmountSelected: booking.depositAmountSelected ? Number(booking.depositAmountSelected) : null,
      depositDeadlineAt: booking.depositDeadlineAt,
      scheduledFor: booking.scheduledFor,
      serviceMode: booking.serviceMode,
      providerId: booking.providerId,
      clientId: booking.clientId,
    };
  }

  async updateStatus(userId: string, bookingId: string, dto: { status: string; note?: string }) {
    const provider = await this.prisma.provider.findFirst({ where: { userId } });
    if (!provider) throw new BadRequestException("Not a provider");

    return this.prisma.$transaction(async (tx) => {
      const booking = await tx.booking.findUnique({ where: { id: bookingId } });
      if (!booking) throw new BadRequestException("Not found");
      if (booking.providerId !== provider.id) throw new UnauthorizedException();

      const allowedFrom = [
        BookingStatus.confirmed,
        BookingStatus.provider_en_route,
        BookingStatus.in_progress,
      ];
      if (!allowedFrom.includes(booking.status)) throw new BadRequestException("Booking not active");

      const next = dto.status as BookingStatus;

      if (next === BookingStatus.completed && booking.status === BookingStatus.confirmed) {
        throw new BadRequestException("Start first");
      }

      const updated = await tx.booking.update({
        where: { id: bookingId },
        data: { status: next, updatedAt: new Date() },
      });

      await tx.bookingStatusEvent.create({
        data: { bookingId, status: next, actorUserId: userId, meta: { note: dto.note ?? null } },
      });

      if (next === BookingStatus.completed) {
        await tx.provider.update({
          where: { id: provider.id },
          data: { jobsCompleted: { increment: 1 } },
        });
      }

      return { id: updated.id, status: updated.status };
    });
  }

  async cancelBooking(userId: string, bookingId: string, dto: { reason?: string }) {
    const provider = await this.prisma.provider.findFirst({ where: { userId } });

    return this.prisma.$transaction(async (tx) => {
      const booking = await tx.booking.findUnique({ where: { id: bookingId } });
      if (!booking) throw new BadRequestException("Not found");

      const allowed = booking.clientId === userId || (provider && booking.providerId === provider.id);
      if (!allowed) throw new UnauthorizedException();

      if ([BookingStatus.completed].includes(booking.status)) {
        throw new BadRequestException("Cannot cancel completed booking");
      }

      const updated = await tx.booking.update({
        where: { id: bookingId },
        data: { status: BookingStatus.cancelled, updatedAt: new Date() },
      });

      await tx.bookingStatusEvent.create({
        data: { bookingId, status: "cancelled", actorUserId: userId, meta: { reason: dto.reason ?? null } },
      });

      await tx.bookingTimeLock.deleteMany({
        where: { bookingRequestId: booking.bookingRequestId, providerId: booking.providerId },
      });

      await tx.bookingRequest.update({
        where: { id: booking.bookingRequestId },
        data: { status: "cancelled", updatedAt: new Date(), matchedProviderId: null, matchedOfferId: null },
      });

      return { id: updated.id, status: updated.status };
    });
  }
}
