import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
import { HydratedDocument, Types } from 'mongoose'

export type SlotLockDocument = HydratedDocument<SlotLock>

// Atomic double-booking guard (§8.2 layer 1). The unique (hostId, startUtc) index
// is the hard guarantee: two concurrent confirmations for the same start can't
// both insert — the loser gets a duplicate-key error and a clean "slot taken".
//
// Lifecycle:
//  - hold during checkout: expiresAt = now + N min (TTL reaps abandoned holds).
//  - confirmed: bookingId set, expiresAt = null. MongoDB's TTL index only deletes
//    docs whose indexed field is a Date, so a null expiresAt makes the lock
//    permanent — keeping the slot blocked for the life of the booking.
@Schema({ collection: 'scheduling_slot_locks', timestamps: true })
export class SlotLock {
  @Prop({ type: Types.ObjectId, ref: 'Tenant', required: true, index: true })
  tenantId!: Types.ObjectId

  @Prop({ type: Types.ObjectId, ref: 'Host', required: true })
  hostId!: Types.ObjectId

  @Prop({ required: true })
  startUtc!: Date

  @Prop({ required: true })
  endUtc!: Date

  // TTL anchor for un-confirmed holds; null once confirmed (permanent).
  @Prop({ type: Date, default: null })
  expiresAt!: Date | null

  // Opaque token returned by POST /holds. A confirm may only claim an existing
  // hold by presenting its token — so a confirm without the token falls through
  // to a fresh unique insert (dup-key => "slot taken") and can never hijack
  // someone else's active hold.
  @Prop({ type: String, default: null })
  holdToken!: string | null

  @Prop({ type: Types.ObjectId, ref: 'Booking', default: null })
  bookingId!: Types.ObjectId | null
}

export const SlotLockSchema = SchemaFactory.createForClass(SlotLock)
// Hard uniqueness: one lock per (host, start).
SlotLockSchema.index({ hostId: 1, startUtc: 1 }, { unique: true })
// TTL: reap abandoned holds. expireAfterSeconds:0 => delete when expiresAt passes;
// docs with expiresAt=null (confirmed) are never reaped.
SlotLockSchema.index({ expiresAt: 1 }, { expireAfterSeconds: 0 })
