import { Injectable, Logger } from "@nestjs/common";
import { Cron } from "@nestjs/schedule";
import { PrismaService } from "../../prisma/prisma.service";
import { BookingRequestStatus, BookingStatus, OfferState, PaymentStatus } from "@prisma/client";
import { PushService } from "../push/push.service";

@Injectable()
export class ExpiryJobs {
  private logger = new Logger(ExpiryJobs.name);
  constructor(private prisma: PrismaService, private push: PushService) {}

  @Cron("*/1 * * * *")
  async expireOffers() {
    const now = new Date();
    const res = await this.prisma.bookingOffer.updateMany({
      where: { state: OfferState.offered, expiresAt: { lt: now } },
      data: { state: OfferState.timed_out, respondedAt: now },
    });
    if (res.count) this.logger.log(`Expired offers: ${res.count}`);
  }

  @Cron("*/1 * * * *")
  async expirePendingDeposits() {
    const now = new Date();

    const bookings = await this.prisma.booking.findMany({
      where: { status: BookingStatus.pending_deposit, depositDeadlineAt: { lt: now } },
      select: { id: true, bookingRequestId: true, providerId: true, clientId: true },
      take: 200,
    });

    for (const b of bookings) {
      await this.prisma.$transaction(async (tx) => {
        await tx.booking.update({
          where: { id: b.id },
          data: { status: BookingStatus.cancelled, updatedAt: now },
        });
        await tx.bookingStatusEvent.create({
          data: { bookingId: b.id, status: "deposit_expired_cancelled", actorUserId: null, meta: {} },
        });

        await tx.paymentIntent.updateMany({
          where: { bookingId: b.id, type: "DEPOSIT", status: { in: [PaymentStatus.initiated, PaymentStatus.pending] } },
          data: { status: PaymentStatus.expired, updatedAt: now },
        });

        const req = await tx.bookingRequest.findUnique({ where: { id: b.bookingRequestId } });
        if (req) {
          const shouldExpire = req.expiresAt ? req.expiresAt < now : false;
          await tx.bookingRequest.update({
            where: { id: req.id },
            data: { status: shouldExpire ? BookingRequestStatus.expired : BookingRequestStatus.open, matchedProviderId: null, matchedOfferId: null, updatedAt: now },
          });

          await tx.bookingOffer.updateMany({
            where: { bookingRequestId: req.id, state: OfferState.accepted },
            data: { state: OfferState.cancelled, respondedAt: now },
          });
        }

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

      // Push both sides
      await this.push.sendToUser(b.clientId, {
        title: "Deposit expired",
        body: "Deposit window ended. Your request has been reopened.",
        data: { type: "DEPOSIT_EXPIRED", bookingRequestId: b.bookingRequestId },
      });

      const provider = await this.prisma.provider.findUnique({ where: { id: b.providerId } });
      if (provider) {
        await this.push.sendToUser(provider.userId, {
          title: "Client did not pay",
          body: "Deposit window expired. You are free for other jobs.",
          data: { type: "DEPOSIT_EXPIRED_PROVIDER", bookingRequestId: b.bookingRequestId },
        });
      }
    }

    if (bookings.length) this.logger.log(`Expired pending deposits: ${bookings.length}`);
  }
}
