generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["multiSchema"]
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
  schemas  = ["passenger"]
}

enum UserRole {
  PASSENGER
  AGENT
  SUPERVISOR
  ADMIN
  STAFF

  @@schema("passenger")
}

enum TripStatus {
  SCHEDULED
  BOARDING
  EN_ROUTE
  ARRIVED
  CANCELLED
  DELAYED

  @@schema("passenger")
}

enum SeatKind {
  STANDARD
  PREMIUM
  ACCESSIBLE

  @@schema("passenger")
}

enum SeatStatus {
  AVAILABLE
  HELD
  BOOKED
  BLOCKED

  @@schema("passenger")
}

enum PassengerCategory {
  ADULT
  CHILD

  @@schema("passenger")
}

enum IdDocumentType {
  NATIONAL_ID
  PASSPORT
  DRIVING_LICENSE
  OTHER

  @@schema("passenger")
}

enum Currency {
  ETB
  DJF
  USD

  @@schema("passenger")
}

model CoachType {
  id          String      @id @default(uuid())
  code        String
  name        String
  type        String      @default("passenger") // 'passenger', 'sleeper', 'dining', 'baggage'
  createdAt   DateTime    @default(now())
  updatedAt   DateTime    @updatedAt
  coaches     Coach[]
  seatClasses SeatClass[]
  @@schema("passenger")
}

model SeatClass {
  id               String   @id @default(uuid())
  coachTypeId      String
  name             String
  description      String?
  nationalityType  String?  // 'LOCAL' | 'INTERNATIONAL'
  bedPosition      String?  // 'UPPER' | 'MIDDLE' | 'LOWER' | null for regular seat
  baseFareMinor    Int      @default(0)     // per-km rate (tariff decimal × 100000)
  premiumMinor     Int      @default(0)     // flat fee per passenger
  insuranceFeeMinor Int     @default(0)     // flat fee per passenger
  isActive         Boolean  @default(true)
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt
  coachType        CoachType @relation(fields: [coachTypeId], references: [id])
  fareRules        FareRule[]
  routeFareRules   RouteFareRule[]
  segmentFares     SegmentFareRule[]
  packagePriceTiers PackagePriceTier[]
  @@unique([coachTypeId, name])
  @@index([coachTypeId])
  @@index([coachTypeId, nationalityType, bedPosition])
  @@schema("passenger")
}

enum BookingStatus {
  DRAFT
  PENDING_PAYMENT
  CONFIRMED
  CANCELLED
  BOARDED
  NO_SHOW
  REFUNDED

  @@schema("passenger")
}

enum ReturnLegStatus {
  NOT_APPLICABLE
  BOTH_USED
  OUTBOUND_ONLY
  INBOUND_ONLY
  NEITHER_USED

  @@schema("passenger")
}

enum PaymentRegion {
  ETHIOPIA
  DJIBOUTI
  INTERNATIONAL
  GLOBAL

  @@schema("passenger")
}

enum PaymentMethodType {
  TELEBIRR
  CBE_BIRR
  EBIRR
  CARD
  WALLET
  WAAFI
  DMONEY
  CAC_BANK

  @@schema("passenger")
}

enum PaymentIntentStatus {
  REQUIRES_ACTION
  PROCESSING
  SUCCEEDED
  FAILED
  CANCELLED
  REFUNDED

  @@schema("passenger")
}

enum WalletLedgerType {
  CREDIT
  DEBIT

  @@schema("passenger")
}

enum NotificationCategory {
  BOOKING
  PAYMENT
  DISRUPTION
  PROMOTION
  SYSTEM

  @@schema("passenger")
}

enum StopStatus {
  OPEN
  CHECKIN_CLOSED
  BOARDED
  COMPLETED

  @@schema("passenger")
}

enum SupportConversationStatus {
  OPEN
  RESOLVED
  CLOSED

  @@schema("passenger")
}

enum SupportSender {
  USER
  BOT
  AGENT

  @@schema("passenger")
}

enum LoyaltyTier {
  BRONZE
  SILVER
  GOLD
  PLATINUM

  @@schema("passenger")
}

enum LoyaltyLedgerReason {
  TRIP_COMPLETED
  REWARD_REDEEMED
  PROMO_BONUS
  MANUAL_ADJUSTMENT
  EXPIRY

  @@schema("passenger")
}

enum FoodOrderStatus {
  PENDING
  PREPARING
  READY
  DELIVERED
  CANCELLED

  @@schema("passenger")
}

enum DevicePlatform {
  IOS
  ANDROID
  WEB

  @@schema("passenger")
}

model User {
  id           String    @id @default(uuid())
  email        String    @unique
  phone        String    @unique
  fullName     String
  passwordHash String
  role         UserRole  @default(PASSENGER)
  nationality  String?
  nationalityCode String?
  gender       String?   // Male, Female, Other
  dateOfBirth  DateTime?
  passportNumber String?
  nationalId   String?
  failedLoginAttempts Int @default(0)
  lockedUntil  DateTime?
  blockedUntil DateTime?
  lastLoginAt  DateTime?
  createdAt    DateTime  @default(now())
  updatedAt    DateTime  @updatedAt

  faydaVerified   Boolean   @default(false)
  faydaVerifiedAt DateTime?
  faydaSub        String?   @unique

  sessions     Session[]
  passenger    Passenger?

  @@schema("passenger")
}

model Session {
  id             String   @id @default(uuid())
  userId         String
  token          String   @unique
  expiresAt      DateTime
  ipAddress      String?
  userAgent      String?
  lastActivityAt DateTime @default(now())
  createdAt      DateTime @default(now())
  user           User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  @@schema("passenger")
}

model Passenger {
  id        String   @id @default(uuid())
  userId    String?  @unique
  iamUserId String?  @unique
  defaultTravelerProfileId String?
  preferredLanguage        String?
  blockedUntil DateTime?
  createdAt                DateTime          @default(now())
  user                     User?             @relation(fields: [userId], references: [id])
  bookings                 Booking[]
  loyalty                  LoyaltyAccount?
  wallet                   WalletAccount?
  notifications            Notification[]
  travelerProfiles         TravelerProfile[]
  savedRoutes              SavedRoute[]
  packageBookings          PackageBooking[]
  supportConversations     SupportConversation[]
  @@index([userId])
  @@index([iamUserId])
  @@schema("passenger")
}

model TravelerProfile {
  id           String    @id @default(uuid())
  passengerId  String
  fullName     String
  gender       String?
  relationship String
  dateOfBirth  DateTime?
  nationalId   String?
  notes        String?
  createdAt    DateTime  @default(now())
  passenger    Passenger @relation(fields: [passengerId], references: [id])
  @@schema("passenger")
}

model Station {
  id       String  @id @default(uuid())
  code     String  @unique
  name     String
  city     String
  countryCode String?
  sequence Int     @default(0)
  isOperational Boolean @default(true)
  lat      Decimal? @db.Decimal(9, 6)
  lng      Decimal? @db.Decimal(9, 6)
  originSchedules      TrainSchedule[] @relation("OriginTrips")
  destinationSchedules TrainSchedule[] @relation("DestinationTrips")
  stopTimes            TripStopTime[]
  crowdSignals             StationCrowdSignal[]
  bookingDepartures        Booking[]        @relation("BookingPackageDepartureStation")
  packageBookingDepartures PackageBooking[] @relation("PackageBookingDepartureStation")
  @@index([city, countryCode])
  @@index([sequence])
  @@schema("passenger")
}

model Train {
  id           String          @id @default(uuid())
  number       String          @unique
  name         String
  operatorId   String          @default("op_edr")
  operatorName String?
  description  String?
  isActive     Boolean         @default(true)
  createdAt    DateTime        @default(now())
  updatedAt    DateTime        @updatedAt
  schedules    TrainSchedule[]
  @@schema("passenger")
}

model TrainSchedule {
  id                   String            @id @default(uuid())
  trainId              String
  routeId              String?
  originStationId      String
  destinationStationId String
  departureAt          DateTime
  arrivalAt            DateTime
  durationMinutes      Int
  status               TripStatus        @default(SCHEDULED)
  stopsCount           Int               @default(0)
  reservedCount        Int               @default(0)
  onTimePercent        Int               @default(100)
  carbonRating         String            @default("A")
  notes                String?
  isPackageOnly        Boolean           @default(false)
  train                Train             @relation(fields: [trainId], references: [id])
  route                Route?            @relation(fields: [routeId], references: [id])
  originStation        Station           @relation("OriginTrips", fields: [originStationId], references: [id])
  destinationStation   Station           @relation("DestinationTrips", fields: [destinationStationId], references: [id])
  coachAssignments     CoachAssignment[]
  bookings             Booking[]         @relation("OutboundSchedule")
  returnBookings       Booking[]         @relation("ReturnSchedule")
  stopTimes            TripStopTime[]
  liveStatus           TripLiveStatus?
  menuItems            MenuItem[]
  journeySegments      JourneySegment[]
  outboundPackages     TravelPackage[]   @relation("PackageOutbound")
  returnPackages       TravelPackage[]   @relation("PackageReturn")

  @@index([departureAt, originStationId])
  @@schema("passenger")
}

model TripStopTime {
  id                 String        @id @default(uuid())
  scheduleId         String
  stationId          String
  sequence           Int
  plannedArrivalAt   DateTime?
  plannedDepartureAt DateTime?
  actualArrivalAt    DateTime?
  status             StopStatus    @default(OPEN)
  schedule           TrainSchedule @relation(fields: [scheduleId], references: [id])
  station            Station       @relation(fields: [stationId], references: [id])

  @@unique([scheduleId, sequence])
  @@schema("passenger")
}

model TripLiveStatus {
  id                   String        @id @default(uuid())
  scheduleId           String        @unique
  state                String
  currentLocationLabel String?
  progressPercent      Int           @default(0)
  delayMinutes         Int           @default(0)
  currentSpeedKph      Int?
  platformLabel        String?
  updatedAt            DateTime      @updatedAt
  schedule             TrainSchedule @relation(fields: [scheduleId], references: [id])
  @@schema("passenger")
}

model Coach {
  id              String            @id @default(uuid())
  coachTypeId     String
  number          String            @unique
  arrangement     String            @default("2+2") // e.g., '2+2', '3+2', '2+2+2'
  capacity        Int               @default(0) // Total seats/beds
  sequence        Int               @default(0)
  status          String            @default("ACTIVE") // 'ACTIVE', 'MAINTENANCE', 'INACTIVE'
  createdAt       DateTime          @default(now())
  updatedAt       DateTime          @updatedAt
  coachType      CoachType              @relation(fields: [coachTypeId], references: [id])
  seats          Seat[]
  assignments    CoachAssignment[]
  routeTemplates RouteCoachTemplate[]
  @@index([coachTypeId])
  @@index([sequence])
  @@schema("passenger")
}

model CoachAssignment {
  id             String        @id @default(uuid())
  scheduleId     String
  coachId        String
  positionNumber Int
  isOperational  Boolean       @default(true)
  createdAt      DateTime      @default(now())
  schedule       TrainSchedule @relation(fields: [scheduleId], references: [id])
  coach          Coach         @relation(fields: [coachId], references: [id])

  @@unique([scheduleId, positionNumber])
  @@index([scheduleId])
  @@schema("passenger")
}

model Seat {
  id              String        @id @default(uuid())
  coachId         String
  seatNumber      String // Auto-generated: e.g., '1', '2', '3' (unique per coach)
  row             Int
  col             String
  kind            SeatKind      @default(STANDARD)
  status          SeatStatus    @default(AVAILABLE)
  heldUntil       DateTime?
  isWindow        Boolean       @default(false)
  isAisle         Boolean       @default(false)
  bedPosition     String? // 'lower', 'middle', 'upper'
  premiumFeeMinor Int           @default(0)
  coach           Coach         @relation(fields: [coachId], references: [id])
  bookingSeats    BookingSeat[]
  blocks          SeatBlock[]
  tickets         Ticket[]

  @@unique([coachId, seatNumber])
  @@unique([coachId, row, col])
  @@index([coachId])
  @@schema("passenger")
}

model SeatHold {
  id          String   @id @default(uuid())
  scheduleId  String
  seatIds     String[]
  fareQuoteId String?
  passengerId String
  createdBy   String?
  expiresAt   DateTime
  createdAt   DateTime @default(now())

  @@index([expiresAt])
  @@schema("passenger")
}

model FareRule {
  id            String    @id @default(uuid())
  tripId        String?
  route         String?
  nationality   String? // Ethiopian, Djiboutian, Other
  seatClassId   String
  baseFareMinor Int
  seatClass     SeatClass @relation(fields: [seatClassId], references: [id])
  currency      String    @default("ETB")
  refundable    Boolean   @default(true)
  validFrom     DateTime
  validUntil    DateTime?
  createdAt     DateTime  @default(now())
  @@schema("passenger")
}

model Booking {
  id                        String                @id @default(uuid())
  bookingRef                String                @unique
  passengerId               String
  scheduleId                String
  packageId                 String?
  priceTierId               String?
  bookingType               String                @default("ONE_WAY")
  status                    BookingStatus         @default(DRAFT)
  currency                  String                @default("ETB")
  totalMinor                Int
  adultCount                Int                   @default(1)
  childCount                Int                   @default(0)
  displayCurrency           Currency?
  displayTotalMinor         Int?
  returnScheduleId          String?
  returnOriginStationId     String?
  returnDestinationStationId String?
  returnHoldId              String?
  returnSeatClassId         String?
  returnLegStatus           ReturnLegStatus @default(NOT_APPLICABLE)
  // Transit leg-2 fields (single-booking transit)
  leg2ScheduleId              String?
  leg2OriginStationId         String?
  leg2DestinationStationId    String?
  leg2SeatClassId             String?
  // Round-trip transit: return journey transit fields
  returnLeg2ScheduleId        String?
  returnLeg2OriginStationId   String?
  returnLeg2DestStationId     String?
  returnLeg2SeatClassId       String?
  originStationId             String?
  destinationStationId        String?
  outboundBoardedAt           DateTime?
  returnBoardedAt           DateTime?
  contactEmail              String?
  contactPhone              String?
  userAgent                 String?
  source                    String                @default("WEB")
  promoCode                 String?
  paidAt                    DateTime?
  paymentReminderSentAt     DateTime?
  packageDepartureStationId String?
  payToken                  String?               @unique
  payTokenExpiresAt         DateTime?
  createdAt                 DateTime              @default(now())
  updatedAt                 DateTime              @updatedAt
  passenger                 Passenger             @relation(fields: [passengerId], references: [id])
  schedule                  TrainSchedule         @relation("OutboundSchedule", fields: [scheduleId], references: [id])
  returnSchedule            TrainSchedule?        @relation("ReturnSchedule", fields: [returnScheduleId], references: [id])
  package                   TravelPackage?        @relation(fields: [packageId], references: [id])
  priceTier                 PackagePriceTier?     @relation(fields: [priceTierId], references: [id])
  departureStation          Station?              @relation("BookingPackageDepartureStation", fields: [packageDepartureStationId], references: [id])
  seats                     BookingSeat[]
  paymentIntent             PaymentIntent?
  tickets                   Ticket[]
  foodOrders                FoodOrder[]
  agentBooking              AgentBooking?
  modifications             BookingModification[]
  cancellation              BookingCancellation?
  baggage                   BaggageBooking[]
  excessBaggageCharges      ExcessBaggageCharge[]
  supplementaryCharges      SupplementaryCharge[]
  journey                   Journey?

  @@index([passengerId, status])
  @@index([bookingType])
  @@schema("passenger")
}

model BookingSeat {
  id                String            @id @default(uuid())
  bookingId         String
  seatId            String
  leg               Int               @default(1) // 1=outbound/leg-1, 2=return/leg-2
  scheduleId        String?            // which schedule this seat belongs to
  passengerName     String
  dateOfBirth       DateTime?
  passengerCategory PassengerCategory @default(ADULT)
  idDocumentType    IdDocumentType?
  idDocumentNumber  String?
  passportNumber    String?
  passportCountry   String?
  verifaydaVerified Boolean           @default(false)
  verifaydaData     Json?
  faydaVerifiedAt   DateTime?
  faydaSub          String?
  faydaVerifiedName String?
  seatLabelSnapshot String?
  fareMinor         Int?
  displayCurrency   Currency?
  displayFareMinor  Int?
  booking           Booking           @relation(fields: [bookingId], references: [id])
  seat              Seat              @relation(fields: [seatId], references: [id])
  @@unique([scheduleId, seatId])
  @@schema("passenger")
}

model PaymentMethod {
  id          String            @id @default(uuid())
  type        PaymentMethodType @unique
  displayName String
  region      PaymentRegion     @default(GLOBAL)
  currency    String            @default("ETB")
  providerId  String?
  isDefault   Boolean           @default(false)
  enabled     Boolean           @default(true)
  sortOrder   Int               @default(0)
  createdAt   DateTime          @default(now())
  updatedAt   DateTime          @updatedAt
  @@schema("passenger")
}

model PaymentIntent {
  id              String              @id @default(uuid())
  bookingId       String              @unique
  amountMinor     Float
  currency        String              @default("ETB")
  method          PaymentMethodType
  provider        String?
  status          PaymentIntentStatus @default(REQUIRES_ACTION)
  providerRef     String?
  clientAction    Json?
  merchantOrderId String?             @unique
  providerOrderId String?
  providerTxnId   String?
  rawInitiation   Json?
  paidAt          DateTime?
  refundedAt      DateTime?
  captureMethod   String?
  failureCode     String?
  failureMessage  String?
  expiresAt       DateTime?
  createdAt       DateTime            @default(now())
  updatedAt       DateTime            @updatedAt
  booking         Booking             @relation(fields: [bookingId], references: [id])
  refunds         PaymentRefund[]

  @@index([providerOrderId])
  @@index([providerTxnId])
  @@schema("passenger")
}

model PaymentWebhookEvent {
  id              String            @id @default(uuid())
  provider        PaymentMethodType
  externalEventId String
  merchantOrderId String?
  providerTxnId   String?
  signatureValid  Boolean
  status          String
  payload         Json
  receivedAt      DateTime          @default(now())
  processedAt     DateTime?
  processingError String?

  @@unique([provider, externalEventId])
  @@index([merchantOrderId])
  @@schema("passenger")
}

model PaymentRefund {
  id               String        @id @default(uuid())
  paymentIntentId  String
  amountMinor      Int
  reason           String?
  providerRefundId String?
  status           String
  createdAt        DateTime      @default(now())
  paymentIntent    PaymentIntent @relation(fields: [paymentIntentId], references: [id])
  @@schema("passenger")
}

model Ticket {
  id          String    @id @default(uuid())
  bookingId   String
  bookingRef  String
  passengerName String
  seatId      String
  leg         Int       @default(1)
  scheduleId  String?
  status      String    @default("ACTIVE")
  qrPayload   String
  barcodePayload String?
  pdfUrl      String?
  deliveryChannel String @default("EMAIL")
  issuedAt    DateTime  @default(now())
  validatedAt DateTime?
  validatorId String?
  boardedAt   DateTime?
  booking Booking @relation(fields: [bookingId], references: [id])
  seat    Seat    @relation(fields: [seatId], references: [id])
  validationLogs GateValidationLog[]
  @@index([bookingId])
  @@index([seatId])
  @@schema("passenger")
}

model LoyaltyAccount {
  id             String               @id @default(uuid())
  passengerId    String               @unique
  pointsBalance  Int                  @default(0)
  lifetimePoints Int                  @default(0)
  tier           LoyaltyTier          @default(BRONZE)
  tierUpdatedAt  DateTime?
  updatedAt      DateTime             @updatedAt
  passenger      Passenger            @relation(fields: [passengerId], references: [id])
  ledger         LoyaltyLedgerEntry[]
  rewards        LoyaltyReward[]
  @@schema("passenger")
}

model LoyaltyLedgerEntry {
  id           String              @id @default(uuid())
  accountId    String
  delta        Int
  reason       LoyaltyLedgerReason
  bookingId    String?
  balanceAfter Int
  createdAt    DateTime            @default(now())
  account      LoyaltyAccount      @relation(fields: [accountId], references: [id])
  @@schema("passenger")
}

model LoyaltyReward {
  id          String         @id @default(uuid())
  accountId   String
  title       String
  costPoints  Int
  available   Boolean        @default(true)
  description String?
  account     LoyaltyAccount @relation(fields: [accountId], references: [id])
  @@schema("passenger")
}

model WalletAccount {
  id           String              @id @default(uuid())
  passengerId  String              @unique
  balanceMinor Int                 @default(0)
  status       String              @default("ACTIVE")
  holdMinor    Int                 @default(0)
  currency     String              @default("ETB")
  updatedAt    DateTime            @updatedAt
  passenger    Passenger           @relation(fields: [passengerId], references: [id])
  ledger       WalletLedgerEntry[]

  @@index([passengerId])
  @@schema("passenger")
}

model WalletLedgerEntry {
  id                String           @id @default(uuid())
  walletId          String
  type              WalletLedgerType
  amountMinor       Int
  balanceAfterMinor Int
  description       String
  relatedBookingId  String?
  createdAt         DateTime         @default(now())
  wallet            WalletAccount    @relation(fields: [walletId], references: [id])
  @@schema("passenger")
}

model Notification {
  id          String               @id @default(uuid())
  passengerId String
  title       String
  body        String
  category    NotificationCategory
  read        Boolean              @default(false)
  deepLink    String?
  metadata    Json?
  createdAt   DateTime             @default(now())
  passenger   Passenger            @relation(fields: [passengerId], references: [id])
  @@schema("passenger")
}

model Promotion {
  id             String   @id @default(uuid())
  title          String
  subtitle       String?
  code           String   @unique
  percentOff     Int?
  amountOffMinor Int?
  validUntil     DateTime
  ctaLabel       String?
  deepLink       String?
  active         Boolean  @default(true)
  createdAt      DateTime @default(now())
  @@schema("passenger")
}

model StationCrowdSignal {
  id          String    @id @default(uuid())
  stationId   String
  level       String
  label       String
  statusLabel String
  confidence  Int?
  observedAt  DateTime?
  updatedAt   DateTime  @updatedAt
  station     Station   @relation(fields: [stationId], references: [id])
  @@schema("passenger")
}

model WeatherAlert {
  id         String   @id @default(uuid())
  region     String
  severity   String
  title      String
  message    String
  validUntil DateTime
  createdAt  DateTime @default(now())
  @@schema("passenger")
}

model MenuCategory {
  id    String     @id @default(uuid())
  name  String
  items MenuItem[]
  @@schema("passenger")
}

model MenuItem {
  id             String        @id @default(uuid())
  scheduleId     String
  categoryId     String
  name           String
  priceMinor     Int
  currency       String        @default("ETB")
  available      Boolean       @default(true)
  availableUntil DateTime?
  schedule       TrainSchedule @relation(fields: [scheduleId], references: [id])
  category       MenuCategory  @relation(fields: [categoryId], references: [id])
  @@schema("passenger")
}

model FoodOrder {
  id                  String          @id @default(uuid())
  bookingId           String
  status              FoodOrderStatus @default(PENDING)
  totalMinor          Int
  currency            String          @default("ETB")
  specialInstructions String?
  estimatedReadyAt    DateTime?
  createdAt           DateTime        @default(now())
  booking             Booking         @relation(fields: [bookingId], references: [id])
  items               FoodOrderItem[]
  @@schema("passenger")
}

model FoodOrderItem {
  id             String    @id @default(uuid())
  orderId        String
  menuItemId     String
  name           String
  quantity       Int
  unitPriceMinor Int?
  lineTotalMinor Int
  order          FoodOrder @relation(fields: [orderId], references: [id])
  @@schema("passenger")
}

model FaqCategory {
  id       String       @id @default(uuid())
  title    String
  iconKey  String?
  articles FaqArticle[]
  @@schema("passenger")
}

model FaqArticle {
  id             String      @id @default(uuid())
  categoryId     String
  question       String
  answerMarkdown String
  rank           Int         @default(0)
  category       FaqCategory @relation(fields: [categoryId], references: [id])
  @@schema("passenger")
}

model SupportConversation {
  id                 String                    @id @default(uuid())
  userId             String?
  guestId            String?
  guestName          String?
  guestEmail         String?
  guestPhone         String?
  passengerId        String?
  passengerName      String?
  subject            String?
  assignedAgentId    String?
  status             SupportConversationStatus @default(OPEN)
  lastMessageAt      DateTime?
  lastMessagePreview String?
  lastMessageSender  SupportSender?
  userLastReadAt     DateTime?
  agentLastReadAt    DateTime?
  createdAt          DateTime                  @default(now())
  updatedAt          DateTime                  @updatedAt @default(now())
  messages           SupportMessage[]
  passenger          Passenger?                @relation(fields: [passengerId], references: [id])
  @@index([userId])
  @@index([guestId])
  @@index([status, lastMessageAt])
  @@schema("passenger")
}

model SupportMessage {
  id             String              @id @default(uuid())
  conversationId String
  sender         SupportSender
  /// NULL for an attachment-only message — absence of text is representable
  /// rather than smuggled through "". The DTO maps NULL -> "".
  text           String?
  createdAt      DateTime            @default(now())
  conversation   SupportConversation @relation(fields: [conversationId], references: [id])
  attachments    SupportAttachment[]
  /// Backs keyset pagination of a thread (newest-first over (createdAt, id)).
  /// Without it, paging a long thread degrades to a scan per page.
  @@index([conversationId, createdAt])
  @@schema("passenger")
}

/// A file posted on a support message.
///
/// The freight side stores the equivalent in its polymorphic `freight.files`
/// table; this app has no such table (and no TypeORM), so chat attachments get a
/// purpose-built model rather than a shared one. Bytes live in MinIO — `url` is
/// the unsigned object path, signed on read for preview.
model SupportAttachment {
  id        String         @id @default(uuid())
  messageId String
  name      String
  mimeType  String
  /// Bytes.
  size      Int
  /// Unsigned MinIO object URL. Not directly fetchable by a browser — the API
  /// mints a short-lived signed URL per response.
  url       String
  createdAt DateTime       @default(now())
  message   SupportMessage @relation(fields: [messageId], references: [id], onDelete: Cascade)
  @@index([messageId])
  @@schema("passenger")
}

model UserPreferences {
  id                     String  @id @default(uuid())
  iamUserId              String  @unique
  pushEnabled            Boolean @default(true)
  emailEnabled           Boolean @default(true)
  smsEnabled             Boolean @default(false)
  promosEnabled          Boolean @default(true)
  biometricEnabled       Boolean @default(false)
  twoFactorEnabled       Boolean @default(false)
  defaultPaymentMethodId String?
  autoDownloadTickets    Boolean @default(true)
  dataSharing            Boolean @default(false)
  locale                 String  @default("en")
  darkMode               Boolean @default(false)
  language               String  @default("en")

  @@schema("passenger")
}

model Device {
  id         String         @id @default(uuid())
  iamUserId  String
  platform   DevicePlatform
  name       String
  pushToken  String?
  trusted    Boolean        @default(false)
  lastSeenAt DateTime       @default(now())

  @@schema("passenger")
}

model SavedRoute {
  id            String    @id @default(uuid())
  passengerId   String
  fromStationId String
  toStationId   String
  fromName      String
  toName        String
  tripCount     Int       @default(0)
  createdAt     DateTime  @default(now())
  passenger     Passenger @relation(fields: [passengerId], references: [id])
  @@schema("passenger")
}

model Journey {
  id              String           @id @default(uuid())
  passengerId     String
  bookingId       String?          @unique
  status          String
  totalMinor      Int
  currency        String           @default("ETB")
  createdAt       DateTime         @default(now())
  booking         Booking?         @relation(fields: [bookingId], references: [id])
  journeySegments JourneySegment[]
  @@schema("passenger")
}

model JourneySegment {
  id                 String        @id @default(uuid())
  journeyId          String
  scheduleId         String
  segmentOrder       Int
  seatId             String?
  coachId            String?
  departureStationId String
  arrivalStationId   String
  journey            Journey       @relation(fields: [journeyId], references: [id])
  schedule           TrainSchedule @relation(fields: [scheduleId], references: [id])

  // Prevents two confirmed bookings from occupying the same seat on the same
  // schedule hop — the hard DB backstop against application-level race conditions.
  @@unique([scheduleId, seatId, departureStationId])
  @@schema("passenger")
}

model OtpCode {
  id        String   @id @default(uuid())
  userId    String?
  email     String?
  phone     String?
  code      String
  purpose   String
  expiresAt DateTime
  verified  Boolean  @default(false)
  createdAt DateTime @default(now())

  @@index([email, phone])
  @@schema("passenger")
}

model PasswordResetToken {
  id        String   @id @default(uuid())
  userId    String
  token     String   @unique
  expiresAt DateTime
  used      Boolean  @default(false)
  createdAt DateTime @default(now())

  @@index([userId])
  @@schema("passenger")
}

model Route {
  id                   String            @id @default(uuid())
  code                 String            @unique
  name                 String
  description          String?
  effectiveFrom        DateTime
  effectiveUntil       DateTime?
  active               Boolean           @default(true)
  checkinMinutesBefore Int               @default(30)
  createdAt            DateTime          @default(now())
  stops          RouteStop[]
  fareRules      RouteFareRule[]
  segmentFares   SegmentFareRule[]
  schedules      TrainSchedule[]
  coachTemplates RouteCoachTemplate[]
  @@schema("passenger")
}

model RouteStop {
  id                   String   @id @default(uuid())
  routeId              String
  stationId            String
  sequence             Int
  distanceKm           Float?
  checkinMinutesBefore Int?
  travelMinutesToStop  Int?
  plannedArrivalTime   DateTime?
  plannedDepartureTime DateTime?
  createdAt            DateTime @default(now())
  route                Route    @relation(fields: [routeId], references: [id], onDelete: Cascade)

  @@unique([routeId, sequence])
  @@index([routeId, stationId])
  @@schema("passenger")
}

model RouteCoachTemplate {
  id             String   @id @default(uuid())
  routeId        String
  coachId        String
  positionNumber Int
  createdAt      DateTime @default(now())
  route          Route    @relation(fields: [routeId], references: [id], onDelete: Cascade)
  coach          Coach    @relation(fields: [coachId], references: [id])

  @@unique([routeId, positionNumber])
  @@index([routeId])
  @@schema("passenger")
}

model RouteFareRule {
  id                String            @id @default(uuid())
  routeId           String
  seatClassId       String
  passengerCategory PassengerCategory @default(ADULT)
  baseFareMinor     Int
  discountPercent   Int?
  taxPercent        Int?
  surchargeMinor    Int?
  currency          String            @default("ETB")
  validFrom         DateTime
  validUntil        DateTime?
  createdAt         DateTime          @default(now())
  route             Route             @relation(fields: [routeId], references: [id], onDelete: Cascade)
  seatClass         SeatClass         @relation(fields: [seatClassId], references: [id])

  @@index([routeId, seatClassId])
  @@schema("passenger")
}

model SegmentFareRule {
  id                      String    @id @default(uuid())
  routeId                 String
  originStopSequence      Int
  destinationStopSequence Int
  seatClassId             String
  baseFareMinor           Int
  nationality             String? // Optional: Ethiopian, Djiboutian, Other
  currency                String    @default("ETB")
  validFrom               DateTime
  validUntil              DateTime?
  createdAt               DateTime  @default(now())
  route                   Route     @relation(fields: [routeId], references: [id], onDelete: Cascade)
  seatClass               SeatClass @relation(fields: [seatClassId], references: [id])

  @@unique([routeId, originStopSequence, destinationStopSequence, seatClassId, nationality])
  @@index([routeId, seatClassId])
  @@schema("passenger")
}

model Agent {
  id             String            @id @default(uuid())
  iamUserId      String?           @unique
  agentCode      String            @unique
  stationId      String?
  commissionRate Int               @default(5)
  active         Boolean           @default(true)
  createdAt      DateTime          @default(now())
  bookings       AgentBooking[]
  shifts         AgentShift[]
  commissions    AgentCommission[]
  @@index([iamUserId])
  @@schema("passenger")
}

model AgentBooking {
  id            String   @id @default(uuid())
  agentId       String
  bookingId     String   @unique
  paymentMethod String
  cashReceived  Int?
  changeGiven   Int?
  paperTicket   Boolean  @default(false)
  createdAt     DateTime @default(now())
  agent         Agent    @relation(fields: [agentId], references: [id])
  booking       Booking  @relation(fields: [bookingId], references: [id])
  @@schema("passenger")
}

model AgentShift {
  id             String    @id @default(uuid())
  agentId        String
  openedAt       DateTime  @default(now())
  closedAt       DateTime?
  openingBalance Int       @default(0)
  closingBalance Int?
  reconciled     Boolean   @default(false)
  notes          String?
  agent          Agent     @relation(fields: [agentId], references: [id])

  @@index([agentId, openedAt])
  @@schema("passenger")
}

model AgentCommission {
  id          String    @id @default(uuid())
  agentId     String
  bookingId   String
  amountMinor Int
  rate        Int
  paidAt      DateTime?
  createdAt   DateTime  @default(now())
  agent       Agent     @relation(fields: [agentId], references: [id])

  @@index([agentId, paidAt])
  @@schema("passenger")
}

model BookingModification {
  id               String   @id @default(uuid())
  bookingId        String
  modifiedBy       String
  modificationType String
  oldData          Json
  newData          Json
  fareAdjustment   Int      @default(0)
  reason           String?
  createdAt        DateTime @default(now())
  booking          Booking  @relation(fields: [bookingId], references: [id])

  @@index([bookingId])
  @@schema("passenger")
}

model BookingCancellation {
  id           String    @id @default(uuid())
  bookingId    String    @unique
  cancelledBy  String
  reason       String?
  refundAmount Int
  refundMethod String
  refundStatus String
  processedAt  DateTime?
  createdAt    DateTime  @default(now())
  booking      Booking   @relation(fields: [bookingId], references: [id])
  @@schema("passenger")
}

model GateValidationLog {
  id          String   @id @default(uuid())
  ticketId    String
  validatorId String
  gateId      String?
  leg         String?  // 'OUTBOUND' | 'RETURN' — for round-trip tickets
  status      String
  reason      String?
  validatedAt DateTime @default(now())
  ticket      Ticket   @relation(fields: [ticketId], references: [id])

  @@index([ticketId])
  @@index([validatorId])
  @@schema("passenger")
}

model BaggageAllowance {
  id             String   @id @default(uuid())
  seatClassId    String
  maxWeightKg    Int
  maxPiecesCount Int
  excessFeePerKg Int
  currency       String   @default("ETB")
  createdAt      DateTime @default(now())
  @@schema("passenger")
}

model BaggageBooking {
  id             String   @id @default(uuid())
  bookingId      String
  weightKg       Int
  piecesCount    Int
  excessFeeMinor Int      @default(0)
  paid           Boolean  @default(false)
  createdAt      DateTime @default(now())
  booking        Booking  @relation(fields: [bookingId], references: [id])

  @@index([bookingId])
  @@schema("passenger")
}

model SupplementaryCharge {
  id            String    @id @default(uuid())
  bookingId     String
  reason        String    // e.g. "UNDERPAYMENT", "FARE_CORRECTION"
  amountMinor   Int
  currency      String    @default("ETB")
  status        String    @default("PENDING") // PENDING | PAID | WAIVED | EXPIRED
  paymentToken  String    @unique @default(uuid())
  providerTxnId String?
  notes         String?
  createdBy     String
  paidAt        DateTime?
  expiresAt     DateTime?
  createdAt     DateTime  @default(now())
  booking       Booking   @relation(fields: [bookingId], references: [id])

  @@index([bookingId])
  @@index([paymentToken])
  @@index([status])
  @@schema("passenger")
}

model ExcessBaggageCharge {
  id             String   @id @default(uuid())
  bookingId      String
  agentId        String
  excessWeightKg Int
  feePerKgMinor  Int
  totalMinor     Int
  currency       String   @default("ETB")
  status         String   @default("PENDING") // PENDING | PAID | EXPIRED | WAIVED | CASH_COLLECTED
  paymentToken   String   @unique @default(uuid())
  expiresAt      DateTime
  paidAt         DateTime?
  waivedBy       String?
  waivedReason   String?
  contactPhone   String?
  contactEmail   String?
  createdAt      DateTime @default(now())
  booking        Booking  @relation(fields: [bookingId], references: [id])

  @@index([bookingId])
  @@index([paymentToken])
  @@index([status])
  @@schema("passenger")
}

model AuditLog {
  id          String   @id @default(uuid())
  iamUserId   String?
  action      String
  entityType  String
  entityId    String?
  oldData     Json?
  newData     Json?
  ipAddress   String?
  userAgent   String?
  createdAt   DateTime @default(now())
  @@index([iamUserId, createdAt])
  @@index([entityType, entityId])
  @@schema("passenger")
}

model NotificationTemplate {
  id           String   @id @default(uuid())
  code         String   @unique
  channel      String
  subject      String?
  bodyTemplate String
  active       Boolean  @default(true)
  createdAt    DateTime @default(now())
  @@schema("passenger")
}

model SeatBlock {
  id         String    @id @default(uuid())
  seatId     String
  scheduleId String?
  reason     String
  blockedBy  String
  approvedBy String?
  blockedAt  DateTime  @default(now())
  unblockAt  DateTime?
  seat       Seat      @relation(fields: [seatId], references: [id])

  @@index([seatId])
  @@index([scheduleId])
  @@schema("passenger")
}

model OperationalReport {
  id          String   @id @default(uuid())
  reportType  String
  dateFrom    DateTime
  dateTo      DateTime
  data        Json
  generatedBy String?
  createdAt   DateTime @default(now())

  @@index([reportType, dateFrom])
  @@schema("passenger")
}

model FraudRule {
  id        String   @id @default(uuid())
  type      String   @unique
  enabled   Boolean  @default(true)
  threshold Float
  config    Json?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  @@schema("passenger")
}

model FraudAlert {
  id             String   @id @default(uuid())
  iamUserId      String
  eventType      String
  triggeredRules String[]
  context        Json
  severity       String   @default("MEDIUM")
  acknowledged   Boolean  @default(false)
  acknowledgedAt DateTime?
  createdAt      DateTime @default(now())
  @@index([iamUserId, createdAt])
  @@index([acknowledged])
  @@schema("passenger")
}

model CurrencyExchangeRate {
  id            String   @id @default(uuid())
  fromCurrency  Currency
  toCurrency    Currency
  rate          Decimal  @db.Decimal(18, 6)
  effectiveDate DateTime @default(now())
  source        String   @default("MANUAL")
  createdAt     DateTime @default(now())

  @@unique([fromCurrency, toCurrency, effectiveDate])
  @@index([fromCurrency, toCurrency])
  @@schema("passenger")
}

model VerifaydaVerification {
  id              String    @id @default(uuid())
  bookingId       String?
  nationalId      String
  requestPayload  Json
  responsePayload Json?
  verified        Boolean   @default(false)
  failureReason   String?
  verifiedAt      DateTime?
  createdAt       DateTime  @default(now())

  @@index([nationalId])
  @@index([bookingId])
  @@schema("passenger")
}

model SavedPassengerProfile {
  id              String         @id @default(uuid())
  userId          String?
  deviceId        String?
  passengerName   String
  dateOfBirth     DateTime
  idDocumentType  IdDocumentType
  passportNumber  String?
  passportCountry String?
  nationality     String?
  phone           String?
  email           String?
  createdAt       DateTime       @default(now())
  updatedAt       DateTime       @updatedAt

  @@index([userId])
  @@index([deviceId])
  @@schema("passenger")
}

model FaydaVerificationSession {
  id               String    @id @default(uuid())
  state            String    @unique
  codeVerifier     String
  purpose          String    @default("VERIFY") // VERIFY | LOGIN
  platform         String    @default("WEB") // WEB | MOBILE — recorded for audit
  redirectUri      String? // exact OAuth redirect_uri sent to eSignet at start; reused verbatim at token exchange (domain-aware for WEB)
  saveToAccount    Boolean   @default(false)
  status           String    @default("PENDING")
  errorCode        String?
  errorDescription String?
  authCode         String?
  createdAt        DateTime  @default(now())
  expiresAt        DateTime
  completedAt      DateTime?

  iamUserId String?
  bookingId String?

  @@index([iamUserId])
  @@index([bookingId])
  @@index([state])
  @@index([expiresAt])
  @@schema("passenger")
}

model SystemConfig {
  id        String   @id @default(uuid())
  key       String   @unique
  value     String
  updatedAt DateTime @updatedAt

  @@schema("passenger")
}

enum PackageStatus {
  DRAFT
  ACTIVE
  SOLD_OUT
  EXPIRED
  CANCELLED

  @@schema("passenger")
}

model TravelPackage {
  id                   String        @id @default(uuid())
  code                 String        @unique
  name                 String
  description          String?
  status               PackageStatus @default(DRAFT)
  outboundScheduleId   String
  returnScheduleId     String
  originStationId      String
  destinationStationId String
  boardingTime         DateTime
  departureTime        DateTime
  arrivalTime          DateTime
  totalCapacity        Int
  bookedCount          Int           @default(0)
  includedServices     Json
  coachConfiguration   String?
  busTransferIncluded  Boolean       @default(false)
  busTransferRoute     String?
  validFrom            DateTime
  validUntil           DateTime
  createdAt            DateTime      @default(now())
  updatedAt            DateTime      @updatedAt

  outboundSchedule     TrainSchedule      @relation("PackageOutbound", fields: [outboundScheduleId], references: [id])
  returnSchedule       TrainSchedule      @relation("PackageReturn",   fields: [returnScheduleId],   references: [id])
  priceTiers           PackagePriceTier[]
  bookings             Booking[]
  packageBookings      PackageBooking[]
  inquiries            PackageInquiry[]

  @@index([status, validFrom])
  @@schema("passenger")
}

model PackagePriceTier {
  id             String         @id @default(uuid())
  packageId      String
  seatClassId    String?
  seatType       String
  label          String
  priceMinor     Int
  currency       String         @default("ETB")
  availableSeats Int            @default(0)
  bookedSeats    Int            @default(0)

  package        TravelPackage   @relation(fields: [packageId], references: [id])
  seatClass      SeatClass?      @relation(fields: [seatClassId], references: [id])
  bookings       Booking[]
  packageBookings PackageBooking[]
  inquiries      PackageInquiry[]

  @@unique([packageId, seatType])
  @@schema("passenger")
}

model PackageBooking {
  id                String        @id @default(uuid())
  bookingRef        String        @unique
  packageId         String
  priceTierId       String
  passengerId       String?
  contactEmail      String?
  contactPhone      String?
  status            BookingStatus @default(PENDING_PAYMENT)
  passengerCount    Int           @default(1)
  adultCount        Int           @default(1)
  childCount        Int           @default(0)
  totalMinor        Int
  currency          String        @default("ETB")
  displayCurrency   Currency?
  displayTotalMinor Int?
  promoCode         String?
  source            String        @default("WEB")
  paidAt            DateTime?
  createdAt         DateTime      @default(now())
  updatedAt         DateTime      @updatedAt
  packageDepartureStationId String?

  package           TravelPackage             @relation(fields: [packageId], references: [id])
  priceTier         PackagePriceTier          @relation(fields: [priceTierId], references: [id])
  passenger         Passenger?                @relation(fields: [passengerId], references: [id])
  departureStation  Station?                  @relation("PackageBookingDepartureStation", fields: [packageDepartureStationId], references: [id])
  passengers        PackageBookingPassenger[]
  paymentIntent     PackagePaymentIntent?

  @@index([packageId, status])
  @@schema("passenger")
}

model PackageBookingPassenger {
  id               String          @id @default(uuid())
  bookingId        String
  passengerName    String
  dateOfBirth      DateTime?
  idDocumentType   IdDocumentType?
  idDocumentNumber String?
  passportNumber   String?
  passportCountry  String?
  seatLabel        String?

  booking          PackageBooking  @relation(fields: [bookingId], references: [id])

  @@schema("passenger")
}

model PackagePaymentIntent {
  id               String              @id @default(uuid())
  packageBookingId String              @unique
  amountMinor      Int
  currency         String              @default("ETB")
  method           PaymentMethodType
  status           PaymentIntentStatus @default(REQUIRES_ACTION)
  providerRef      String?
  paidAt           DateTime?
  createdAt        DateTime            @default(now())
  updatedAt        DateTime            @updatedAt

  packageBooking   PackageBooking      @relation(fields: [packageBookingId], references: [id])

  @@schema("passenger")
}

model PackageInquiry {
  id              String        @id @default(uuid())
  packageId       String
  priceTierId     String?
  travelerCount   Int
  contactName     String
  contactEmail    String?
  contactPhone    String?
  notes           String?
  status          String        @default("NEW")
  enquiredAt      DateTime      @default(now())
  createdAt       DateTime      @default(now())
  updatedAt       DateTime      @updatedAt

  package         TravelPackage      @relation(fields: [packageId], references: [id])
  priceTier       PackagePriceTier?  @relation(fields: [priceTierId], references: [id])

  @@index([packageId])
  @@schema("passenger")
}

model AppRelease {
  id          String   @id @default(uuid())
  os          String   // "android" | "ios"
  version     String
  forceUpdate Boolean  @default(false)
  storeLink   String?
  notes       String?
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@unique([os, version])
  @@schema("passenger")
}
