// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

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

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

// generator dbml {
//  provider = "prisma-dbml-generator"
// }

generator zod {
  provider      = "zod-prisma"
  output        = "./zod"
  imports       = "./zod-utils"
  relationModel = "default"
}

generator json {
  provider = "prisma-json-types-generator"
}

enum PipelineTriggers {
  responseCreated
  responseUpdated
  responseFinished
}

enum WebhookSource {
  user
  zapier
  make
  n8n
}

model Webhook {
  id            String             @id @default(cuid())
  name          String?
  createdAt     DateTime           @default(now()) @map(name: "created_at")
  updatedAt     DateTime           @updatedAt @map(name: "updated_at")
  url           String
  source        WebhookSource      @default(user)
  environment   Environment        @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  environmentId String
  triggers      PipelineTriggers[]
  surveyIds     String[]

  @@index([environmentId])
}

model Attribute {
  id               String         @id @default(cuid())
  createdAt        DateTime       @default(now()) @map(name: "created_at")
  updatedAt        DateTime       @updatedAt @map(name: "updated_at")
  attributeClass   AttributeClass @relation(fields: [attributeClassId], references: [id], onDelete: Cascade)
  attributeClassId String
  person           Person         @relation(fields: [personId], references: [id], onDelete: Cascade)
  personId         String
  value            String

  @@unique([personId, attributeClassId])
}

enum AttributeType {
  code
  noCode
  automatic
}

model AttributeClass {
  id               String                  @id @default(cuid())
  createdAt        DateTime                @default(now()) @map(name: "created_at")
  updatedAt        DateTime                @updatedAt @map(name: "updated_at")
  name             String
  description      String?
  archived         Boolean                 @default(false)
  type             AttributeType
  environment      Environment             @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  environmentId    String
  attributes       Attribute[]
  attributeFilters SurveyAttributeFilter[]

  @@unique([name, environmentId])
  @@index([environmentId, createdAt])
  @@index([environmentId, archived])
}

model Person {
  id            String      @id @default(cuid())
  userId        String
  createdAt     DateTime    @default(now()) @map(name: "created_at")
  updatedAt     DateTime    @updatedAt @map(name: "updated_at")
  environment   Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  environmentId String
  responses     Response[]
  attributes    Attribute[]
  displays      Display[]
  actions       Action[]

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

model Response {
  id               String            @id @default(cuid())
  createdAt        DateTime          @default(now()) @map(name: "created_at")
  updatedAt        DateTime          @updatedAt @map(name: "updated_at")
  finished         Boolean           @default(false)
  survey           Survey            @relation(fields: [surveyId], references: [id], onDelete: Cascade)
  surveyId         String
  person           Person?           @relation(fields: [personId], references: [id], onDelete: Cascade)
  personId         String?
  notes            ResponseNote[]
  /// @zod.custom(imports.ZResponseData)
  /// [ResponseData]
  data             Json              @default("{}")
  /// @zod.custom(imports.ZResponseTtc)
  /// [ResponseTtc]
  ttc              Json              @default("{}")
  /// @zod.custom(imports.ZResponseMeta)
  /// [ResponseMeta]
  meta             Json              @default("{}")
  tags             TagsOnResponses[]
  /// @zod.custom(imports.ZResponsePersonAttributes)
  /// [ResponsePersonAttributes]
  personAttributes Json?
  // singleUseId, used to prevent multiple responses
  singleUseId      String?
  language         String?

  @@unique([surveyId, singleUseId])
  @@index([surveyId, createdAt]) // to determine monthly response count
  @@index([personId, createdAt]) // to determine monthly identified users (persons)
  @@index([surveyId])
}

model ResponseNote {
  id         String   @id @default(cuid())
  createdAt  DateTime @default(now()) @map(name: "created_at")
  updatedAt  DateTime @updatedAt @map(name: "updated_at")
  response   Response @relation(fields: [responseId], references: [id], onDelete: Cascade)
  responseId String
  user       User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId     String
  text       String
  isResolved Boolean  @default(false)
  isEdited   Boolean  @default(false)

  @@index([responseId])
}

model Tag {
  id            String            @id @default(cuid())
  createdAt     DateTime          @default(now()) @map(name: "created_at")
  updatedAt     DateTime          @updatedAt @map(name: "updated_at")
  name          String
  responses     TagsOnResponses[]
  environmentId String
  environment   Environment       @relation(fields: [environmentId], references: [id], onDelete: Cascade)

  @@unique([environmentId, name])
  @@index([environmentId])
}

model TagsOnResponses {
  responseId String
  response   Response @relation(fields: [responseId], references: [id], onDelete: Cascade)
  tagId      String
  tag        Tag      @relation(fields: [tagId], references: [id], onDelete: Cascade)

  @@id([responseId, tagId])
  @@index([responseId])
}

enum SurveyStatus {
  draft
  scheduled
  inProgress
  paused
  completed
}

enum DisplayStatus {
  seen
  responded
}

model Display {
  id         String         @id @default(cuid())
  createdAt  DateTime       @default(now()) @map(name: "created_at")
  updatedAt  DateTime       @updatedAt @map(name: "updated_at")
  survey     Survey         @relation(fields: [surveyId], references: [id], onDelete: Cascade)
  surveyId   String
  person     Person?        @relation(fields: [personId], references: [id], onDelete: Cascade)
  personId   String?
  responseId String?        @unique
  status     DisplayStatus?

  @@index([surveyId])
  @@index([personId, createdAt])
}

model SurveyTrigger {
  id            String      @id @default(cuid())
  createdAt     DateTime    @default(now()) @map(name: "created_at")
  updatedAt     DateTime    @updatedAt @map(name: "updated_at")
  survey        Survey      @relation(fields: [surveyId], references: [id], onDelete: Cascade)
  surveyId      String
  actionClass   ActionClass @relation(fields: [actionClassId], references: [id], onDelete: Cascade)
  actionClassId String

  @@unique([surveyId, actionClassId])
  @@index([surveyId])
}

enum SurveyAttributeFilterCondition {
  equals
  notEquals
}

model SurveyAttributeFilter {
  id               String                         @id @default(cuid())
  createdAt        DateTime                       @default(now()) @map(name: "created_at")
  updatedAt        DateTime                       @updatedAt @map(name: "updated_at")
  attributeClass   AttributeClass                 @relation(fields: [attributeClassId], references: [id], onDelete: Cascade)
  attributeClassId String
  survey           Survey                         @relation(fields: [surveyId], references: [id], onDelete: Cascade)
  surveyId         String
  condition        SurveyAttributeFilterCondition
  value            String

  @@unique([surveyId, attributeClassId])
  @@index([surveyId])
  @@index([attributeClassId])
}

enum SurveyType {
  link
  web
  website
  app
}

enum displayOptions {
  displayOnce
  displayMultiple
  displaySome
  respondMultiple
}

model Survey {
  id                  String                  @id @default(cuid())
  createdAt           DateTime                @default(now()) @map(name: "created_at")
  updatedAt           DateTime                @updatedAt @map(name: "updated_at")
  name                String
  redirectUrl         String?
  type                SurveyType              @default(web)
  environment         Environment             @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  environmentId       String
  creator             User?                   @relation(fields: [createdBy], references: [id])
  createdBy           String?
  status              SurveyStatus            @default(draft)
  /// @zod.custom(imports.ZSurveyWelcomeCard)
  /// [SurveyWelcomeCard]
  welcomeCard         Json                    @default("{\"enabled\": false}")
  /// @zod.custom(imports.ZSurveyQuestions)
  /// [SurveyQuestions]
  questions           Json                    @default("[]")
  endings             Json[]                  @default([])
  thankYouCard        Json? //deprecated            
  /// @zod.custom(imports.ZSurveyHiddenFields)
  /// [SurveyHiddenFields]
  hiddenFields        Json                    @default("{\"enabled\": false}")
  responses           Response[]
  displayOption       displayOptions          @default(displayOnce)
  recontactDays       Int?
  displayLimit        Int?
  triggers            SurveyTrigger[]
  /// @zod.custom(imports.ZSurveyInlineTriggers)
  /// [SurveyInlineTriggers]
  inlineTriggers      Json?
  attributeFilters    SurveyAttributeFilter[]
  displays            Display[]
  autoClose           Int?
  autoComplete        Int?
  delay               Int                     @default(0)
  runOnDate           DateTime?
  closeOnDate         DateTime?
  /// @zod.custom(imports.ZSurveyClosedMessage)
  /// [SurveyClosedMessage]
  surveyClosedMessage Json?
  segmentId           String?
  segment             Segment?                @relation(fields: [segmentId], references: [id])

  /// @zod.custom(imports.ZSurveyProductOverwrites)
  /// [SurveyProductOverwrites]
  productOverwrites Json?

  /// @zod.custom(imports.ZSurveyStyling)
  /// [SurveyStyling]
  styling Json?

  /// @zod.custom(imports.ZSurveySingleUse)
  /// [SurveySingleUse]
  singleUse Json? @default("{\"enabled\": false, \"isEncrypted\": true}")

  /// @zod.custom(imports.ZSurveyVerifyEmail)
  /// [SurveyVerifyEmail]
  verifyEmail          Json? // deprecated
  isVerifyEmailEnabled Boolean          @default(false)
  pin                  String?
  resultShareKey       String?          @unique
  displayPercentage    Decimal?
  languages            SurveyLanguage[]
  showLanguageSwitch   Boolean?

  @@index([environmentId, updatedAt])
  @@index([segmentId])
}

enum ActionType {
  code
  noCode
  automatic
}

model ActionClass {
  id            String          @id @default(cuid())
  createdAt     DateTime        @default(now()) @map(name: "created_at")
  updatedAt     DateTime        @updatedAt @map(name: "updated_at")
  name          String
  description   String?
  type          ActionType
  key           String?
  /// @zod.custom(imports.ZActionClassNoCodeConfig)
  /// [ActionClassNoCodeConfig]
  noCodeConfig  Json?
  environment   Environment     @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  environmentId String
  surveys       SurveyTrigger[]
  actions       Action[]

  @@unique([key, environmentId])
  @@unique([name, environmentId])
  @@index([environmentId, createdAt])
}

model Action {
  id            String      @id @default(cuid())
  createdAt     DateTime    @default(now()) @map(name: "created_at")
  actionClass   ActionClass @relation(fields: [actionClassId], references: [id], onDelete: Cascade)
  actionClassId String
  person        Person      @relation(fields: [personId], references: [id], onDelete: Cascade)
  personId      String
  /// @zod.custom(imports.ZActionProperties)
  /// @zod.custom(imports.ZActionProperties)
  /// [ActionProperties]
  properties    Json        @default("{}")

  @@index([personId, actionClassId, createdAt])
  @@index([actionClassId, createdAt])
  @@index([personId, createdAt])
}

enum EnvironmentType {
  production
  development
}

enum IntegrationType {
  googleSheets
  notion
  airtable
  slack
}

model Integration {
  id            String          @id @default(cuid())
  type          IntegrationType
  environmentId String
  /// @zod.custom(imports.ZIntegrationConfig)
  /// [IntegrationConfig]
  config        Json
  environment   Environment     @relation(fields: [environmentId], references: [id], onDelete: Cascade)

  @@unique([type, environmentId])
  @@index([environmentId])
}

model Environment {
  id                    String           @id @default(cuid())
  createdAt             DateTime         @default(now()) @map(name: "created_at")
  updatedAt             DateTime         @updatedAt @map(name: "updated_at")
  type                  EnvironmentType
  product               Product          @relation(fields: [productId], references: [id], onDelete: Cascade)
  productId             String
  widgetSetupCompleted  Boolean          @default(false)
  appSetupCompleted     Boolean          @default(false)
  websiteSetupCompleted Boolean          @default(false)
  surveys               Survey[]
  people                Person[]
  actionClasses         ActionClass[]
  attributeClasses      AttributeClass[]
  apiKeys               ApiKey[]
  webhooks              Webhook[]
  tags                  Tag[]
  segments              Segment[]
  integration           Integration[]

  @@index([productId])
}

enum WidgetPlacement {
  bottomLeft
  bottomRight
  topLeft
  topRight
  center
}

model Product {
  id                   String          @id @default(cuid())
  createdAt            DateTime        @default(now()) @map(name: "created_at")
  updatedAt            DateTime        @updatedAt @map(name: "updated_at")
  name                 String
  organization         Organization    @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  organizationId       String
  environments         Environment[]
  brandColor           String? // deprecated; use styling.brandColor instead
  highlightBorderColor String? // deprecated
  /// @zod.custom(imports.ZProductStyling)
  /// [Styling]
  styling              Json            @default("{\"allowStyleOverwrite\":true}")
  /// @zod.custom(imports.ZProductConfig)
  /// [ProductConfig]
  config               Json            @default("{}")
  recontactDays        Int             @default(7)
  linkSurveyBranding   Boolean         @default(true) // Determines if the survey branding should be displayed in link surveys
  inAppSurveyBranding  Boolean         @default(true) // Determines if the survey branding should be displayed in in-app surveys
  placement            WidgetPlacement @default(bottomRight)
  clickOutsideClose    Boolean         @default(true)
  darkOverlay          Boolean         @default(false)
  languages            Language[]
  /// @zod.custom(imports.ZLogo)
  /// [Logo]
  logo                 Json?

  @@unique([organizationId, name])
  @@index([organizationId])
}

model Organization {
  id          String       @id @default(cuid())
  createdAt   DateTime     @default(now()) @map(name: "created_at")
  updatedAt   DateTime     @updatedAt @map(name: "updated_at")
  name        String
  memberships Membership[]
  products    Product[]
  /// @zod.custom(imports.ZTeamBilling)
  /// [TeamBilling]
  billing     Json
  invites     Invite[]
}

enum MembershipRole {
  owner
  admin
  editor
  developer
  viewer
}

model Membership {
  organization   Organization   @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  organizationId String
  user           User           @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId         String
  accepted       Boolean        @default(false)
  role           MembershipRole

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

model Invite {
  id             String         @id @default(uuid())
  email          String
  name           String?
  organization   Organization   @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  organizationId String
  creator        User           @relation("inviteCreatedBy", fields: [creatorId], references: [id])
  creatorId      String
  acceptor       User?          @relation("inviteAcceptedBy", fields: [acceptorId], references: [id], onDelete: Cascade)
  acceptorId     String?
  accepted       Boolean        @default(false)
  createdAt      DateTime       @default(now())
  expiresAt      DateTime
  role           MembershipRole @default(admin)

  @@index([email, organizationId])
  @@index([organizationId])
}

model ApiKey {
  id            String      @id @unique @default(cuid())
  createdAt     DateTime    @default(now())
  lastUsedAt    DateTime?
  label         String?
  hashedKey     String      @unique()
  environment   Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  environmentId String

  @@index([environmentId])
}

enum IdentityProvider {
  email
  github
  google
  azuread
  openid
}

model Account {
  id                String   @id @default(cuid())
  createdAt         DateTime @default(now()) @map(name: "created_at")
  updatedAt         DateTime @updatedAt @map(name: "updated_at")
  user              User?    @relation(fields: [userId], references: [id], onDelete: Cascade)
  userId            String
  type              String
  provider          String
  providerAccountId String
  access_token      String?  @db.Text
  refresh_token     String?  @db.Text
  expires_at        Int?
  ext_expires_in    Int?
  token_type        String?
  scope             String?
  id_token          String?  @db.Text
  session_state     String?

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

enum Role {
  project_manager
  engineer
  founder
  marketing_specialist
  other
}

enum Objective {
  increase_conversion
  improve_user_retention
  increase_user_adoption
  sharpen_marketing_messaging
  support_sales
  other
}

enum Intention {
  survey_user_segments
  survey_at_specific_point_in_user_journey
  enrich_customer_profiles
  collect_all_user_feedback_on_one_platform
  other
}

model User {
  id                        String           @id @default(cuid())
  createdAt                 DateTime         @default(now()) @map(name: "created_at")
  updatedAt                 DateTime         @updatedAt @map(name: "updated_at")
  name                      String
  email                     String           @unique
  emailVerified             DateTime?        @map(name: "email_verified")
  imageUrl                  String?
  twoFactorSecret           String?
  twoFactorEnabled          Boolean          @default(false)
  backupCodes               String?
  password                  String?
  identityProvider          IdentityProvider @default(email)
  identityProviderAccountId String?
  memberships               Membership[]
  accounts                  Account[]
  responseNotes             ResponseNote[]
  groupId                   String?
  invitesCreated            Invite[]         @relation("inviteCreatedBy")
  invitesAccepted           Invite[]         @relation("inviteAcceptedBy")
  role                      Role?
  objective                 Objective?
  /// @zod.custom(imports.ZUserNotificationSettings)
  /// @zod.custom(imports.ZUserNotificationSettings)
  /// [UserNotificationSettings]
  notificationSettings      Json             @default("{}")
  surveys                   Survey[]

  @@index([email])
}

model ShortUrl {
  id        String   @id // generate nanoId in service
  createdAt DateTime @default(now()) @map(name: "created_at")
  updatedAt DateTime @updatedAt @map(name: "updated_at")
  url       String   @unique
}

model Segment {
  id            String      @id @default(cuid())
  createdAt     DateTime    @default(now()) @map(name: "created_at")
  updatedAt     DateTime    @updatedAt @map(name: "updated_at")
  title         String
  description   String?
  isPrivate     Boolean     @default(true)
  /// @zod.custom(imports.ZSegmentFilters)
  /// [SegmentFilter]
  filters       Json        @default("[]")
  environmentId String
  environment   Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  surveys       Survey[]

  @@unique([environmentId, title])
  @@index([environmentId])
}

model Language {
  id              String           @id @default(cuid())
  createdAt       DateTime         @default(now()) @map(name: "created_at")
  updatedAt       DateTime         @updatedAt @map(name: "updated_at")
  code            String
  alias           String?
  product         Product          @relation(fields: [productId], references: [id], onDelete: Cascade)
  productId       String
  surveyLanguages SurveyLanguage[]

  @@unique([productId, code])
}

model SurveyLanguage {
  language   Language @relation(fields: [languageId], references: [id], onDelete: Cascade)
  languageId String
  surveyId   String
  survey     Survey   @relation(fields: [surveyId], references: [id], onDelete: Cascade)
  default    Boolean  @default(false)
  enabled    Boolean  @default(true)

  @@id([languageId, surveyId])
  @@index([surveyId])
  @@index([languageId])
}
