generator client {
  provider      = "prisma-client-js"
  engineType    = "client"
  binaryTargets = ["native", "debian-openssl-1.0.x"]
}

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

enum UserRole {
  candidate
  employer
  admin
  super_admin
  trusted_person
}

enum CompanyVerificationStatus {
  unverified
  pending
  verified
  rejected
  suspended
}

enum JobStatus {
  draft
  pending_review
  published
  rejected
  closed
  archived
}

enum JobCategoryStatus {
  active
  inactive
}

enum ApplicationStatus {
  draft
  generated
  submitted
  shortlisted
  rejected
  withdrawn
}

enum DocumentType {
  cv
  id_copy
  certificate
  proof_of_residence
  z83
  application_pack
  other
}

enum WalletTransactionType {
  purchase
  usage
  refund
  adjustment
}

enum WalletTransactionStatus {
  pending
  successful
  failed
  reversed
}

enum AiRequestStatus {
  pending
  completed
  failed
}

enum InterviewSafetyStatus {
  scheduled
  active
  checkin_requested
  safe
  emergency
  cancelled
}

enum EmergencyLevel {
  reminder
  trusted_alert
  repeated_alert
  manual_escalation
}

enum ReportStatus {
  open
  investigating
  resolved
  dismissed
}

enum AlertFrequency {
  instant
  daily
  weekly
  closing_soon
}

enum AdPlacement {
  homepage_top
  homepage_mid
  jobs_feed_native
  jobs_sidebar
  job_details_sidebar
  mobile_feed
  mobile_job_details
}

enum AdStatus {
  draft
  active
  paused
  expired
  rejected
}

enum SponsoredJobStatus {
  draft
  active
  paused
  expired
  rejected
}

enum FileAssetType {
  uploaded_document
  generated_cv
  generated_z83
  application_pack
  template
  employer_proof
  ad_asset
  other
}

enum FileVisibility {
  private
  admin_only
  owner_only
  public
}

enum NotificationChannel {
  email
  push
  in_app
  multi
}

enum NotificationTemplateStatus {
  draft
  active
  archived
}

enum NotificationCampaignStatus {
  draft
  scheduled
  running
  sending
  completed
  sent
  failed
  cancelled
}

enum NotificationDeliveryStatus {
  pending
  sent
  failed
  skipped
  mock_sent
  read
}

enum NotificationAudienceType {
  all_candidates
  matched_candidates
  province_candidates
  saved_job_candidates
  employers
  admins
  custom
}

model User {
  id                          String                        @id @default(uuid())
  fullName                    String
  email                       String                        @unique
  passwordHash                String
  role                        UserRole
  phone                       String?
  isPremium                   Boolean                       @default(false)
  premiumUntil                DateTime?
  emailVerified               Boolean                       @default(false)
  emailVerifiedAt             DateTime?
  emailVerificationTokenHash  String?
  emailVerificationExpiresAt  DateTime?
  lastVerificationEmailSentAt DateTime?
  createdAt                   DateTime                      @default(now())
  updatedAt                   DateTime                      @updatedAt
  candidateProfile            CandidateProfile?
  cvProfile                   CVProfile?
  cvGenerated                 CVGenerated[]
  cvGenerationCounters        CVGenerationCounter[]
  cvRewriteUsages             CVRewriteUsage[]
  cvRewriteRequests           CVRewriteRequest[]
  employerProfile             EmployerProfile?
  z83Profile                  Z83Profile?
  candidateSignature          CandidateSignature?
  z83Generations              Z83Generation[]
  z83UsageCounters            Z83UsageCounter[]
  applicationPackCounters     ApplicationPackUsageCounter[]
  wallet                      UserWallet?
  documents                   CandidateDocument[]
  applications                Application[]
  deviceTokens                DeviceToken[]
  notificationPreference      NotificationPreference?
  matchDigests                CandidateMatchDigest[]
  auditLogs                   AuditLog[]
  fileAssetsOwned             FileAsset[]                   @relation("FileOwner")
  fileAssetsUploaded          FileAsset[]                   @relation("FileUploader")
  notificationTemplates       NotificationTemplate[]
  notificationCampaigns       NotificationCampaign[]
  notificationDeliveries      NotificationDelivery[]
  emailLogs                   EmailLog[]
  adminSettings               AdminSetting[]
  savedJobs                   SavedJob[]
  jobAlerts                   JobAlert[]
  jobViews                    JobView[]
  analyticsSessions           AnalyticsSession[]
  analyticsPageViews          AnalyticsPageView[]
  analyticsEvents             AnalyticsEvent[]
  adImpressions               AdImpression[]
  adClicks                    AdClick[]
  rewardedAdGrants            RewardedAdGrant[]
  notificationLogs            NotificationLog[]
}

model Z83Profile {
  id                                    String    @id @default(uuid())
  userId                                String    @unique
  surname                               String?
  fullNames                             String?
  dateOfBirth                           DateTime?
  idNumber                              String?
  passportNumber                        String?
  race                                  String?
  gender                                String?
  hasDisability                         Boolean?
  isSouthAfricanCitizen                 Boolean?
  nationality                           String?
  hasWorkPermit                         Boolean?
  hasCriminalOffence                    Boolean?
  criminalOffenceDetails                String?
  hasPendingCriminalCase                Boolean?
  pendingCriminalCaseDetails            String?
  dismissedForPublicServiceMisconduct   Boolean?
  misconductDetails                     String?
  hasPendingDisciplinaryCase            Boolean?
  disciplinaryCaseDetails               String?
  resignedPendingDisciplinary           Boolean?
  resignedPendingDisciplinaryDetails    String?
  dischargedForIllHealth                Boolean?
  conductsBusinessWithState             Boolean?
  businessWithStateDetails              String?
  willRelinquishBusinessInterests       Boolean?
  privateSectorYears                    Int?
  publicSectorYears                     Int?
  professionalRegistrationDate          DateTime?
  professionalRegistrationNumber        String?
  preferredLanguage                     String?
  correspondenceMethod                  String?
  contactDetails                        String?
  languagesJson                         Json      @default("[]")
  qualificationsJson                    Json      @default("[]")
  workExperienceJson                    Json      @default("[]")
  referencesJson                        Json      @default("[]")
  previousPublicServiceCondition        Boolean?
  previousPublicServiceConditionDetails String?
  noticePeriodOrStartDate               String?
  initials                              String?
  createdAt                             DateTime  @default(now())
  updatedAt                             DateTime  @updatedAt
  user                                  User      @relation(fields: [userId], references: [id], onDelete: Cascade)

  // TODO: Encrypt sensitive identity and declaration fields at rest before production launch.
}

model CandidateSignature {
  id                 String    @id @default(uuid())
  userId             String    @unique
  signatureImageUrl  String?
  initialsImageUrl   String?
  typedInitials      String?
  consentConfirmedAt DateTime?
  createdAt          DateTime  @default(now())
  updatedAt          DateTime  @updatedAt
  user               User      @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model Z83Generation {
  id                        String   @id @default(uuid())
  userId                    String
  jobId                     String
  templateVersion           String
  status                    String   @default("generated")
  sourceProfileSnapshotJson Json
  sourceJobSnapshotJson     Json
  filledFieldSnapshotJson   Json
  outputFileUrl             String?
  consumedFreeQuota         Boolean  @default(false)
  consumedTokens            Int      @default(0)
  createdAt                 DateTime @default(now())
  user                      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  job                       JobPost  @relation(fields: [jobId], references: [id], onDelete: Cascade)
}

model Z83UsageCounter {
  id                  String   @id @default(uuid())
  userId              String
  weekStartDate       DateTime
  freeGenerationsUsed Int      @default(0)
  createdAt           DateTime @default(now())
  updatedAt           DateTime @updatedAt
  user                User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, weekStartDate])
}

model CandidateProfile {
  id                     String   @id @default(uuid())
  userId                 String   @unique
  educationLevel         String?
  province               String?
  city                   String?
  skills                 Json     @default("[]")
  driversLicence         Json     @default("[]")
  experienceYears        Int      @default(0)
  preferredRadiusKm      Int      @default(50)
  preferredJobTypes      Json     @default("[]")
  preferredRoles         Json     @default("[]")
  preferredJobCategories Json     @default("[]")
  qualifications         Json     @default("[]")
  certifications         Json     @default("[]")
  languages              Json     @default("[]")
  workSectors            Json     @default("[]")
  willingToRelocate      Boolean  @default(false)
  alertPreference        String?
  bio                    String?  @db.Text
  createdAt              DateTime @default(now())
  updatedAt              DateTime @updatedAt
  user                   User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model RewardedAdGrant {
  id             String    @id @default(uuid())
  userId         String
  feature        String
  adUnitId       String?
  transactionId  String?   @unique
  clientEarnedAt DateTime?
  verifiedAt     DateTime?
  consumedAt     DateTime?
  expiresAt      DateTime
  createdAt      DateTime  @default(now())
  updatedAt      DateTime  @updatedAt
  user           User      @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId, feature, consumedAt, expiresAt])
  @@index([createdAt])
}

model EmployerProfile {
  id                 String                    @id @default(uuid())
  userId             String                    @unique
  companyName        String
  registrationNumber String?
  companyEmail       String?
  phone              String?
  website            String?
  province           String?
  city               String?
  address            String?
  industry           String?
  verificationStatus CompanyVerificationStatus @default(pending)
  createdAt          DateTime                  @default(now())
  updatedAt          DateTime                  @updatedAt
  user               User                      @relation(fields: [userId], references: [id], onDelete: Cascade)
  jobs               JobPost[]
}

model JobPost {
  id                          String             @id @default(uuid())
  employerId                  String?
  createdByAdminId            String?
  categoryId                  String?
  title                       String
  shortSummary                String?            @db.Text
  department                  String?
  companyName                 String?
  companyLogoUrl              String?
  companyIconUrl              String?
  employerProfileId           String?
  website                     String?
  contactEmail                String?
  contactPhone                String?
  province                    String
  city                        String?
  address                     String?
  workMode                    String?
  category                    String?
  categoryName                String?
  categorySlug                String?
  closingDate                 DateTime?
  closingTime                 String?
  employmentType              String?
  salary                      String?            @db.Text
  numberOfPosts               Int                @default(1)
  educationLevel              String?
  requiredSkills              Json               @default("[]")
  requiredDriversLicence      Json               @default("[]")
  requiredExperienceYears     Int                @default(0)
  requiredDocuments           Json               @default("[]")
  requiredQualifications      Json               @default("[]")
  requiredCertifications      Json               @default("[]")
  languageRequirements        Json               @default("[]")
  otherEligibility            String?
  ageRequirement              String?
  description                 String?            @db.LongText
  descriptionHtml             String?            @db.LongText
  descriptionText             String?            @db.LongText
  duties                      String?            @db.LongText
  dutiesHtml                  String?            @db.LongText
  dutiesText                  String?            @db.LongText
  requirementsHtml            String?            @db.LongText
  requirementsText            String?            @db.LongText
  howToApply                  String?            @db.LongText
  howToApplyHtml              String?            @db.LongText
  howToApplyText              String?            @db.LongText
  applicationInstructions     String?            @db.LongText
  applicationInstructionsHtml String?            @db.LongText
  applicationInstructionsText String?            @db.LongText
  applicationMethod           String?
  applicationUrl              String?            @db.Text
  applicationEmail            String?
  applicationAddress          String?            @db.LongText
  sourceUrl                   String?            @db.Text
  sourceName                  String?
  referenceNumber             String?
  referenceSource             String?
  referenceOptionsJson        Json?
  circularName                String?
  circularNumber              Int?
  circularYear                Int?
  circularTitle               String?
  sourceType                  String?
  sourcePdfUrl                String?
  governmentSourceName        String?
  governmentSourceUrl         String?            @db.Text
  sourceCircularUrl           String?            @db.Text
  sourceCircularNumber        String?
  sourceCircularYear          String?
  sourcePageStart             Int?
  sourcePageEnd               Int?
  sourceDepartmentUrl         String?            @db.Text
  sourceLastCheckedAt         DateTime?
  sourceVerified              Boolean            @default(false)
  referenceNotStated          Boolean            @default(false)
  closingDateNotStated        Boolean            @default(false)
  dpsaCircularId              String?
  dpsaExtractedPostId         String?            @unique
  dpsaDepartmentName          String?
  numberOfPostsText           String?
  sourceStatedPosts           Boolean            @default(false)
  requiredDocumentsText       String?            @db.Text
  enquiriesText               String?            @db.Text
  notesHtml                   String?            @db.LongText
  notesText                   String?            @db.LongText
  departmentScope             String?            @db.Text
  postLevel                   String?
  adminNote                   String?            @db.Text
  visibility                  String?
  slug                        String?            @unique
  publishDate                 DateTime?
  closeDate                   DateTime?
  seoTitle                    String?
  seoDescription              String?
  focusKeyword                String?            @db.Text
  canonicalUrl                String?
  publicContentHtml           String?            @db.LongText
  publicContentText           String?            @db.LongText
  contentDetailsJson          Json?
  structuredSectionsJson      Json?
  rankMathJson                Json?
  readabilityScore            Int?
  readabilityIssuesJson       Json?
  contentFormatVersion        String?
  contentFormattingStatus     String?
  contentFormattedAt          DateTime?
  indexedAt                   DateTime?
  noIndex                     Boolean            @default(false)
  z83Required                 Boolean            @default(false)
  status                      JobStatus          @default(pending_review)
  reviewNote                  String?            @db.Text
  isGovernment                Boolean            @default(false)
  isSponsored                 Boolean            @default(false)
  isFeatured                  Boolean            @default(false)
  sponsoredUntil              DateTime?
  viewsCount                  Int                @default(0)
  createdAt                   DateTime           @default(now())
  updatedAt                   DateTime           @updatedAt
  employer                    EmployerProfile?   @relation(fields: [employerId], references: [id])
  categoryRef                 JobCategory?       @relation(fields: [categoryId], references: [id])
  applications                Application[]
  reports                     JobReport[]
  savedJobs                   SavedJob[]
  alerts                      JobAlert[]
  views                       JobView[]
  sponsoredPlacements         SponsoredJob[]
  z83Generations              Z83Generation[]
  cvGenerated                 CVGenerated[]
  cvRewriteRequests           CVRewriteRequest[]
  dpsaCircular                DpsaCircular?      @relation(fields: [dpsaCircularId], references: [id], onDelete: SetNull)
  dpsaExtraction              DpsaExtractedPost?

  @@index([status, createdAt])
  @@index([status, province])
  @@index([status, categorySlug])
  @@index([status, isGovernment])
  @@index([status, isSponsored])
  @@index([closingDate])
  @@index([province, city])
  @@index([title])
  @@index([dpsaCircularId])
  @@index([sourceType, circularYear, circularNumber])
}

model DpsaCircular {
  id                    String                  @id @default(uuid())
  circularNumber        Int?
  circularYear          Int?
  title                 String?
  sourceFileName        String
  originalFileName      String
  sourcePdfUrl          String
  governmentSourceUrl   String?                 @db.Text
  sourceCircularUrl     String?                 @db.Text
  sourceLastCheckedAt   DateTime?
  sourceVerified        Boolean                 @default(false)
  sourceLocalPath       String
  sourceFileHash        String                  @unique
  sourceFileSize        Int?
  pageCount             Int                     @default(0)
  closingDate           DateTime?
  importStatus          String                  @default("uploaded")
  uploadedByAdminId     String?
  extractionVersion     String?
  extractionStrategy    String                  @default("deterministic")
  rawText               String?                 @db.LongText
  circularNotes         String?                 @db.Text
  applicationDisclaimer String?                 @db.Text
  extractionReportJson  Json                    @default("{}")
  createdAt             DateTime                @default(now())
  updatedAt             DateTime                @updatedAt
  extractedAt           DateTime?
  importedAt            DateTime?
  publishedAt           DateTime?
  pages                 DpsaCircularPage[]
  departmentSections    DpsaDepartmentSection[]
  extractedPosts        DpsaExtractedPost[]
  jobPosts              JobPost[]

  @@unique([circularYear, circularNumber])
  @@index([importStatus, createdAt])
}

model DpsaCircularPage {
  id                     String       @id @default(uuid())
  circularId             String
  pageNumber             Int
  text                   String       @db.LongText
  cleanedText            String       @db.LongText
  detectedDepartmentName String?
  detectedHeadingsJson   Json         @default("[]")
  lowTextWarning         Boolean      @default(false)
  createdAt              DateTime     @default(now())
  circular               DpsaCircular @relation(fields: [circularId], references: [id], onDelete: Cascade)

  @@unique([circularId, pageNumber])
  @@index([circularId])
}

model DpsaDepartmentSection {
  id                 String              @id @default(uuid())
  circularId         String
  departmentName     String
  normalizedName     String
  startPage          Int
  endPage            Int
  sectionOrder       Int
  applicationsText   String?             @db.Text
  applicationAddress String?             @db.Text
  notesText          String?             @db.Text
  enquiriesText      String?             @db.Text
  closingDate        DateTime?
  rawText            String?             @db.LongText
  createdAt          DateTime            @default(now())
  updatedAt          DateTime            @updatedAt
  circular           DpsaCircular        @relation(fields: [circularId], references: [id], onDelete: Cascade)
  extractedPosts     DpsaExtractedPost[]

  @@unique([circularId, sectionOrder])
  @@index([circularId, normalizedName])
}

model DpsaExtractedPost {
  id                          String                 @id @default(uuid())
  circularId                  String
  departmentSectionId         String?
  postNumber                  String?
  title                       String                 @db.Text
  branch                      String?                @db.Text
  directorate                 String?                @db.Text
  component                   String?                @db.Text
  departmentName              String
  numberOfPosts               Int                    @default(1)
  numberOfPostsText           String?
  sourceStatedPosts           Boolean                @default(false)
  referenceNumber             String?
  referenceSource             String?
  referenceOptionsJson        Json                   @default("[]")
  salaryText                  String?                @db.Text
  salaryLevel                 String?
  centreText                  String?                @db.Text
  province                    String?
  city                        String?
  requirementsText            String?                @db.LongText
  dutiesText                  String?                @db.LongText
  knowledgeSkillsText         String?                @db.LongText
  enquiriesText               String?                @db.Text
  applicationInstructionsText String?                @db.LongText
  applicationMethod           String?
  applicationUrl              String?
  applicationEmail            String?
  applicationAddress          String?                @db.Text
  closingDate                 DateTime?
  notesText                   String?                @db.LongText
  requiredDocumentsText       String?                @db.Text
  z83Required                 Boolean                @default(false)
  employmentType              String?
  educationLevel              String?
  requiredSkillsJson          Json                   @default("[]")
  requiredDriversLicenceJson  Json                   @default("[]")
  requiredExperienceYears     Int                    @default(0)
  requiredQualificationsJson  Json                   @default("[]")
  requiredCertificationsJson  Json                   @default("[]")
  jobCategoryName             String?
  jobCategorySlug             String?
  sourcePageStart             Int
  sourcePageEnd               Int
  rawText                     String                 @db.LongText
  extractionStrategy          String                 @default("deterministic")
  extractionVersion           String
  confidenceScore             Int                    @default(0)
  warningsJson                Json                   @default("[]")
  missingFieldsJson           Json                   @default("[]")
  duplicateMatchesJson        Json                   @default("[]")
  formattedContentJson        Json?
  formattingStatus            String                 @default("not_generated")
  formattingApprovedAt        DateTime?
  formattedByAdminId          String?
  formattingUpdatedAt         DateTime?
  reviewStatus                String                 @default("pending")
  importStatus                String                 @default("not_imported")
  reviewedByAdminId           String?
  reviewedAt                  DateTime?
  mappedJobPostId             String?                @unique
  createdAt                   DateTime               @default(now())
  updatedAt                   DateTime               @updatedAt
  circular                    DpsaCircular           @relation(fields: [circularId], references: [id], onDelete: Cascade)
  departmentSection           DpsaDepartmentSection? @relation(fields: [departmentSectionId], references: [id], onDelete: SetNull)
  mappedJobPost               JobPost?               @relation(fields: [mappedJobPostId], references: [id], onDelete: SetNull)

  @@index([circularId, reviewStatus])
  @@index([circularId, importStatus])
  @@index([departmentName, referenceNumber])
}

model JobCategory {
  id          String            @id @default(uuid())
  name        String            @unique
  slug        String            @unique
  description String?
  status      JobCategoryStatus @default(active)
  createdAt   DateTime          @default(now())
  updatedAt   DateTime          @updatedAt
  jobs        JobPost[]
}

model GuestToolUsage {
  id            String    @id @default(uuid())
  anonymousId   String
  weekStartDate DateTime
  freeUsesUsed  Int       @default(0)
  lastUsedAt    DateTime?
  ipHash        String?
  userAgentHash String?
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt

  @@unique([anonymousId, weekStartDate])
  @@index([weekStartDate])
  @@index([ipHash, weekStartDate])
}

model GuestToolRun {
  id              String   @id @default(uuid())
  anonymousId     String
  toolType        String
  weekStartDate   DateTime
  consumedFreeUse Boolean  @default(false)
  createdAt       DateTime @default(now())

  @@index([anonymousId, weekStartDate])
  @@index([createdAt])
}

model CVProfile {
  id                      String   @id @default(uuid())
  userId                  String   @unique
  title                   String?
  fullName                String?
  email                   String?
  phone                   String?
  province                String?
  city                    String?
  professionalSummary     String?  @db.Text
  careerObjective         String?  @db.Text
  personalDetailsJson     Json     @default("{}")
  professionalLinksJson   Json     @default("{}")
  skillsJson              Json     @default("[]")
  softSkillsJson          Json     @default("[]")
  languagesJson           Json     @default("[]")
  educationJson           Json     @default("[]")
  qualificationsJson      Json     @default("[]")
  certificationsJson      Json     @default("[]")
  licencesJson            Json     @default("[]")
  workExperienceJson      Json     @default("[]")
  volunteerExperienceJson Json     @default("[]")
  projectsJson            Json     @default("[]")
  achievementsJson        Json     @default("[]")
  awardsJson              Json     @default("[]")
  membershipsJson         Json     @default("[]")
  publicationsJson        Json     @default("[]")
  hobbiesJson             Json     @default("[]")
  customSectionsJson      Json     @default("[]")
  referencesJson          Json     @default("[]")
  preferredTemplate       String   @default("simple-professional")
  createdAt               DateTime @default(now())
  updatedAt               DateTime @updatedAt
  user                    User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  // TODO: Add privacy controls/encryption for sensitive CV content before production launch.
}

model CVGenerated {
  id                  String   @id @default(uuid())
  userId              String
  jobId               String?
  templateKey         String
  title               String
  contentSnapshotJson Json
  jobSnapshotJson     Json?
  matchedToJob        Boolean  @default(false)
  aiOptimized         Boolean  @default(false)
  outputPdfUrl        String?
  outputDocxUrl       String?
  createdAt           DateTime @default(now())
  user                User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  job                 JobPost? @relation(fields: [jobId], references: [id], onDelete: SetNull)
}

model CVGenerationCounter {
  id                  String   @id @default(uuid())
  userId              String
  weekStartDate       DateTime
  freeGenerationsUsed Int      @default(0)
  createdAt           DateTime @default(now())
  updatedAt           DateTime @updatedAt
  user                User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, weekStartDate])
}

model CVRewriteUsage {
  id            String   @id @default(uuid())
  userId        String
  weekStartDate DateTime
  freeUsesUsed  Int      @default(0)
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt
  user          User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, weekStartDate])
}

model CVRewriteRequest {
  id                 String          @id @default(uuid())
  userId             String
  jobId              String?
  type               String
  inputSnapshotJson  Json
  outputSnapshotJson Json?
  tokenCost          Int             @default(1)
  consumedFreeUse    Boolean         @default(false)
  consumedTokens     Int             @default(0)
  status             AiRequestStatus @default(pending)
  createdAt          DateTime        @default(now())
  updatedAt          DateTime        @updatedAt
  user               User            @relation(fields: [userId], references: [id], onDelete: Cascade)
  job                JobPost?        @relation(fields: [jobId], references: [id], onDelete: SetNull)
}

model Application {
  id               String            @id @default(uuid())
  candidateId      String
  jobId            String
  status           ApplicationStatus @default(draft)
  generatedPackUrl String?
  notes            String?
  createdAt        DateTime          @default(now())
  updatedAt        DateTime          @updatedAt
  candidate        User              @relation(fields: [candidateId], references: [id], onDelete: Cascade)
  job              JobPost           @relation(fields: [jobId], references: [id], onDelete: Cascade)

  @@unique([candidateId, jobId])
}

model CandidateDocument {
  id        String       @id @default(uuid())
  userId    String
  type      DocumentType
  title     String
  fileName  String
  fileUrl   String
  mimeType  String?
  sizeBytes Int?
  createdAt DateTime     @default(now())
  updatedAt DateTime     @updatedAt
  user      User         @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model ApplicationPackUsageCounter {
  id             String   @id @default(uuid())
  userId         String
  weekStartDate  DateTime
  freeMergesUsed Int      @default(0)
  createdAt      DateTime @default(now())
  updatedAt      DateTime @updatedAt
  user           User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, weekStartDate])
}

model UserWallet {
  id           String              @id @default(uuid())
  userId       String              @unique
  tokenBalance Int                 @default(0)
  createdAt    DateTime            @default(now())
  updatedAt    DateTime            @updatedAt
  user         User                @relation(fields: [userId], references: [id], onDelete: Cascade)
  transactions WalletTransaction[]
}

model WalletTransaction {
  id          String                  @id @default(uuid())
  walletId    String
  userId      String
  type        WalletTransactionType
  status      WalletTransactionStatus @default(successful)
  tokens      Int
  amountCents Int?
  reference   String?
  description String?
  createdAt   DateTime                @default(now())
  wallet      UserWallet              @relation(fields: [walletId], references: [id], onDelete: Cascade)
}

model AiCvRequest {
  id           String          @id @default(uuid())
  userId       String
  jobId        String?
  inputText    String?
  outputText   String?
  tokenCost    Int             @default(1)
  status       AiRequestStatus @default(pending)
  errorMessage String?
  createdAt    DateTime        @default(now())
  updatedAt    DateTime        @updatedAt
}

model TrustedContact {
  id           String   @id @default(uuid())
  candidateId  String
  name         String
  email        String?
  phone        String?
  relationship String?
  accepted     Boolean  @default(false)
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt
}

model InterviewSafetyEvent {
  id                      String                @id @default(uuid())
  candidateId             String
  trustedContactId        String?
  jobId                   String?
  title                   String
  companyName             String?
  locationLabel           String?
  latitude                Float?
  longitude               Float?
  geofenceRadiusM         Int                   @default(500)
  expectedDurationMinutes Int                   @default(120)
  status                  InterviewSafetyStatus @default(scheduled)
  lastKnownLatitude       Float?
  lastKnownLongitude      Float?
  checkinDeadline         DateTime?
  emergencyLevel          EmergencyLevel?
  createdAt               DateTime              @default(now())
  updatedAt               DateTime              @updatedAt
}

model DeviceToken {
  id          String    @id @default(uuid())
  userId      String
  token       String    @unique
  platform    String?
  deviceLabel String?
  appVersion  String?
  lastSeenAt  DateTime?
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
  user        User      @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model NotificationPreference {
  id                     String    @id @default(uuid())
  userId                 String    @unique
  emailEnabled           Boolean   @default(true)
  pushEnabled            Boolean   @default(true)
  inAppEnabled           Boolean   @default(true)
  matchAlertsEnabled     Boolean   @default(true)
  closingReminderEnabled Boolean   @default(true)
  alertFrequency         String    @default("six_hourly")
  unsubscribedAt         DateTime?
  createdAt              DateTime  @default(now())
  updatedAt              DateTime  @updatedAt
  user                   User      @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model JobReport {
  id         String       @id @default(uuid())
  jobId      String
  reporterId String?
  reason     String
  details    String?
  status     ReportStatus @default(open)
  createdAt  DateTime     @default(now())
  updatedAt  DateTime     @updatedAt
  job        JobPost      @relation(fields: [jobId], references: [id], onDelete: Cascade)
}

model SavedJob {
  userId    String
  jobId     String
  createdAt DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  job       JobPost  @relation(fields: [jobId], references: [id], onDelete: Cascade)

  @@id([userId, jobId])
}

model JobAlert {
  id             String         @id @default(uuid())
  userId         String
  jobId          String?
  keyword        String?
  province       String?
  city           String?
  category       String?
  educationLevel String?
  jobType        String?
  frequency      AlertFrequency @default(daily)
  active         Boolean        @default(true)
  createdAt      DateTime       @default(now())
  updatedAt      DateTime       @updatedAt
  user           User           @relation(fields: [userId], references: [id], onDelete: Cascade)
  job            JobPost?       @relation(fields: [jobId], references: [id], onDelete: SetNull)
}

model JobView {
  id          String   @id @default(uuid())
  jobId       String
  userId      String?
  anonymousId String?
  source      String?
  createdAt   DateTime @default(now())
  job         JobPost  @relation(fields: [jobId], references: [id], onDelete: Cascade)
  user        User?    @relation(fields: [userId], references: [id], onDelete: SetNull)
}

model AdCampaign {
  id             String         @id @default(uuid())
  advertiserName String
  title          String
  imageUrl       String?
  targetUrl      String?
  placement      AdPlacement
  province       String?
  category       String?
  status         AdStatus       @default(draft)
  startDate      DateTime?
  endDate        DateTime?
  impressions    Int            @default(0)
  clicks         Int            @default(0)
  createdAt      DateTime       @default(now())
  updatedAt      DateTime       @updatedAt
  impressionLogs AdImpression[]
  clickLogs      AdClick[]
}

model AdImpression {
  id          String      @id @default(uuid())
  campaignId  String
  userId      String?
  anonymousId String?
  placement   AdPlacement
  createdAt   DateTime    @default(now())
  campaign    AdCampaign  @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  user        User?       @relation(fields: [userId], references: [id], onDelete: SetNull)
}

model AdClick {
  id          String      @id @default(uuid())
  campaignId  String
  userId      String?
  anonymousId String?
  placement   AdPlacement
  createdAt   DateTime    @default(now())
  campaign    AdCampaign  @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  user        User?       @relation(fields: [userId], references: [id], onDelete: SetNull)
}

model SponsoredJob {
  id          String             @id @default(uuid())
  jobId       String
  placement   String
  startDate   DateTime?
  endDate     DateTime?
  status      SponsoredJobStatus @default(active)
  impressions Int                @default(0)
  clicks      Int                @default(0)
  createdAt   DateTime           @default(now())
  updatedAt   DateTime           @updatedAt
  job         JobPost            @relation(fields: [jobId], references: [id], onDelete: Cascade)
}

model NotificationLog {
  id        String   @id @default(uuid())
  userId    String
  type      String
  title     String
  message   String
  channel   String
  status    String
  createdAt DateTime @default(now())
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model FileAsset {
  id                String         @id @default(uuid())
  ownerUserId       String?
  uploadedByUserId  String?
  relatedEntityType String?
  relatedEntityId   String?
  fileType          FileAssetType  @default(other)
  title             String
  fileName          String
  fileUrl           String
  mimeType          String?
  sizeBytes         Int?
  visibility        FileVisibility @default(owner_only)
  deletedAt         DateTime?
  createdAt         DateTime       @default(now())
  updatedAt         DateTime       @updatedAt
  owner             User?          @relation("FileOwner", fields: [ownerUserId], references: [id], onDelete: SetNull)
  uploadedBy        User?          @relation("FileUploader", fields: [uploadedByUserId], references: [id], onDelete: SetNull)
}

model NotificationTemplate {
  id          String                     @id @default(uuid())
  name        String
  channel     NotificationChannel
  subject     String?
  body        String
  status      NotificationTemplateStatus @default(active)
  createdById String?
  createdAt   DateTime                   @default(now())
  updatedAt   DateTime                   @updatedAt
  createdBy   User?                      @relation(fields: [createdById], references: [id], onDelete: SetNull)
  campaigns   NotificationCampaign[]
}

model NotificationCampaign {
  id           String                     @id @default(uuid())
  title        String
  channel      NotificationChannel
  audienceType NotificationAudienceType
  status       NotificationCampaignStatus @default(draft)
  templateId   String?
  messageTitle String
  messageBody  String
  filtersJson  Json                       @default("{}")
  scheduledAt  DateTime?
  sentAt       DateTime?
  createdById  String?
  createdAt    DateTime                   @default(now())
  updatedAt    DateTime                   @updatedAt
  template     NotificationTemplate?      @relation(fields: [templateId], references: [id], onDelete: SetNull)
  createdBy    User?                      @relation(fields: [createdById], references: [id], onDelete: SetNull)
  deliveries   NotificationDelivery[]
}

model NotificationDelivery {
  id                String                     @id @default(uuid())
  campaignId        String?
  userId            String
  channel           NotificationChannel
  status            NotificationDeliveryStatus @default(pending)
  title             String
  body              String
  providerMessageId String?
  errorMessage      String?
  metadataJson      Json                       @default("{}")
  sentAt            DateTime?
  readAt            DateTime?
  createdAt         DateTime                   @default(now())
  campaign          NotificationCampaign?      @relation(fields: [campaignId], references: [id], onDelete: SetNull)
  user              User                       @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model CandidateMatchDigest {
  id                String   @id @default(uuid())
  userId            String
  cycleKey          String
  matchedJobIdsJson Json     @default("[]")
  matchedCount      Int
  topJobTitle       String?
  emailSent         Boolean  @default(false)
  pushSent          Boolean  @default(false)
  inAppSent         Boolean  @default(false)
  createdAt         DateTime @default(now())
  user              User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([userId, cycleKey])
}

model EmailSuppression {
  id        String   @id @default(uuid())
  email     String
  reason    String
  createdAt DateTime @default(now())

  @@unique([email, reason])
}

model EmailLog {
  id                String    @id @default(uuid())
  userId            String?
  toEmail           String
  subject           String
  status            String
  providerMessageId String?
  errorMessage      String?
  sentAt            DateTime?
  createdAt         DateTime  @default(now())
  user              User?     @relation(fields: [userId], references: [id], onDelete: SetNull)
}

model AnalyticsSession {
  id           String              @id @default(uuid())
  anonymousId  String
  userId       String?
  clientType   String              @default("web")
  appVersion   String?
  currentPath  String?
  currentTitle String?
  firstSeenAt  DateTime            @default(now())
  lastSeenAt   DateTime            @default(now())
  ipHash       String?
  userAgent    String?
  deviceType   String?
  browser      String?
  os           String?
  country      String?
  province     String?
  referrer     String?
  createdAt    DateTime            @default(now())
  updatedAt    DateTime            @updatedAt
  user         User?               @relation(fields: [userId], references: [id], onDelete: SetNull)
  pageViews    AnalyticsPageView[]
  events       AnalyticsEvent[]

  @@unique([anonymousId])
  @@index([clientType, lastSeenAt])
}

model AnalyticsPageView {
  id         String           @id @default(uuid())
  sessionId  String
  userId     String?
  path       String
  title      String?
  referrer   String?
  source     String?
  clientType String           @default("web")
  appVersion String?
  createdAt  DateTime         @default(now())
  session    AnalyticsSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
  user       User?            @relation(fields: [userId], references: [id], onDelete: SetNull)

  @@index([clientType, createdAt])
}

model AnalyticsEvent {
  id           String           @id @default(uuid())
  sessionId    String
  userId       String?
  eventName    String
  entityType   String?
  entityId     String?
  metadataJson Json             @default("{}")
  createdAt    DateTime         @default(now())
  session      AnalyticsSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
  user         User?            @relation(fields: [userId], references: [id], onDelete: SetNull)
}

model AnalyticsDailySummary {
  id               String   @id @default(uuid())
  date             DateTime @unique
  pageViews        Int      @default(0)
  uniqueVisitors   Int      @default(0)
  jobViews         Int      @default(0)
  signups          Int      @default(0)
  logins           Int      @default(0)
  generatedCvs     Int      @default(0)
  generatedZ83s    Int      @default(0)
  applicationPacks Int      @default(0)
  createdAt        DateTime @default(now())
  updatedAt        DateTime @updatedAt
}

model AdminSetting {
  id          String   @id @default(uuid())
  key         String   @unique
  valueJson   Json
  updatedById String?
  updatedAt   DateTime @updatedAt
  updatedBy   User?    @relation(fields: [updatedById], references: [id], onDelete: SetNull)
}

model AuditLog {
  id         String   @id @default(uuid())
  userId     String?
  action     String
  entityType String?
  entityId   String?
  metadata   Json?
  createdAt  DateTime @default(now())
  user       User?    @relation(fields: [userId], references: [id])
}
