// StaffGuide - data model
// Datasource is SQLite by default. To move to Postgres, change provider to
// "postgresql" and set DATABASE_URL accordingly, then run a migration.

generator client {
  provider = "prisma-client-js"
}

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

// NOTE: SQLite does not support native Prisma enums, so role/status/type are
// stored as strings. Allowed values are enforced in the app via src/lib/enums.ts.
// (Switching the datasource to postgresql later lets you promote these to enums.)

model User {
  id           String   @id @default(cuid())
  email        String   @unique
  name         String?
  passwordHash String
  role         String   @default("VIEWER")
  // Staff title shown in the UI (e.g. "Owner", "Head of Department - Moderation").
  title        String?
  // Custom uploaded avatar; falls back to the default image when null.
  avatarUrl    String?
  // Glow colour around the avatar: gold | pink | green | purple | red | blue.
  avatarGlow   String?
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  documents       Document[] @relation("AuthoredDocuments")
  editedDocuments Document[] @relation("EditedDocuments")
  versions        Version[]
  uploads         MediaAsset[]
  activity        ActivityLog[]
}

// A Section is a node in the navigation tree. It can be a folder (container)
// and/or hold documents. Self-referential parent enables nested pages/folders.
model Section {
  id        String    @id @default(cuid())
  title     String
  emoji     String?
  // When set, pages in this section are locked behind this password on the public
  // site (scrypt hash; staff always bypass).
  passwordHash String?
  // Minimum role required to view this section (null = public). One of
  // VIEWER/EDITOR/ADMIN/OWNER. Enforced in nav + doc page alongside the password.
  minRole   String?
  slug      String
  parentId  String?
  parent    Section?  @relation("SectionTree", fields: [parentId], references: [id], onDelete: Cascade)
  children  Section[] @relation("SectionTree")
  order     Int       @default(0)
  documents Document[]
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt

  @@unique([parentId, slug])
  @@index([parentId])
}

model Document {
  id          String    @id @default(cuid())
  title       String
  slug        String
  // Full path slug (e.g. "guides/getting-started") used for shareable URLs.
  path        String    @unique
  content     String    @default("")
  excerpt     String?
  status      String    @default("DRAFT")
  order       Int       @default(0)

  // Optional short label shown in the sidebar/pager instead of the full title.
  sidebarTitle String?

  // Notion-style full-width cover image at the top of the page, with an optional
  // CSS object-position (e.g. "50% 30%") for repositioning the crop.
  coverUrl    String?
  coverPosition String?

  // Self-referential parent for sub-pages (a page nested under another page).
  // e.g. "Common Bugs" -> "Bug 1". Display/ordering only; the URL stays flat.
  parentId    String?
  parent      Document?  @relation("DocTree", fields: [parentId], references: [id], onDelete: SetNull)
  children    Document[] @relation("DocTree")

  // Draft workflow: the primary fields above are the LIVE (published) version.
  // Unpublished edits are stashed here until "Save & publish" applies them.
  hasDraft      Boolean @default(false)
  draftTitle    String?
  draftContent  String?
  draftExcerpt  String?
  draftCoverUrl String?

  // SEO metadata
  metaTitle       String?
  metaDescription String?
  ogImageId       String?

  sectionId String?
  section   Section? @relation(fields: [sectionId], references: [id], onDelete: SetNull)

  authorId String
  author   User   @relation("AuthoredDocuments", fields: [authorId], references: [id])

  // Who last edited/published this page (shown in the byline).
  lastEditedById String?
  lastEditedBy   User?   @relation("EditedDocuments", fields: [lastEditedById], references: [id])

  versions Version[]
  media    DocumentMedia[]

  // Scheduled publishing: when set and in the future, the doc auto-publishes on
  // the first request after this time (lazy promotion - no cron needed).
  publishAt   DateTime?

  // Review reminders: flag a page as stale N days after it was last reviewed.
  reviewIntervalDays Int?
  lastReviewedAt     DateTime?

  // Shareable read-only preview link for drafts (random 16-byte token; null = no
  // link). Indexed (not unique) so applying the schema never triggers a data-loss
  // warning on the unique-constraint add; collisions are astronomically unlikely.
  previewToken String?

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

  @@index([sectionId])
  @@index([status])
  @@index([previewToken])
  @@index([parentId])
}

// Immutable snapshot of a document's content for version history / restore.
model Version {
  id         String   @id @default(cuid())
  documentId String
  document   Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
  title      String
  content    String
  editorId   String
  editor     User     @relation(fields: [editorId], references: [id])
  note       String?
  createdAt  DateTime @default(now())

  @@index([documentId])
}

model MediaAsset {
  id          String    @id @default(cuid())
  filename    String
  storageKey  String    @unique // path/key within storage driver
  url         String            // resolved public or signed URL base
  mimeType    String
  type        String            // one of MediaType in src/lib/enums.ts
  sizeBytes   Int
  width       Int?
  height      Int?
  altText     String?
  caption     String?
  isPrivate   Boolean   @default(false)
  uploadedById String
  uploadedBy  User      @relation(fields: [uploadedById], references: [id])
  documents   DocumentMedia[]
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt

  @@index([type])
}

// Join table so an asset can be reused across multiple documents.
model DocumentMedia {
  documentId String
  mediaId    String
  document   Document   @relation(fields: [documentId], references: [id], onDelete: Cascade)
  media      MediaAsset @relation(fields: [mediaId], references: [id], onDelete: Cascade)

  @@id([documentId, mediaId])
}

// Single-row site configuration (branding + SEO defaults).
model Settings {
  id            Int     @id @default(1)
  siteName      String  @default("StaffGuide")
  tagline       String?
  logoUrl       String?
  faviconUrl    String?
  // Optional banner image shown on the public home page, with a CSS
  // object-position (e.g. "50% 30%") so it can be repositioned in the frame.
  heroImageUrl  String?
  heroImagePosition String?
  brandColor    String  @default("#2563eb")
  brandFgColor  String  @default("#ffffff")
  defaultMetaDescription String?

  // Maintenance mode: when on, the public site is locked behind a password
  // unless the visitor is signed in as staff.
  maintenanceMode     Boolean @default(false)
  maintenancePassword String  @default("Lizard1")

  updatedAt     DateTime @updatedAt
}

// Audit trail of who did what (document/section/user changes).
model ActivityLog {
  id         String   @id @default(cuid())
  userId     String?
  user       User?    @relation(fields: [userId], references: [id], onDelete: SetNull)
  action     String   // created | updated | published | unpublished | deleted | restored | reviewed
  entityType String   // document | section | user
  entityId   String?
  summary    String
  createdAt  DateTime @default(now())

  @@index([createdAt])
}

// Log of what staff/visitors searched for, to reveal gaps (zero-result queries).
model SearchQuery {
  id        String   @id @default(cuid())
  term      String
  results   Int
  userId    String?
  createdAt DateTime @default(now())

  @@index([createdAt])
}
