// ---------------------------------------------------------------------------
// RijTheorie - independent Dutch driving theory preparation platform
// PostgreSQL + Prisma.  Everything a product manager might want to change
// (categories, languages, prices, exam rules, feature flags, permissions) lives
// in the database, never in the React tree.
// ---------------------------------------------------------------------------

generator client {
  provider = "prisma-client-js"
  // The query engine is a native binary, so the one generated on a developer's
  // machine is useless on the server. These cover the usual hosting targets:
  // `native` for local work, Debian/Ubuntu with OpenSSL 3 for most VPS and
  // managed Node hosting, and musl for Alpine-based containers.
  //
  // Wrong target = "Query engine binary for current platform could not be
  // found" at runtime, never at build time. Check the host's distribution and
  // `openssl version` before deploying somewhere new.
  binaryTargets = ["native", "debian-openssl-3.0.x", "linux-musl-openssl-3.0.x"]
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// ===========================================================================
// ENUMS
// ===========================================================================

enum ContentStatus {
  DRAFT
  REVIEW
  PUBLISHED
  ARCHIVED
}

enum Difficulty {
  EASY
  MEDIUM
  HARD
}

enum QuestionType {
  MULTIPLE_CHOICE
  MULTIPLE_ANSWER
  TRUE_FALSE
  YES_NO
  NUMERICAL
  DRAG_AND_DROP
  HOTSPOT
  IMAGE
  VIDEO
  SCENARIO
}

enum PracticeMode {
  RANDOM
  CATEGORY
  DIFFICULT
  MISTAKES
  BOOKMARKS
  UNSEEN
  WEAK_AREAS
  DAILY_CHALLENGE
  QUICK
}

enum ExamAttemptStatus {
  IN_PROGRESS
  SUBMITTED
  EXPIRED
  ABANDONED
}

enum ExamKind {
  MOCK
  SMART
  CATEGORY
  PRACTICE
}

enum PlanTier {
  FREE
  BASIC
  PREMIUM
  PREMIUM_PLUS
}

enum BillingInterval {
  MONTHLY
  YEARLY
  LIFETIME
}

enum SubscriptionStatus {
  TRIALING
  ACTIVE
  PAST_DUE
  CANCELED
  INCOMPLETE
  EXPIRED
}

enum PaymentStatus {
  PENDING
  SUCCEEDED
  FAILED
  REFUNDED
}

enum DiscountType {
  PERCENTAGE
  FIXED
}

enum LeadStatus {
  NEW
  CONTACTED
  QUALIFIED
  CONVERTED
  CLOSED
}

enum ModerationStatus {
  VISIBLE
  HIDDEN
  LOCKED
  REMOVED
}

enum ReportReason {
  SPAM
  ABUSE
  OFFENSIVE
  INCORRECT_INFORMATION
  ADVERTISING
  OTHER
}

enum ReportStatus {
  OPEN
  REVIEWING
  RESOLVED
  DISMISSED
}

enum NotificationType {
  SYSTEM
  STUDY_PLAN
  FORUM_REPLY
  SUBSCRIPTION
  STREAK
  ACHIEVEMENT
  EXAM_RESULT
  LEAD
}

enum Transmission {
  MANUAL
  AUTOMATIC
  BOTH
}

enum ThemePreference {
  LIGHT
  DARK
  SYSTEM
}

enum VoteType {
  UP
  DOWN
}

// ===========================================================================
// IDENTITY, ROLES & PERMISSIONS
// ===========================================================================

model User {
  id            String    @id @default(cuid())
  email         String    @unique
  emailVerified DateTime?
  passwordHash  String?
  name          String?
  image         String?
  bio           String?
  city          String?
  isActive      Boolean   @default(true)
  bannedUntil   DateTime?
  banReason     String?
  lastActiveAt  DateTime?
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt

  accounts          Account[]
  sessions          Session[]
  roles             UserRole[]
  preference        UserPreference?
  stats             UserStats?
  progress          Progress[]
  attempts          QuestionAttempt[]
  bookmarks         Bookmark[]
  notes             Note[]
  lessonCompletions LessonCompletion[]
  dailyActivity     DailyActivity[]
  examAttempts      ExamAttempt[]
  studyPlans        StudyPlan[]
  subscriptions     Subscription[]
  payments          Payment[]
  invoices          Invoice[]
  topics            ForumTopic[]
  posts             ForumPost[]
  votes             ForumVote[]
  reportsFiled      ForumReport[]         @relation("ReportAuthor")
  reportsHandled    ForumReport[]         @relation("ReportHandler")
  topicFollows      TopicFollow[]
  followers         UserFollow[]          @relation("Following")
  following         UserFollow[]          @relation("Follower")
  badges            UserBadge[]
  notifications     Notification[]
  questionReports   QuestionReport[]
  schoolMemberships DrivingSchoolUser[]
  leads             DrivingSchoolLead[]
  reviews           DrivingSchoolReview[]
  blogPosts         BlogPost[]
  media             Media[]
  auditLogs         AuditLog[]
  consents          ConsentRecord[]
  aiConversations   AiConversation[]

  @@index([createdAt])
  @@index([lastActiveAt])
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String?
  access_token      String?
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String?
  session_state     String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
  @@index([userId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}

model Role {
  id          String   @id @default(cuid())
  key         String   @unique // STUDENT, DRIVING_SCHOOL, INSTRUCTOR, MODERATOR, EDITOR, ADMIN
  name        String
  description String?
  isSystem    Boolean  @default(true)
  createdAt   DateTime @default(now())

  users       UserRole[]
  permissions RolePermission[]
}

model Permission {
  id          String  @id @default(cuid())
  key         String  @unique // e.g. "question.publish"
  description String?
  group       String? // e.g. "theory"

  roles RolePermission[]
}

model RolePermission {
  roleId       String
  permissionId String

  role       Role       @relation(fields: [roleId], references: [id], onDelete: Cascade)
  permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)

  @@id([roleId, permissionId])
  @@index([permissionId])
}

model UserRole {
  userId     String
  roleId     String
  assignedAt DateTime @default(now())

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)

  @@id([userId, roleId])
  @@index([roleId])
}

model UserFollow {
  followerId  String
  followingId String
  createdAt   DateTime @default(now())

  follower  User @relation("Follower", fields: [followerId], references: [id], onDelete: Cascade)
  following User @relation("Following", fields: [followingId], references: [id], onDelete: Cascade)

  @@id([followerId, followingId])
  @@index([followingId])
}

// ===========================================================================
// LANGUAGES & PREFERENCES
// ===========================================================================

model Language {
  id         String   @id @default(cuid())
  code       String   @unique // nl, en, tr, ar, pl, de, es, fa, uk
  name       String // English name
  nativeName String
  flag       String
  isRtl      Boolean  @default(false)
  isActive   Boolean  @default(true)
  isDefault  Boolean  @default(false)
  sortOrder  Int      @default(0)
  createdAt  DateTime @default(now())

  preferences          UserPreference[]
  categoryTranslations CategoryTranslation[]
  lessonTranslations   LessonTranslation[]
  questionTranslations QuestionTranslation[]
  answerTranslations   AnswerTranslation[]
  planTranslations     SubscriptionPlanTranslation[]
  forumTranslations    ForumCategoryTranslation[]
  badgeTranslations    BadgeTranslation[]
  blogTranslations     BlogPostTranslation[]
  routeSlugs           RouteSlug[]

  @@index([isActive, sortOrder])
}

/// Localised URL segments, so /nl/theorie and /en/theory resolve to one route id.
model RouteSlug {
  id           String @id @default(cuid())
  routeKey     String // "theory", "practice", "exam", ...
  languageCode String
  slug         String

  language Language @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([routeKey, languageCode])
  @@unique([languageCode, slug])
}

model UserPreference {
  id                 String          @id @default(cuid())
  userId             String          @unique
  languageCode       String          @default("nl")
  theme              ThemePreference @default(SYSTEM)
  licenseCategoryId  String?
  targetExamDate     DateTime?
  dailyGoal          Int             @default(20)
  weeklyGoalMinutes  Int             @default(180)
  emailNotifications Boolean         @default(true)
  marketingEmails    Boolean         @default(false)
  publicProfile      Boolean         @default(false)
  reducedMotion      Boolean         @default(false)
  updatedAt          DateTime        @updatedAt

  user            User             @relation(fields: [userId], references: [id], onDelete: Cascade)
  language        Language         @relation(fields: [languageCode], references: [code])
  licenseCategory LicenseCategory? @relation(fields: [licenseCategoryId], references: [id])
}

/// B (car) today; A/C/D can be added without touching a single component.
model LicenseCategory {
  id          String   @id @default(cuid())
  code        String   @unique // B, A, C, D
  name        String
  description String?
  icon        String?
  isActive    Boolean  @default(true)
  sortOrder   Int      @default(0)
  createdAt   DateTime @default(now())

  categories    Category[]
  examTemplates ExamTemplate[]
  preferences   UserPreference[]
}

// ===========================================================================
// THEORY: CATEGORIES & LESSONS
// ===========================================================================

model Category {
  id                String        @id @default(cuid())
  licenseCategoryId String
  key               String        @unique // stable identifier: "traffic-signs"
  icon              String        @default("BookOpen")
  color             String        @default("blue")
  difficulty        Difficulty    @default(MEDIUM)
  sortOrder         Int           @default(0)
  status            ContentStatus @default(PUBLISHED)
  createdAt         DateTime      @default(now())
  updatedAt         DateTime      @updatedAt

  licenseCategory LicenseCategory       @relation(fields: [licenseCategoryId], references: [id], onDelete: Cascade)
  translations    CategoryTranslation[]
  lessons         Lesson[]
  questions       Question[]
  progress        Progress[]
  studyPlanItems  StudyPlanItem[]
  examRules       ExamTemplateRule[]

  @@index([licenseCategoryId, sortOrder])
  @@index([status])
}

model CategoryTranslation {
  id             String  @id @default(cuid())
  categoryId     String
  languageCode   String
  name           String
  slug           String
  description    String?
  seoTitle       String?
  seoDescription String?
  isReviewed     Boolean @default(false)
  isMachineMade  Boolean @default(false)

  category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)
  language Language @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([categoryId, languageCode])
  @@unique([languageCode, slug])
  @@index([languageCode])
}

model Lesson {
  id          String        @id @default(cuid())
  categoryId  String
  key         String        @unique
  sortOrder   Int           @default(0)
  readMinutes Int           @default(5)
  status      ContentStatus @default(PUBLISHED)
  createdAt   DateTime      @default(now())
  updatedAt   DateTime      @updatedAt

  category     Category            @relation(fields: [categoryId], references: [id], onDelete: Cascade)
  translations LessonTranslation[]
  completions  LessonCompletion[]
  bookmarks    Bookmark[]
  notes        Note[]
  studyItems   StudyPlanItem[]

  @@index([categoryId, sortOrder])
  @@index([status])
}

model LessonTranslation {
  id             String   @id @default(cuid())
  lessonId       String
  languageCode   String
  title          String
  slug           String
  intro          String?
  /// Structured blocks: [{ type: "text" | "rule" | "tip" | "mistake" | "example" | "image", ... }]
  body           Json
  seoTitle       String?
  seoDescription String?
  isReviewed     Boolean  @default(false)
  isMachineMade  Boolean  @default(false)
  updatedAt      DateTime @updatedAt

  lesson   Lesson   @relation(fields: [lessonId], references: [id], onDelete: Cascade)
  language Language @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([lessonId, languageCode])
  @@unique([languageCode, slug])
  @@index([languageCode])
}

model LessonCompletion {
  id          String   @id @default(cuid())
  userId      String
  lessonId    String
  completedAt DateTime @default(now())

  user   User   @relation(fields: [userId], references: [id], onDelete: Cascade)
  lesson Lesson @relation(fields: [lessonId], references: [id], onDelete: Cascade)

  @@unique([userId, lessonId])
  @@index([userId])
}

// ===========================================================================
// QUESTION BANK
// ===========================================================================

model Question {
  id          String        @id @default(cuid())
  categoryId  String
  subcategory String?
  type        QuestionType  @default(MULTIPLE_CHOICE)
  difficulty  Difficulty    @default(MEDIUM)
  status      ContentStatus @default(PUBLISHED)
  imageUrl    String?
  imageAlt    String?
  videoUrl    String?
  /// Type-specific payload: hotspot regions, drag targets, numeric tolerance, ...
  meta        Json?
  points      Int           @default(1)
  tags        String[]      @default([])
  isFree      Boolean       @default(false) // usable on the FREE plan
  createdAt   DateTime      @default(now())
  updatedAt   DateTime      @updatedAt

  category      Category              @relation(fields: [categoryId], references: [id], onDelete: Cascade)
  translations  QuestionTranslation[]
  answers       Answer[]
  attempts      QuestionAttempt[]
  bookmarks     Bookmark[]
  notes         Note[]
  examQuestions ExamQuestion[]
  reports       QuestionReport[]
  stat          QuestionStat?

  @@index([categoryId, status])
  @@index([difficulty, status])
  @@index([type])
  @@index([status, isFree])
}

model QuestionTranslation {
  id            String  @id @default(cuid())
  questionId    String
  languageCode  String
  text          String
  explanation   String?
  hint          String?
  isReviewed    Boolean @default(false)
  isMachineMade Boolean @default(false)

  question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)
  language Language @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([questionId, languageCode])
  @@index([languageCode])
}

model Answer {
  id         String  @id @default(cuid())
  questionId String
  key        String // "a", "b", "c" - stable across translations
  isCorrect  Boolean @default(false)
  sortOrder  Int     @default(0)
  imageUrl   String?
  /// Hotspot / drag-drop coordinates when relevant.
  meta       Json?

  question     Question            @relation(fields: [questionId], references: [id], onDelete: Cascade)
  translations AnswerTranslation[]

  @@unique([questionId, key])
  @@index([questionId])
}

model AnswerTranslation {
  id            String  @id @default(cuid())
  answerId      String
  languageCode  String
  text          String
  isReviewed    Boolean @default(false)
  isMachineMade Boolean @default(false)

  answer   Answer   @relation(fields: [answerId], references: [id], onDelete: Cascade)
  language Language @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([answerId, languageCode])
}

model QuestionAttempt {
  id            String        @id @default(cuid())
  userId        String
  questionId    String
  examAttemptId String?
  mode          PracticeMode?
  isCorrect     Boolean
  /// Answer keys the user picked, or the raw value for numeric/hotspot.
  givenAnswer   Json
  timeSpentMs   Int           @default(0)
  languageCode  String        @default("nl")
  createdAt     DateTime      @default(now())

  user     User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)

  @@index([userId, createdAt])
  @@index([userId, questionId])
  @@index([userId, isCorrect])
  @@index([questionId])
}

model QuestionStat {
  questionId    String   @id
  timesAnswered Int      @default(0)
  timesCorrect  Int      @default(0)
  avgTimeMs     Int      @default(0)
  skipCount     Int      @default(0)
  reportCount   Int      @default(0)
  needsReview   Boolean  @default(false)
  updatedAt     DateTime @updatedAt

  question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)

  @@index([needsReview])
}

model QuestionReport {
  id         String       @id @default(cuid())
  questionId String
  userId     String?
  reason     ReportReason
  message    String?
  status     ReportStatus @default(OPEN)
  createdAt  DateTime     @default(now())

  question Question @relation(fields: [questionId], references: [id], onDelete: Cascade)
  user     User?    @relation(fields: [userId], references: [id], onDelete: SetNull)

  @@index([status, createdAt])
  @@index([questionId])
}

model Bookmark {
  id         String   @id @default(cuid())
  userId     String
  questionId String?
  lessonId   String?
  createdAt  DateTime @default(now())

  user     User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  question Question? @relation(fields: [questionId], references: [id], onDelete: Cascade)
  lesson   Lesson?   @relation(fields: [lessonId], references: [id], onDelete: Cascade)

  @@unique([userId, questionId])
  @@unique([userId, lessonId])
  @@index([userId, createdAt])
}

model Note {
  id         String   @id @default(cuid())
  userId     String
  questionId String?
  lessonId   String?
  body       String
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  user     User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  question Question? @relation(fields: [questionId], references: [id], onDelete: Cascade)
  lesson   Lesson?   @relation(fields: [lessonId], references: [id], onDelete: Cascade)

  @@index([userId, updatedAt])
}

// ===========================================================================
// EXAM ENGINE  (fully database-driven, versioned)
// ===========================================================================

model ExamTemplate {
  id                String   @id @default(cuid())
  licenseCategoryId String
  key               String   @unique
  name              String
  description       String?
  kind              ExamKind @default(MOCK)
  questionCount     Int      @default(50)
  timeLimitMinutes  Int      @default(30)
  passingScore      Int      @default(88) // percentage
  /// Optional difficulty mix, e.g. { "EASY": 30, "MEDIUM": 50, "HARD": 20 }
  difficultyMix     Json?
  /// Optional question-type mix.
  typeMix           Json?
  shuffleQuestions  Boolean  @default(true)
  shuffleAnswers    Boolean  @default(true)
  allowReview       Boolean  @default(true)
  showResultsAfter  Boolean  @default(true)
  requiredPlanTier  PlanTier @default(FREE)
  isActive          Boolean  @default(true)
  version           Int      @default(1)
  effectiveFrom     DateTime @default(now())
  createdAt         DateTime @default(now())
  updatedAt         DateTime @updatedAt

  licenseCategory LicenseCategory    @relation(fields: [licenseCategoryId], references: [id], onDelete: Cascade)
  rules           ExamTemplateRule[]
  attempts        ExamAttempt[]

  @@index([isActive, kind])
}

/// "Take N questions from category X at difficulty Y" - the distribution rules.
model ExamTemplateRule {
  id             String      @id @default(cuid())
  examTemplateId String
  categoryId     String?
  difficulty     Difficulty?
  questionCount  Int
  sortOrder      Int         @default(0)

  examTemplate ExamTemplate @relation(fields: [examTemplateId], references: [id], onDelete: Cascade)
  category     Category?    @relation(fields: [categoryId], references: [id], onDelete: Cascade)

  @@index([examTemplateId])
}

model ExamAttempt {
  id               String            @id @default(cuid())
  userId           String
  examTemplateId   String
  status           ExamAttemptStatus @default(IN_PROGRESS)
  languageCode     String            @default("nl")
  /// Frozen copy of the template settings at start time - results stay reproducible
  /// even after an admin edits the template.
  config           Json
  startedAt        DateTime          @default(now())
  expiresAt        DateTime
  submittedAt      DateTime?
  timeSpentSeconds Int               @default(0)
  score            Int? // correct answers
  totalQuestions   Int
  percentage       Int?
  passed           Boolean?

  user         User           @relation(fields: [userId], references: [id], onDelete: Cascade)
  examTemplate ExamTemplate   @relation(fields: [examTemplateId], references: [id], onDelete: Cascade)
  questions    ExamQuestion[]
  answers      ExamAnswer[]

  @@index([userId, startedAt])
  @@index([status, expiresAt])
}

model ExamQuestion {
  id            String   @id @default(cuid())
  examAttemptId String
  questionId    String
  position      Int
  /// Shuffled answer key order for this attempt.
  answerOrder   String[]

  examAttempt ExamAttempt @relation(fields: [examAttemptId], references: [id], onDelete: Cascade)
  question    Question    @relation(fields: [questionId], references: [id], onDelete: Cascade)
  answer      ExamAnswer?

  @@unique([examAttemptId, position])
  @@unique([examAttemptId, questionId])
  @@index([questionId])
}

model ExamAnswer {
  id              String   @id @default(cuid())
  examAttemptId   String
  examQuestionId  String   @unique
  givenAnswer     Json
  isCorrect       Boolean?
  markedForReview Boolean  @default(false)
  timeSpentMs     Int      @default(0)
  answeredAt      DateTime @default(now())

  examAttempt  ExamAttempt  @relation(fields: [examAttemptId], references: [id], onDelete: Cascade)
  examQuestion ExamQuestion @relation(fields: [examQuestionId], references: [id], onDelete: Cascade)

  @@index([examAttemptId])
}

// ===========================================================================
// PROGRESS, STUDY PLANS & GAMIFICATION
// ===========================================================================

model Progress {
  id               String    @id @default(cuid())
  userId           String
  categoryId       String
  questionsSeen    Int       @default(0)
  questionsCorrect Int       @default(0)
  lessonsTotal     Int       @default(0)
  lessonsDone      Int       @default(0)
  timeSpentSeconds Int       @default(0)
  lastPracticedAt  DateTime?
  updatedAt        DateTime  @updatedAt

  user     User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  category Category @relation(fields: [categoryId], references: [id], onDelete: Cascade)

  @@unique([userId, categoryId])
  @@index([userId])
}

model UserStats {
  userId           String    @id
  xp               Int       @default(0)
  level            Int       @default(1)
  streakDays       Int       @default(0)
  longestStreak    Int       @default(0)
  lastStudyDate    DateTime?
  totalAnswered    Int       @default(0)
  totalCorrect     Int       @default(0)
  totalExams       Int       @default(0)
  examsPassed      Int       @default(0)
  studySeconds     Int       @default(0)
  readinessPercent Int       @default(0)
  updatedAt        DateTime  @updatedAt

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model DailyActivity {
  id           String   @id @default(cuid())
  userId       String
  date         DateTime @db.Date
  questions    Int      @default(0)
  correct      Int      @default(0)
  studySeconds Int      @default(0)
  goalReached  Boolean  @default(false)

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, date])
  @@index([userId, date])
}

model StudyPlan {
  id             String    @id @default(cuid())
  userId         String
  targetExamDate DateTime?
  minutesPerDay  Int       @default(30)
  startDate      DateTime  @default(now())
  totalDays      Int       @default(14)
  isActive       Boolean   @default(true)
  /// Snapshot of the answers used to build the plan.
  intake         Json?
  createdAt      DateTime  @default(now())
  updatedAt      DateTime  @updatedAt

  user  User            @relation(fields: [userId], references: [id], onDelete: Cascade)
  items StudyPlanItem[]

  @@index([userId, isActive])
}

model StudyPlanItem {
  id          String    @id @default(cuid())
  studyPlanId String
  dayNumber   Int
  date        DateTime  @db.Date
  categoryId  String?
  lessonId    String?
  taskType    String    @default("LEARN") // LEARN | PRACTICE | EXAM | REVIEW
  targetCount Int       @default(20)
  completed   Boolean   @default(false)
  completedAt DateTime?

  studyPlan StudyPlan @relation(fields: [studyPlanId], references: [id], onDelete: Cascade)
  category  Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
  lesson    Lesson?   @relation(fields: [lessonId], references: [id], onDelete: SetNull)

  @@index([studyPlanId, dayNumber])
}

model Badge {
  id        String  @id @default(cuid())
  key       String  @unique
  icon      String
  /// { "type": "QUESTIONS", "value": 500 }
  criteria  Json
  xpReward  Int     @default(50)
  sortOrder Int     @default(0)
  isActive  Boolean @default(true)

  translations BadgeTranslation[]
  users        UserBadge[]
}

model BadgeTranslation {
  id           String @id @default(cuid())
  badgeId      String
  languageCode String
  name         String
  description  String

  badge    Badge    @relation(fields: [badgeId], references: [id], onDelete: Cascade)
  language Language @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([badgeId, languageCode])
}

model UserBadge {
  userId   String
  badgeId  String
  earnedAt DateTime @default(now())

  user  User  @relation(fields: [userId], references: [id], onDelete: Cascade)
  badge Badge @relation(fields: [badgeId], references: [id], onDelete: Cascade)

  @@id([userId, badgeId])
  @@index([badgeId])
}

// ===========================================================================
// SUBSCRIPTIONS & BILLING
// ===========================================================================

model SubscriptionPlan {
  id                   String   @id @default(cuid())
  key                  String   @unique
  tier                 PlanTier
  priceMonthlyCents    Int      @default(0)
  priceYearlyCents     Int      @default(0)
  currency             String   @default("EUR")
  trialDays            Int      @default(0)
  stripePriceIdMonthly String?
  stripePriceIdYearly  String?
  /// Capability map consumed by the entitlement service.
  features             Json
  isActive             Boolean  @default(true)
  isPopular            Boolean  @default(false)
  sortOrder            Int      @default(0)
  createdAt            DateTime @default(now())
  updatedAt            DateTime @updatedAt

  translations  SubscriptionPlanTranslation[]
  subscriptions Subscription[]

  @@index([isActive, sortOrder])
}

model SubscriptionPlanTranslation {
  id           String   @id @default(cuid())
  planId       String
  languageCode String
  name         String
  tagline      String?
  /// Bullet list shown on the pricing card.
  features     String[] @default([])

  plan     SubscriptionPlan @relation(fields: [planId], references: [id], onDelete: Cascade)
  language Language         @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([planId, languageCode])
}

model Subscription {
  id                   String             @id @default(cuid())
  userId               String
  planId               String
  status               SubscriptionStatus @default(ACTIVE)
  interval             BillingInterval    @default(MONTHLY)
  currentPeriodStart   DateTime           @default(now())
  currentPeriodEnd     DateTime
  cancelAtPeriodEnd    Boolean            @default(false)
  canceledAt           DateTime?
  trialEndsAt          DateTime?
  stripeCustomerId     String?
  stripeSubscriptionId String?            @unique
  couponId             String?
  createdAt            DateTime           @default(now())
  updatedAt            DateTime           @updatedAt

  user     User             @relation(fields: [userId], references: [id], onDelete: Cascade)
  plan     SubscriptionPlan @relation(fields: [planId], references: [id])
  coupon   Coupon?          @relation(fields: [couponId], references: [id], onDelete: SetNull)
  payments Payment[]
  invoices Invoice[]

  @@index([userId, status])
  @@index([status, currentPeriodEnd])
}

model Payment {
  id                    String        @id @default(cuid())
  userId                String
  subscriptionId        String?
  amountCents           Int
  currency              String        @default("EUR")
  status                PaymentStatus @default(PENDING)
  method                String? // card, ideal, apple_pay, google_pay
  stripePaymentIntentId String?       @unique
  failureReason         String?
  refundedCents         Int           @default(0)
  createdAt             DateTime      @default(now())

  user         User          @relation(fields: [userId], references: [id], onDelete: Cascade)
  subscription Subscription? @relation(fields: [subscriptionId], references: [id], onDelete: SetNull)

  @@index([userId, createdAt])
  @@index([status])
}

model Invoice {
  id              String   @id @default(cuid())
  userId          String
  subscriptionId  String?
  number          String   @unique
  amountCents     Int
  taxCents        Int      @default(0)
  currency        String   @default("EUR")
  status          String   @default("PAID")
  pdfUrl          String?
  stripeInvoiceId String?
  issuedAt        DateTime @default(now())

  user         User          @relation(fields: [userId], references: [id], onDelete: Cascade)
  subscription Subscription? @relation(fields: [subscriptionId], references: [id], onDelete: SetNull)

  @@index([userId, issuedAt])
}

model Coupon {
  id             String       @id @default(cuid())
  code           String       @unique
  discountType   DiscountType @default(PERCENTAGE)
  amount         Int // percent, or cents when FIXED
  currency       String       @default("EUR")
  maxRedemptions Int?
  timesRedeemed  Int          @default(0)
  validFrom      DateTime     @default(now())
  validUntil     DateTime?
  isActive       Boolean      @default(true)
  createdAt      DateTime     @default(now())

  subscriptions Subscription[]
}

// ===========================================================================
// FORUM
// ===========================================================================

model ForumCategory {
  id           String   @id @default(cuid())
  key          String   @unique
  icon         String   @default("MessageCircle")
  color        String   @default("blue")
  /// Optional language-restricted room (the "Language communities" section).
  onlyLanguage String?
  sortOrder    Int      @default(0)
  isActive     Boolean  @default(true)
  createdAt    DateTime @default(now())

  translations ForumCategoryTranslation[]
  topics       ForumTopic[]
}

model ForumCategoryTranslation {
  id              String  @id @default(cuid())
  forumCategoryId String
  languageCode    String
  name            String
  slug            String
  description     String?

  forumCategory ForumCategory @relation(fields: [forumCategoryId], references: [id], onDelete: Cascade)
  language      Language      @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([forumCategoryId, languageCode])
  @@unique([languageCode, slug])
}

model ForumTopic {
  id              String           @id @default(cuid())
  forumCategoryId String
  authorId        String?
  title           String
  slug            String           @unique
  body            String
  languageCode    String           @default("nl")
  tags            String[]         @default([])
  status          ModerationStatus @default(VISIBLE)
  isPinned        Boolean          @default(false)
  viewCount       Int              @default(0)
  replyCount      Int              @default(0)
  score           Int              @default(0)
  lastPostAt      DateTime         @default(now())
  createdAt       DateTime         @default(now())
  updatedAt       DateTime         @updatedAt

  forumCategory ForumCategory @relation(fields: [forumCategoryId], references: [id], onDelete: Cascade)
  author        User?         @relation(fields: [authorId], references: [id], onDelete: SetNull)
  posts         ForumPost[]
  votes         ForumVote[]
  follows       TopicFollow[]
  reports       ForumReport[]

  @@index([forumCategoryId, lastPostAt])
  @@index([status, lastPostAt])
  @@index([authorId])
}

model ForumPost {
  id        String           @id @default(cuid())
  topicId   String
  authorId  String?
  parentId  String?
  body      String
  status    ModerationStatus @default(VISIBLE)
  score     Int              @default(0)
  editedAt  DateTime?
  createdAt DateTime         @default(now())

  topic   ForumTopic    @relation(fields: [topicId], references: [id], onDelete: Cascade)
  author  User?         @relation(fields: [authorId], references: [id], onDelete: SetNull)
  parent  ForumPost?    @relation("PostReplies", fields: [parentId], references: [id], onDelete: Cascade)
  replies ForumPost[]   @relation("PostReplies")
  votes   ForumVote[]
  reports ForumReport[]

  @@index([topicId, createdAt])
  @@index([authorId])
}

model ForumVote {
  id        String   @id @default(cuid())
  userId    String
  topicId   String?
  postId    String?
  type      VoteType @default(UP)
  createdAt DateTime @default(now())

  user  User        @relation(fields: [userId], references: [id], onDelete: Cascade)
  topic ForumTopic? @relation(fields: [topicId], references: [id], onDelete: Cascade)
  post  ForumPost?  @relation(fields: [postId], references: [id], onDelete: Cascade)

  @@unique([userId, topicId])
  @@unique([userId, postId])
}

model TopicFollow {
  userId    String
  topicId   String
  createdAt DateTime @default(now())

  user  User       @relation(fields: [userId], references: [id], onDelete: Cascade)
  topic ForumTopic @relation(fields: [topicId], references: [id], onDelete: Cascade)

  @@id([userId, topicId])
  @@index([topicId])
}

model ForumReport {
  id          String       @id @default(cuid())
  reporterId  String?
  topicId     String?
  postId      String?
  reason      ReportReason
  message     String?
  status      ReportStatus @default(OPEN)
  handledById String?
  handledAt   DateTime?
  createdAt   DateTime     @default(now())

  reporter  User?       @relation("ReportAuthor", fields: [reporterId], references: [id], onDelete: SetNull)
  handledBy User?       @relation("ReportHandler", fields: [handledById], references: [id], onDelete: SetNull)
  topic     ForumTopic? @relation(fields: [topicId], references: [id], onDelete: Cascade)
  post      ForumPost?  @relation(fields: [postId], references: [id], onDelete: Cascade)

  @@index([status, createdAt])
}

// ===========================================================================
// DRIVING SCHOOLS
// ===========================================================================

model DrivingSchool {
  id            String       @id @default(cuid())
  slug          String       @unique
  name          String
  description   String?
  logoUrl       String?
  coverUrl      String?
  email         String?
  phone         String?
  website       String?
  languages     String[]     @default([])
  transmission  Transmission @default(BOTH)
  /// { "mon": "09:00-17:00", ... }
  openingHours  Json?
  isVerified    Boolean      @default(false)
  isActive      Boolean      @default(true)
  ratingAverage Float        @default(0)
  ratingCount   Int          @default(0)
  createdAt     DateTime     @default(now())
  updatedAt     DateTime     @updatedAt

  locations   DrivingSchoolLocation[]
  instructors DrivingSchoolInstructor[]
  vehicles    DrivingSchoolVehicle[]
  packages    DrivingSchoolPackage[]
  leads       DrivingSchoolLead[]
  reviews     DrivingSchoolReview[]
  members     DrivingSchoolUser[]

  @@index([isActive, ratingAverage])
}

model DrivingSchoolUser {
  userId   String
  schoolId String
  role     String @default("OWNER") // OWNER | MANAGER | INSTRUCTOR

  user   User          @relation(fields: [userId], references: [id], onDelete: Cascade)
  school DrivingSchool @relation(fields: [schoolId], references: [id], onDelete: Cascade)

  @@id([userId, schoolId])
  @@index([schoolId])
}

model DrivingSchoolLocation {
  id         String  @id @default(cuid())
  schoolId   String
  label      String?
  street     String
  postalCode String
  city       String
  province   String?
  country    String  @default("NL")
  latitude   Float?
  longitude  Float?
  isPrimary  Boolean @default(false)

  school DrivingSchool @relation(fields: [schoolId], references: [id], onDelete: Cascade)

  @@index([city])
  @@index([postalCode])
  @@index([latitude, longitude])
}

model DrivingSchoolInstructor {
  id           String       @id @default(cuid())
  schoolId     String
  name         String
  photoUrl     String?
  bio          String?
  languages    String[]     @default([])
  transmission Transmission @default(BOTH)
  isActive     Boolean      @default(true)

  school DrivingSchool @relation(fields: [schoolId], references: [id], onDelete: Cascade)

  @@index([schoolId])
}

model DrivingSchoolVehicle {
  id           String       @id @default(cuid())
  schoolId     String
  make         String
  model        String
  year         Int?
  transmission Transmission @default(MANUAL)
  photoUrl     String?

  school DrivingSchool @relation(fields: [schoolId], references: [id], onDelete: Cascade)

  @@index([schoolId])
}

model DrivingSchoolPackage {
  id           String  @id @default(cuid())
  schoolId     String
  name         String
  description  String?
  lessonCount  Int
  priceCents   Int
  currency     String  @default("EUR")
  includesExam Boolean @default(false)
  isActive     Boolean @default(true)
  sortOrder    Int     @default(0)

  school DrivingSchool @relation(fields: [schoolId], references: [id], onDelete: Cascade)

  @@index([schoolId, isActive])
}

model DrivingSchoolLead {
  id            String       @id @default(cuid())
  schoolId      String
  userId        String?
  name          String
  email         String
  phone         String?
  city          String?
  languageCode  String       @default("nl")
  transmission  Transmission @default(MANUAL)
  preferredDays String[]     @default([])
  message       String?
  status        LeadStatus   @default(NEW)
  notes         String?
  createdAt     DateTime     @default(now())
  updatedAt     DateTime     @updatedAt

  school DrivingSchool @relation(fields: [schoolId], references: [id], onDelete: Cascade)
  user   User?         @relation(fields: [userId], references: [id], onDelete: SetNull)

  @@index([schoolId, status, createdAt])
}

model DrivingSchoolReview {
  id                  String           @id @default(cuid())
  schoolId            String
  userId              String?
  authorName          String
  ratingOverall       Int
  ratingInstructor    Int?
  ratingCommunication Int?
  ratingValue         Int?
  ratingExperience    Int?
  title               String?
  body                String
  status              ModerationStatus @default(VISIBLE)
  response            String?
  respondedAt         DateTime?
  createdAt           DateTime         @default(now())

  school DrivingSchool @relation(fields: [schoolId], references: [id], onDelete: Cascade)
  user   User?         @relation(fields: [userId], references: [id], onDelete: SetNull)

  @@index([schoolId, status, createdAt])
}

// ===========================================================================
// CONTENT, MEDIA, NOTIFICATIONS, PLATFORM
// ===========================================================================

model BlogPost {
  id          String        @id @default(cuid())
  authorId    String?
  key         String        @unique
  coverUrl    String?
  status      ContentStatus @default(DRAFT)
  publishedAt DateTime?
  createdAt   DateTime      @default(now())
  updatedAt   DateTime      @updatedAt

  author       User?                 @relation(fields: [authorId], references: [id], onDelete: SetNull)
  translations BlogPostTranslation[]

  @@index([status, publishedAt])
}

model BlogPostTranslation {
  id             String  @id @default(cuid())
  blogPostId     String
  languageCode   String
  title          String
  slug           String
  excerpt        String?
  body           String
  seoTitle       String?
  seoDescription String?
  isReviewed     Boolean @default(false)
  isMachineMade  Boolean @default(false)

  blogPost BlogPost @relation(fields: [blogPostId], references: [id], onDelete: Cascade)
  language Language @relation(fields: [languageCode], references: [code], onDelete: Cascade)

  @@unique([blogPostId, languageCode])
  @@unique([languageCode, slug])
}

model Media {
  id         String   @id @default(cuid())
  uploaderId String?
  url        String
  key        String   @unique
  mimeType   String
  sizeBytes  Int
  width      Int?
  height     Int?
  alt        String?
  folder     String   @default("general")
  createdAt  DateTime @default(now())

  uploader User? @relation(fields: [uploaderId], references: [id], onDelete: SetNull)

  @@index([folder, createdAt])
}

model Notification {
  id        String           @id @default(cuid())
  userId    String
  type      NotificationType @default(SYSTEM)
  titleKey  String
  /// Interpolation values for the translated message.
  params    Json?
  url       String?
  readAt    DateTime?
  createdAt DateTime         @default(now())

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId, readAt, createdAt])
}

model FeatureFlag {
  id             String   @id @default(cuid())
  key            String   @unique
  description    String?
  isEnabled      Boolean  @default(false)
  /// Percentage rollout 0-100, applied on top of isEnabled.
  rolloutPercent Int      @default(100)
  updatedAt      DateTime @updatedAt
}

model Setting {
  key       String   @id
  value     Json
  group     String   @default("general")
  updatedAt DateTime @updatedAt

  @@index([group])
}

model AuditLog {
  id         String   @id @default(cuid())
  userId     String?
  action     String
  entityType String
  entityId   String?
  before     Json?
  after      Json?
  ip         String?
  userAgent  String?
  createdAt  DateTime @default(now())

  user User? @relation(fields: [userId], references: [id], onDelete: SetNull)

  @@index([entityType, entityId])
  @@index([userId, createdAt])
}

model ConsentRecord {
  id        String   @id @default(cuid())
  userId    String?
  anonId    String?
  necessary Boolean  @default(true)
  analytics Boolean  @default(false)
  marketing Boolean  @default(false)
  ip        String?
  userAgent String?
  createdAt DateTime @default(now())

  user User? @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId, createdAt])
}

model AiConversation {
  id        String   @id @default(cuid())
  userId    String
  title     String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  user     User        @relation(fields: [userId], references: [id], onDelete: Cascade)
  messages AiMessage[]

  @@index([userId, updatedAt])
}

model AiMessage {
  id             String   @id @default(cuid())
  conversationId String
  role           String // user | assistant
  content        String
  /// Lesson/question ids used as grounding for this answer.
  sources        Json?
  createdAt      DateTime @default(now())

  conversation AiConversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)

  @@index([conversationId, createdAt])
}
