import { BadRequestException, Injectable } from "@nestjs/common";
import { PrismaService } from "../../prisma/prisma.service";
import { CreateBookingRequestDto } from "./dto/create-booking-request.dto";
import { BookingRequestStatus, OfferState, RequestType } from "@prisma/client";
import { PushService } from "../push/push.service";

@Injectable()
export class BookingRequestsService {
  constructor(private prisma: PrismaService, private push: PushService) {}

  async createBookingRequest(clientId: string, dto: CreateBookingRequestDto) {
    if (!dto.isAsap && !dto.desiredTime) throw new BadRequestException("desiredTime required if not ASAP");

    const expiresAt = new Date(Date.now() + 30 * 60 * 1000);

    const req = await this.prisma.bookingRequest.create({
      data: {
        clientId,
        categoryId: dto.categoryId,
        serviceId: dto.serviceId ?? null,
        requestType: RequestType.BROADCAST,
        desiredTime: dto.desiredTime ? new Date(dto.desiredTime) : null,
        isAsap: dto.isAsap,
        serviceMode: dto.serviceMode as any,
        clientLat: dto.location.lat as any,
        clientLng: dto.location.lng as any,
        addressText: dto.location.addressText ?? null,
        budgetMin: dto.budgetMin as any,
        budgetMax: dto.budgetMax as any,
        notes: dto.notes ?? null,
        status: BookingRequestStatus.open,
        expiresAt,
      },
    });

    await this.broadcast(req.id);
    return { requestId: req.id, status: req.status };
  }

  async createFavouriteDirectRequest(clientId: string, providerId: string, dto: CreateBookingRequestDto) {
    const expiresAt = new Date(Date.now() + 30 * 60 * 1000);

    const req = await this.prisma.bookingRequest.create({
      data: {
        clientId,
        categoryId: dto.categoryId,
        serviceId: dto.serviceId ?? null,
        requestType: RequestType.FAVOURITE_DIRECT,
        desiredTime: dto.desiredTime ? new Date(dto.desiredTime) : null,
        isAsap: dto.isAsap,
        serviceMode: dto.serviceMode as any,
        clientLat: dto.location.lat as any,
        clientLng: dto.location.lng as any,
        addressText: dto.location.addressText ?? null,
        notes: dto.notes ?? null,
        status: BookingRequestStatus.open,
        expiresAt,
      },
    });

    const offerExpiresAt = new Date(Date.now() + Number(process.env.OFFER_WINDOW_SECONDS ?? 60) * 1000);
    await this.prisma.bookingOffer.create({
      data: {
        bookingRequestId: req.id,
        providerId,
        score: 999 as any,
        state: OfferState.offered,
        expiresAt: offerExpiresAt,
      },
    });

    // Push provider
    const provider = await this.prisma.provider.findUnique({ where: { id: providerId } });
    if (provider) {
      await this.push.sendToUser(provider.userId, {
        title: "New direct request",
        body: "A client requested you directly. Open offers to accept.",
        data: { type: "OFFER", bookingRequestId: req.id },
      });
    }

    return { requestId: req.id, offeredProviderId: providerId };
  }

  async broadcast(bookingRequestId: string) {
    const req = await this.prisma.bookingRequest.findUnique({ where: { id: bookingRequestId } });
    if (!req || req.status !== BookingRequestStatus.open) return;

    const offerWindowSec = Number(process.env.OFFER_WINDOW_SECONDS ?? 60);
    const expiresAt = new Date(Date.now() + offerWindowSec * 1000);

    const providers = await this.prisma.provider.findMany({
      where: { services: { some: { categoryId: req.categoryId, active: true } } },
      take: 20,
      orderBy: { ratingAvg: "desc" },
      select: { id: true, userId: true },
    });

    if (!providers.length) return;

    await this.prisma.bookingOffer.createMany({
      data: providers.map((p, idx) => ({
        bookingRequestId: req.id,
        providerId: p.id,
        score: (1000 - idx) as any,
        state: OfferState.offered,
        expiresAt,
      })),
      skipDuplicates: true,
    });

    // Push all providers
    for (const p of providers) {
      await this.push.sendToUser(p.userId, {
        title: "New service request",
        body: "A client needs your service. Accept within the time limit.",
        data: { type: "OFFER", bookingRequestId: req.id },
      });
    }
  }

  async getRequest(clientId: string, id: string) {
    const req = await this.prisma.bookingRequest.findFirst({
      where: { id, clientId },
      include: { offers: true, booking: true },
    });
    if (!req) throw new BadRequestException("Not found");
    return req;
  }

  async cancelRequest(clientId: string, id: string) {
    const req = await this.prisma.bookingRequest.findFirst({ where: { id, clientId } });
    if (!req) throw new BadRequestException("Not found");

    await this.prisma.bookingRequest.update({
      where: { id },
      data: { status: BookingRequestStatus.cancelled },
    });

    await this.prisma.bookingOffer.updateMany({
      where: { bookingRequestId: id, state: OfferState.offered },
      data: { state: OfferState.cancelled },
    });

    return { ok: true };
  }
}
