// Phase 1 schema — Futsal/Cricshall booking + advance payment + booking streak.
// Do not pull Phase 2/3 features (team completion, play streak, reviews) into this file
// without product sign-off. See ../../spec.md.

generator client {
  provider      = "prisma-client-js"
  // cPanel/CloudLinux host (titan) runs OpenSSL 1.1; native target is for local dev.
  binaryTargets = ["native", "debian-openssl-1.1.x"]
}

datasource db {
  provider  = "postgresql"
  // Runtime queries go through the transaction pooler (port 6543, pgbouncer).
  url       = env("DATABASE_URL")
  // Migrations and introspection need a direct/session connection because
  // pgbouncer doesn't support all the protocol features Prisma uses for DDL.
  directUrl = env("DIRECT_URL")
}

enum Role {
  GUEST
  PLAYER
  GROUND_OWNER
  ADMIN
}

enum GroundStatus {
  PENDING_APPROVAL
  ACTIVE
  SUSPENDED
}

enum BookingStatus {
  PENDING_PAYMENT
  CONFIRMED
  CANCELLED
  COMPLETED
  NO_SHOW
}

enum SportType {
  FUTSAL
  CRICSHALL
}

enum GroundRequestStatus {
  PENDING
  APPROVED
  REJECTED
}

model User {
  id           String   @id @default(cuid())
  email        String   @unique
  passwordHash String
  phone        String   @unique
  name         String
  role         Role     @default(PLAYER)
  // When set, the password is temporary and must be replaced before this
  // timestamp; logins after it expires are rejected. Cleared on a successful
  // password change. Used for the admin-issued 24h owner invite.
  passwordExpiresAt DateTime?
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  ownedGrounds     Ground[]        @relation("GroundOwner")
  bookings         Booking[]
  bookingStreak    BookingStreak?
  reviewedRequests GroundRequest[] @relation("GroundRequestReviewer")
}

model Ground {
  id        String       @id @default(cuid())
  // Optional during early data-entry — admins seed venues before an owner
  // exists. Bookings/slot mgmt require an owner so the booking pipeline
  // should still 404 when ownerId is null.
  ownerId   String?
  owner     User?        @relation("GroundOwner", fields: [ownerId], references: [id])
  name      String
  address   String
  lat       Float
  lng       Float
  sport     SportType
  status    GroundStatus @default(PENDING_APPROVAL)
  // Cloudinary secure URLs of venue photos. Stored as an array so order is
  // preserved (first image is the cover). Upload lives in admin/ via the
  // Cloudinary SDK; the backend only stores the resulting URLs.
  imageUrls String[]     @default([])
  createdAt DateTime     @default(now())
  updatedAt DateTime     @updatedAt

  slots   Slot[]
  request GroundRequest? // back-ref when this ground was created from a request

  @@index([status, sport])
  @@index([lat, lng])
}

model Slot {
  id              String   @id @default(cuid())
  groundId        String
  ground          Ground   @relation(fields: [groundId], references: [id], onDelete: Cascade)
  startsAt        DateTime
  endsAt          DateTime
  priceNpr        Int
  advanceNpr      Int
  isBlocked       Boolean  @default(false)
  createdAt       DateTime @default(now())

  booking Booking?

  @@unique([groundId, startsAt])
  @@index([startsAt])
}

model Booking {
  id            String        @id @default(cuid())
  userId        String
  user          User          @relation(fields: [userId], references: [id])
  slotId        String        @unique
  slot          Slot          @relation(fields: [slotId], references: [id])
  status        BookingStatus @default(PENDING_PAYMENT)
  totalNpr      Int
  advancePaidNpr Int          @default(0)
  createdAt     DateTime      @default(now())
  updatedAt     DateTime      @updatedAt

  @@index([userId, status])
}

// A submission from the main site asking for a new venue to be added.
// Admin reviews these in the admin app: approving creates the Ground (and
// optionally the GROUND_OWNER User via the invite flow). Owners cannot
// create grounds directly — every active ground originates from a request
// (or an admin-only direct add).
model GroundRequest {
  id        String              @id @default(cuid())
  // Submitter info — copied at submit-time so the snapshot is immutable
  // even if the submitter later changes their profile or isn't signed in.
  contactName  String
  contactEmail String
  contactPhone String
  notes        String?

  // Proposed venue
  groundName    String
  groundAddress String
  lat           Float
  lng           Float
  sport         SportType

  status          GroundRequestStatus @default(PENDING)
  reviewedById    String?
  reviewedBy      User?               @relation("GroundRequestReviewer", fields: [reviewedById], references: [id])
  reviewedAt      DateTime?
  rejectionReason String?

  // Set on approve. The Ground holds the canonical venue data going forward.
  groundId String? @unique
  ground   Ground? @relation(fields: [groundId], references: [id])

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([status, createdAt])
}

// Booking Streak: counts consecutive bookings inside a rolling 7-day window.
// Reward tiers at 3 / 6 / 10 bookings (NPR 70 / NPR 150 / free advance).
// `expiresAt` is the deadline by which the next booking must occur or the streak resets.
model BookingStreak {
  userId         String   @id
  user           User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  count          Int      @default(0)
  lastBookedAt   DateTime
  expiresAt      DateTime
  updatedAt      DateTime @updatedAt
}
