// 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")
  extensions = [pgvector(map: "vector")]
}

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

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

enum PipelineTriggers {
  responseCreated
  responseUpdated
  responseFinished
}

enum WebhookSource {
  user
  zapier
  make
  n8n
  activepieces
}

/// Represents a webhook endpoint for receiving survey-related events.
/// Webhooks can be configured to receive notifications about response creation, updates, and completion.
///
/// @property id - Unique identifier for the webhook
/// @property name - Optional display name for the webhook
/// @property url - The endpoint URL where events will be sent
/// @property source - Origin of the webhook (user, zapier, make, etc.)
/// @property environment - Associated environment
/// @property triggers - Types of events that trigger this webhook
/// @property surveyIds - List of surveys this webhook is monitoring
model Webhook {
  id            String             @id @default(cuid())
  name          String?
  createdAt     DateTime           @default(now()) @map(name: "created_at")
  updatedAt     DateTime           @default(now()) @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])
}

/// Represents an attribute value associated with a contact.
/// Used to store custom properties and characteristics of contacts.
///
/// @property id - Unique identifier for the attribute
/// @property attributeKey - Reference to the attribute definition
/// @property contact - The contact this attribute belongs to
/// @property value - The actual value of the attribute
model ContactAttribute {
  id             String              @id @default(cuid())
  createdAt      DateTime            @default(now()) @map(name: "created_at")
  updatedAt      DateTime            @updatedAt @map(name: "updated_at")
  attributeKey   ContactAttributeKey @relation(fields: [attributeKeyId], references: [id], onDelete: Cascade)
  attributeKeyId String
  contact        Contact             @relation(fields: [contactId], references: [id], onDelete: Cascade)
  contactId      String
  value          String

  @@unique([contactId, attributeKeyId])
  @@index([attributeKeyId, value])
}

enum ContactAttributeType {
  default
  custom
}

/// Defines the possible attributes that can be assigned to contacts.
/// Acts as a schema for contact attributes within an environment.
///
/// @property id - Unique identifier for the attribute key
/// @property isUnique - Whether the attribute must have unique values across contacts
/// @property key - The attribute identifier used in the system
/// @property name - Display name for the attribute
/// @property type - Whether this is a default or custom attribute
/// @property environment - The environment this attribute belongs to
model ContactAttributeKey {
  id               String                  @id @default(cuid())
  createdAt        DateTime                @default(now()) @map(name: "created_at")
  updatedAt        DateTime                @updatedAt @map(name: "updated_at")
  isUnique         Boolean                 @default(false)
  key              String
  name             String?
  description      String?
  type             ContactAttributeType    @default(custom)
  environment      Environment             @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  environmentId    String
  attributes       ContactAttribute[]
  attributeFilters SurveyAttributeFilter[]

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

/// Represents a person or user who can receive and respond to surveys.
/// Contacts are environment-specific and can have multiple attributes and responses.
///
/// @property id - Unique identifier for the contact
/// @property userId - Optional external user identifier
/// @property environment - The environment this contact belongs to
/// @property responses - Survey responses from this contact
/// @property attributes - Custom attributes associated with this contact
/// @property displays - Record of surveys shown to this contact
model Contact {
  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    ContactAttribute[]
  displays      Display[]

  @@index([environmentId])
}

/// Stores a user's response to a survey, including their answers and metadata.
/// Each response is linked to a specific survey and optionally to a contact.
///
/// @property id - Unique identifier for the response
/// @property finished - Whether the survey was completed
/// @property survey - The associated survey
/// @property contact - The optional contact who provided the response
/// @property data - JSON object containing the actual response data
/// @property variables - Custom variables used in the response
/// @property ttc - Time to completion metrics
/// @property meta - Additional metadata about the response
model Response {
  id                String            @id @default(cuid())
  createdAt         DateTime          @default(now()) @map(name: "created_at")
  updatedAt         DateTime          @default(now()) @updatedAt @map(name: "updated_at")
  finished          Boolean           @default(false)
  survey            Survey            @relation(fields: [surveyId], references: [id], onDelete: Cascade)
  surveyId          String
  contact           Contact?          @relation(fields: [contactId], references: [id], onDelete: Cascade)
  contactId         String?
  endingId          String?
  notes             ResponseNote[]
  /// [ResponseData]
  data              Json              @default("{}")
  /// [ResponseVariables]
  variables         Json              @default("{}")
  /// [ResponseTtc]
  ttc               Json              @default("{}")
  /// [ResponseMeta]
  meta              Json              @default("{}")
  tags              TagsOnResponses[]
  /// [ResponseContactAttributes]
  contactAttributes Json?
  // singleUseId, used to prevent multiple responses
  singleUseId       String?
  language          String?
  documents         Document[]
  displayId         String?           @unique
  display           Display?          @relation(fields: [displayId], references: [id])

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

/// Represents notes or comments added to survey responses by team members.
/// Used for internal communication and response analysis.
///
/// @property id - Unique identifier for the note
/// @property response - The associated survey response
/// @property user - The team member who created the note
/// @property text - Content of the note
/// @property isResolved - Whether the note has been marked as resolved
/// @property isEdited - Whether the note has been modified
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])
}

/// Represents a label that can be applied to survey responses.
/// Used for categorizing and organizing responses within an environment.
///
/// @property id - Unique identifier for the tag
/// @property name - Display name of the tag
/// @property responses - Survey responses tagged with this label
/// @property environment - The environment this tag belongs to
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])
}

/// Junction table linking tags to responses.
/// Enables many-to-many relationship between tags and responses.
///
/// @property response - The tagged response
/// @property tag - The tag applied to the response
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
}

/// Records when a survey is shown to a user.
/// Tracks survey display history and response status.
///
/// @property id - Unique identifier for the display event
/// @property survey - The survey that was displayed
/// @property contact - The contact who saw the survey
/// @property status - Whether the survey was just seen or responded to
/// @property response - The associated response if one exists
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
  contact    Contact?       @relation(fields: [contactId], references: [id], onDelete: Cascade)
  contactId  String?
  responseId String?        @unique //deprecated
  status     DisplayStatus?
  response   Response?

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

/// Links surveys to specific trigger actions.
/// Defines which user actions should cause a survey to be displayed.
/// This is the connection table between Surveys and ActionClasses that determines
/// when and under what conditions a survey should be triggered.
///
/// @property id - Unique identifier for the trigger
/// @property survey - The survey to be triggered
/// @property actionClass - The action that triggers the survey
/// @property createdAt - When the trigger was created
/// @property updatedAt - When the trigger was last modified
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
}

/// Defines targeting rules for surveys based on contact attributes.
/// Used to show surveys only to contacts matching specific criteria.
///
/// @property id - Unique identifier for the filter
/// @property attributeKey - The contact attribute to filter on
/// @property survey - The survey being filtered
/// @property condition - The comparison operator to use
/// @property value - The value to compare against
model SurveyAttributeFilter {
  id             String                         @id @default(cuid())
  createdAt      DateTime                       @default(now()) @map(name: "created_at")
  updatedAt      DateTime                       @updatedAt @map(name: "updated_at")
  attributeKey   ContactAttributeKey            @relation(fields: [attributeKeyId], references: [id], onDelete: Cascade)
  attributeKeyId String
  survey         Survey                         @relation(fields: [surveyId], references: [id], onDelete: Cascade)
  surveyId       String
  condition      SurveyAttributeFilterCondition
  value          String

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

enum SurveyType {
  link
  web
  website
  app
}

enum displayOptions {
  displayOnce
  displayMultiple
  displaySome
  respondMultiple
}

/// Represents a complete survey configuration including questions, styling, and display rules.
/// Core model for the survey functionality in Formbricks.
///
/// @property id - Unique identifier for the survey
/// @property name - Display name of the survey
/// @property type - Survey delivery method (link, web, website, app)
/// @property status - Current state of the survey (draft, active, completed, etc)
/// @property environment - The environment this survey belongs to
/// @property questions - JSON array containing survey questions configuration
/// @property displayOption - Rules for how often the survey can be shown
/// @property triggers - Actions that can trigger this survey
/// @property attributeFilters - Rules for targeting specific contacts
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)
  /// [SurveyWelcomeCard]
  welcomeCard         Json                    @default("{\"enabled\": false}")
  /// [SurveyQuestions]
  questions           Json                    @default("[]")
  /// [SurveyEnding]
  endings             Json[]                  @default([])
  thankYouCard        Json? //deprecated            
  /// [SurveyHiddenFields]
  hiddenFields        Json                    @default("{\"enabled\": false}")
  /// [SurveyVariables]
  variables           Json                    @default("[]")
  responses           Response[]
  displayOption       displayOptions          @default(displayOnce)
  recontactDays       Int?
  displayLimit        Int?
  triggers            SurveyTrigger[]
  /// [SurveyInlineTriggers]
  inlineTriggers      Json?
  attributeFilters    SurveyAttributeFilter[]
  displays            Display[]
  autoClose           Int?
  autoComplete        Int?
  delay               Int                     @default(0)
  runOnDate           DateTime?
  closeOnDate         DateTime?
  /// [SurveyClosedMessage]
  surveyClosedMessage Json?
  segmentId           String?
  segment             Segment?                @relation(fields: [segmentId], references: [id])

  /// [SurveyProjectOverwrites]
  projectOverwrites Json?

  /// [SurveyStyling]
  styling Json?

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

  /// [SurveyVerifyEmail]
  verifyEmail                     Json? // deprecated
  isVerifyEmailEnabled            Boolean          @default(false)
  isSingleResponsePerEmailEnabled Boolean          @default(false)
  isBackButtonHidden              Boolean          @default(false)
  pin                             String?
  resultShareKey                  String?          @unique
  displayPercentage               Decimal?
  languages                       SurveyLanguage[]
  showLanguageSwitch              Boolean?
  documents                       Document[]
  followUps                       SurveyFollowUp[]
  /// [SurveyRecaptcha]
  recaptcha                       Json?            @default("{\"enabled\": false, \"threshold\":0.1}")

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

/// Defines follow-up actions for survey responses.
/// Enables automated actions based on specific survey response conditions.
///
/// @property id - Unique identifier for the follow-up
/// @property survey - The associated survey
/// @property name - Display name for the follow-up
/// @property trigger - Conditions that activate the follow-up
/// @property action - Actions to take when triggered
model SurveyFollowUp {
  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
  name      String
  /// [SurveyFollowUpTrigger]
  trigger   Json
  /// [SurveyFollowUpAction]
  action    Json
}

enum ActionType {
  code
  noCode
}

/// Represents a user action that can trigger surveys.
/// Used to define points in the user journey where surveys can be shown.
///
/// @property id - Unique identifier for the action
/// @property name - Display name of the action
/// @property type - Whether this is a code or no-code action
/// @property key - Unique identifier used in code implementation
/// @property noCodeConfig - Configuration for no-code setup
/// @property environment - The environment this action belongs to
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?
  /// [ActionClassNoCodeConfig]
  noCodeConfig   Json?
  environment    Environment     @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  environmentId  String
  surveyTriggers SurveyTrigger[]

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

enum EnvironmentType {
  production
  development
}

enum IntegrationType {
  googleSheets
  notion
  airtable
  slack
}

/// Represents third-party service integrations.
/// Enables data flow between Formbricks and external services.
///
/// @property id - Unique identifier for the integration
/// @property type - The service being integrated (Google Sheets, Notion, etc.)
/// @property environment - The environment this integration belongs to
/// @property config - Service-specific configuration details
model Integration {
  id            String          @id @default(cuid())
  type          IntegrationType
  environmentId String
  /// [IntegrationConfig]
  config        Json
  environment   Environment     @relation(fields: [environmentId], references: [id], onDelete: Cascade)

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

enum DataMigrationStatus {
  pending
  applied
  failed
}

/// Tracks database schema migrations.
/// Used to manage and track the state of data structure changes.
///
/// @property id - Unique identifier for the migration
/// @property name - Name of the migration
/// @property status - Current state of the migration (pending, applied, failed)
/// @property startedAt - When the migration began
/// @property finishedAt - When the migration completed
model DataMigration {
  id         String              @id @default(cuid())
  startedAt  DateTime            @default(now()) @map(name: "started_at")
  finishedAt DateTime?           @map(name: "finished_at")
  name       String              @unique
  status     DataMigrationStatus
}

/// Represents either a production or development environment within a project.
/// Each project has exactly two environments, serving as the main reference point
/// for most Formbricks resources including surveys and actions.
///
/// @property id - Unique identifier for the environment
/// @property type - Either 'production' or 'development'
/// @property project - Reference to parent project
/// @property widgetSetupCompleted - Tracks initial widget setup status
/// @property surveys - Collection of surveys in this environment
/// @property contacts - Collection of contacts/users tracked
/// @property actionClasses - Defined actions that can trigger surveys
/// @property attributeKeys - Custom attributes configuration
model Environment {
  id                   String                @id @default(cuid())
  createdAt            DateTime              @default(now()) @map(name: "created_at")
  updatedAt            DateTime              @updatedAt @map(name: "updated_at")
  type                 EnvironmentType
  project              Project               @relation(fields: [projectId], references: [id], onDelete: Cascade)
  projectId            String
  widgetSetupCompleted Boolean               @default(false)
  appSetupCompleted    Boolean               @default(false)
  surveys              Survey[]
  contacts             Contact[]
  actionClasses        ActionClass[]
  attributeKeys        ContactAttributeKey[]
  webhooks             Webhook[]
  tags                 Tag[]
  segments             Segment[]
  integration          Integration[]
  documents            Document[]
  insights             Insight[]
  ApiKeyEnvironment    ApiKeyEnvironment[]

  @@index([projectId])
}

enum WidgetPlacement {
  bottomLeft
  bottomRight
  topLeft
  topRight
  center
}

/// Main grouping mechanism for resources in Formbricks.
/// Each organization can have multiple projects to separate different applications or products.
///
/// @property id - Unique identifier for the project
/// @property name - Display name of the project
/// @property organization - Reference to parent organization
/// @property environments - Development and production environments
/// @property styling - Project-wide styling configuration
/// @property config - Project-specific configuration
/// @property recontactDays - Default recontact delay for surveys
/// @property placement - Default widget placement for in-app surveys
model Project {
  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
  /// [Styling]
  styling              Json            @default("{\"allowStyleOverwrite\":true}")
  /// [ProjectConfig]
  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[]
  /// [Logo]
  logo                 Json?
  projectTeams         ProjectTeam[]

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

/// Represents the top-level organizational hierarchy in Formbricks.
/// Self-hosting instances typically have a single organization, while Formbricks Cloud
/// supports multiple organizations with multi-tenancy.
///
/// @property id - Unique identifier for the organization
/// @property name - Display name of the organization
/// @property memberships - User memberships within the organization
/// @property projects - Collection of projects owned by the organization
/// @property billing - JSON field containing billing information
/// @property whitelabel - Whitelabel configuration for the organization
/// @property isAIEnabled - Controls access to AI-powered features
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[]
  projects    Project[]
  /// [OrganizationBilling]
  billing     Json
  /// [OrganizationWhitelabel]
  whitelabel  Json         @default("{}")
  invites     Invite[]
  isAIEnabled Boolean      @default(false)
  teams       Team[]
  apiKeys     ApiKey[]
}

enum OrganizationRole {
  owner
  manager
  member
  billing
}

enum MembershipRole {
  owner
  admin
  editor
  developer
  viewer
}

/// Links users to organizations with specific roles.
/// Manages organization membership and permissions.
/// Core model for managing user access within organizations.
///
/// @property organization - The organization the user belongs to
/// @property user - The member user
/// @property accepted - Whether the user has accepted the membership
/// @property role - User's role within the organization (owner, manager, member, billing)
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)
  deprecatedRole MembershipRole? //deprecated
  role           OrganizationRole @default(member)

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

/// Represents pending invitations to join an organization.
/// Used to manage the process of adding new users to an organization.
/// Once accepted, invites are converted into memberships.
///
/// @property id - Unique identifier for the invite
/// @property email - Email address of the invited user
/// @property name - Optional display name for the invited user
/// @property organization - The organization sending the invite
/// @property creator - The user who created the invite
/// @property acceptor - The user who accepted the invite (if accepted)
/// @property expiresAt - When the invite becomes invalid
/// @property role - Intended role for the invited user
/// @property teamIds - Teams the user will be added to upon acceptance
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?
  createdAt      DateTime         @default(now())
  expiresAt      DateTime
  deprecatedRole MembershipRole? //deprecated
  role           OrganizationRole @default(member)
  teamIds        String[]         @default([])

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

/// Represents enhanced API authentication keys with organization-level ownership.
/// Used for authenticating API requests to Formbricks with more granular permissions.
///
/// @property id - Unique identifier for the API key
/// @property label - Optional descriptive name for the key
/// @property hashedKey - Securely stored API key
/// @property organization - The organization this key belongs to
/// @property createdBy - User ID who created this key
/// @property lastUsedAt - Timestamp of last usage
/// @property apiKeyEnvironments - Environments this key has access to
model ApiKey {
  id                 String              @id @default(cuid())
  createdAt          DateTime            @default(now())
  createdBy          String?
  lastUsedAt         DateTime?
  label              String
  hashedKey          String              @unique
  organizationId     String
  organization       Organization        @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  apiKeyEnvironments ApiKeyEnvironment[]
  /// [OrganizationAccess]
  organizationAccess Json                @default("{}")

  @@index([organizationId])
}

/// Defines permission levels for API keys.
/// Controls what operations an API key can perform.
enum ApiKeyPermission {
  read
  write
  manage
}

/// Links API keys to environments with specific permissions.
/// Enables granular access control for API keys across environments.
///
/// @property id - Unique identifier for the environment access entry
/// @property apiKey - The associated API key
/// @property environment - The environment being accessed
/// @property permission - Level of access granted
model ApiKeyEnvironment {
  id            String           @id @default(cuid())
  createdAt     DateTime         @default(now())
  updatedAt     DateTime         @updatedAt
  apiKeyId      String
  apiKey        ApiKey           @relation(fields: [apiKeyId], references: [id], onDelete: Cascade)
  environmentId String
  environment   Environment      @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  permission    ApiKeyPermission

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

enum IdentityProvider {
  email
  github
  google
  azuread
  openid
  saml
}

/// Stores third-party authentication account information.
/// Enables OAuth and other external authentication methods.
///
/// @property id - Unique identifier for the account
/// @property user - The Formbricks user who owns this account
/// @property provider - The authentication provider (GitHub, Google, etc.)
/// @property providerAccountId - User ID from the provider
/// @property access_token - OAuth access token
/// @property refresh_token - OAuth refresh token
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
}

/// Represents a user in the Formbricks system.
/// Central model for user authentication and profile management.
///
/// @property id - Unique identifier for the user
/// @property name - Display name of the user
/// @property email - User's email address
/// @property role - User's professional role
/// @property objective - User's main goal with Formbricks
/// @property twoFactorEnabled - Whether 2FA is active
/// @property memberships - Organizations the user belongs to
/// @property notificationSettings - User's notification preferences
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?
  /// [UserNotificationSettings]
  notificationSettings      Json             @default("{}")
  /// [Locale]
  locale                    String           @default("en-US")
  surveys                   Survey[]
  teamUsers                 TeamUser[]
  lastLoginAt               DateTime?
  isActive                  Boolean          @default(true)

  @@index([email])
}

/// Maps a short URL to its full destination.
/// Used for creating memorable, shortened URLs for surveys.
///
/// @property id - Short identifier/slug for the URL
/// @property url - The full destination URL
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
}

/// Defines a segment of contacts based on attributes.
/// Used for targeting surveys to specific user groups.
///
/// @property id - Unique identifier for the segment
/// @property title - Display name of the segment
/// @property filters - Rules defining the segment
/// @property isPrivate - Whether the segment is private
/// @property environment - The environment this segment belongs to
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)
  /// [SegmentFilter]
  filters       Json        @default("[]")
  environmentId String
  environment   Environment @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  surveys       Survey[]

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

/// Represents a supported language in the system.
/// Used for multilingual survey support.
///
/// @property id - Unique identifier for the language
/// @property code - Language code (e.g., 'en-US')
/// @property alias - Optional friendly name
/// @property project - The project this language is enabled for
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?
  project         Project          @relation(fields: [projectId], references: [id], onDelete: Cascade)
  projectId       String
  surveyLanguages SurveyLanguage[]

  @@unique([projectId, code])
}

/// Links surveys to their supported languages.
/// Manages which languages are available for each survey.
///
/// @property language - The supported language
/// @property survey - The associated survey
/// @property default - Whether this is the default language
/// @property enabled - Whether this language is currently active
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])
}

enum InsightCategory {
  featureRequest
  complaint
  praise
  other
}

/// Stores analyzed insights from survey responses.
/// Used for tracking patterns and extracting meaningful information.
///
/// @property id - Unique identifier for the insight
/// @property category - Type of insight (feature request, complaint, etc.)
/// @property title - Summary of the insight
/// @property description - Detailed explanation
/// @property vector - Embedding vector for similarity search
model Insight {
  id               String                      @id @default(cuid())
  createdAt        DateTime                    @default(now()) @map(name: "created_at")
  updatedAt        DateTime                    @updatedAt @map(name: "updated_at")
  environmentId    String
  environment      Environment                 @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  category         InsightCategory
  title            String
  description      String
  vector           Unsupported("vector(512)")?
  documentInsights DocumentInsight[]
}

/// Links insights to source documents.
/// Enables tracing insights back to original responses.
///
/// @property document - The source document
/// @property insight - The derived insight
model DocumentInsight {
  documentId String
  document   Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
  insightId  String
  insight    Insight  @relation(fields: [insightId], references: [id], onDelete: Cascade)

  @@id([documentId, insightId])
  @@index([insightId])
}

enum Sentiment {
  positive
  negative
  neutral
}

/// Represents a processed text document from survey responses.
/// Used for analysis and insight generation.
///
/// @property id - Unique identifier for the document
/// @property survey - The associated survey
/// @property response - The source response
/// @property sentiment - Analyzed sentiment (positive, negative, neutral)
/// @property text - The document content
/// @property vector - Embedding vector for similarity search
model Document {
  id               String                      @id @default(cuid())
  createdAt        DateTime                    @default(now()) @map(name: "created_at")
  updatedAt        DateTime                    @updatedAt @map(name: "updated_at")
  environmentId    String
  environment      Environment                 @relation(fields: [environmentId], references: [id], onDelete: Cascade)
  surveyId         String?
  survey           Survey?                     @relation(fields: [surveyId], references: [id], onDelete: Cascade)
  responseId       String?
  response         Response?                   @relation(fields: [responseId], references: [id], onDelete: Cascade)
  questionId       String?
  sentiment        Sentiment
  isSpam           Boolean
  text             String
  vector           Unsupported("vector(512)")?
  documentInsights DocumentInsight[]

  @@unique([responseId, questionId])
  @@index([createdAt])
}

/// Represents a team within an organization.
/// Enables group-based access control and collaboration.
///
/// @property id - Unique identifier for the team
/// @property name - Display name of the team
/// @property organization - The parent organization
/// @property teamUsers - Users who are part of this team
/// @property projectTeams - Projects this team has access to
model Team {
  id             String        @id @default(cuid())
  createdAt      DateTime      @default(now()) @map(name: "created_at")
  updatedAt      DateTime      @updatedAt @map(name: "updated_at")
  name           String
  organizationId String
  organization   Organization  @relation(fields: [organizationId], references: [id], onDelete: Cascade)
  teamUsers      TeamUser[]
  projectTeams   ProjectTeam[]

  @@unique([organizationId, name])
}

enum TeamUserRole {
  admin
  contributor
}

/// Links users to teams with specific roles.
/// Manages team membership and permissions.
///
/// @property team - The associated team
/// @property user - The team member
/// @property role - User's role within the team
model TeamUser {
  createdAt DateTime     @default(now()) @map(name: "created_at")
  updatedAt DateTime     @updatedAt @map(name: "updated_at")
  teamId    String
  team      Team         @relation(fields: [teamId], references: [id], onDelete: Cascade)
  userId    String
  user      User         @relation(fields: [userId], references: [id], onDelete: Cascade)
  role      TeamUserRole

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

enum ProjectTeamPermission {
  read
  readWrite
  manage
}

/// Defines team access to specific projects.
/// Manages project-level permissions for teams.
///
/// @property project - The accessed project
/// @property team - The team receiving access
/// @property permission - Level of access granted
model ProjectTeam {
  createdAt  DateTime              @default(now()) @map(name: "created_at")
  updatedAt  DateTime              @updatedAt @map(name: "updated_at")
  projectId  String
  project    Project               @relation(fields: [projectId], references: [id], onDelete: Cascade)
  teamId     String
  team       Team                  @relation(fields: [teamId], references: [id], onDelete: Cascade)
  permission ProjectTeamPermission @default(read)

  @@id([projectId, teamId])
  @@index([teamId])
}
