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

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

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

generator zod {
  provider    = "zod-prisma"
  modelSuffix = "ModelSchema"
  imports     = "./zod/schemas"
}

model User {
  id                 String    @id @unique @default(cuid()) @db.VarChar(30)
  username           String    @unique @db.VarChar(255)
  password           String    @db.VarChar(60)
  role               String    @db.VarChar(50)
  createdAt          DateTime  @default(now()) @db.Timestamptz(6)
  updatedAt          DateTime  @updatedAt @db.Timestamptz(6)
  deletedAt          DateTime? @db.Timestamptz(6)
  currentWorkspaceId String    @db.VarChar(30)

  currentWorkspace Workspace @relation(fields: [currentWorkspaceId], references: [id])

  workspaces WorkspacesOnUsers[]
}

model Workspace {
  id              String   @id @unique @default(cuid()) @db.VarChar(30)
  name            String   @db.VarChar(100)
  dashboardOrder  String[] // deprecated
  /// [DashboardLayout]
  dashboardLayout Json?    @db.Json
  /// [CommonPayload]
  settings        Json     @default("{}")
  createdAt       DateTime @default(now()) @db.Timestamptz(6)
  updatedAt       DateTime @updatedAt @db.Timestamptz(6)

  users              WorkspacesOnUsers[]
  websites           Website[]
  notifications      Notification[]
  monitors           Monitor[]
  monitorStatusPages MonitorStatusPage[]

  // for user currentWorkspace
  selectedUsers       User[] // user list who select this workspace, not use in most of case
  workspaceDailyUsage WorkspaceDailyUsage[]
  workspaceAuditLog   WorkspaceAuditLog[]
}

model WorkspacesOnUsers {
  userId      String   @db.VarChar(30)
  workspaceId String   @db.VarChar(30)
  role        String   @db.VarChar(100)
  createdAt   DateTime @default(now()) @db.Timestamptz(6)
  updatedAt   DateTime @updatedAt @db.Timestamptz(6)

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

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

model Website {
  id          String    @id @unique @default(cuid()) @db.VarChar(30)
  workspaceId String    @db.VarChar(30)
  name        String    @db.VarChar(100)
  domain      String?   @db.VarChar(500)
  shareId     String?   @unique @db.VarChar(50)
  resetAt     DateTime? @db.Timestamptz(6)
  monitorId   String?   @db.VarChar(30)
  createdAt   DateTime  @default(now()) @db.Timestamptz(6)
  updatedAt   DateTime  @updatedAt @db.Timestamptz(6)
  deletedAt   DateTime? @db.Timestamptz(6)

  workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade)
  monitor   Monitor?  @relation(fields: [monitorId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  sessions    WebsiteSession[]
  eventData   WebsiteEventData[]
  sessionData WebsiteSessionData[]

  @@index([workspaceId])
  @@index([createdAt])
  @@index([shareId])
}

model WebsiteSession {
  id             String   @id @unique @db.Uuid
  websiteId      String   @db.VarChar(30)
  hostname       String?  @db.VarChar(100)
  browser        String?  @db.VarChar(20)
  os             String?  @db.VarChar(20)
  device         String?  @db.VarChar(20)
  screen         String?  @db.VarChar(11)
  language       String?  @db.VarChar(35)
  ip             String?  @db.VarChar(45) // The max length of ipv6 which adapter with ipv4 is 45(for example: [::ffff:192.168.100.228] => 0000:0000:0000:0000:0000:ffff:192.168.100.228)
  country        String?  @db.Char(2)
  subdivision1   String?  @db.VarChar(20)
  subdivision2   String?  @db.VarChar(50)
  city           String?  @db.VarChar(50)
  longitude      Float?
  latitude       Float?
  accuracyRadius Int?
  createdAt      DateTime @default(now()) @db.Timestamptz(6)

  website Website @relation(fields: [websiteId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  websiteEvent WebsiteEvent[]
  sessionData  WebsiteSessionData[]

  @@index([createdAt])
  @@index([websiteId])
  @@index([websiteId, createdAt])
  @@index([websiteId, createdAt, hostname])
  @@index([websiteId, createdAt, browser])
  @@index([websiteId, createdAt, os])
  @@index([websiteId, createdAt, device])
  @@index([websiteId, createdAt, screen])
  @@index([websiteId, createdAt, language])
  @@index([websiteId, createdAt, country])
  @@index([websiteId, createdAt, subdivision1])
  @@index([websiteId, createdAt, city])
}

model WebsiteEvent {
  id             String   @id() @default(cuid()) @db.VarChar(30)
  websiteId      String   @db.VarChar(30)
  sessionId      String   @db.Uuid
  urlPath        String   @db.VarChar(500)
  urlQuery       String?  @db.VarChar(500)
  referrerPath   String?  @db.VarChar(500)
  referrerQuery  String?  @db.VarChar(500)
  referrerDomain String?  @db.VarChar(500)
  pageTitle      String?  @db.VarChar(500)
  eventType      Int      @default(1) @db.Integer // 1 is view, 2 is click event
  eventName      String?  @db.VarChar(50)
  createdAt      DateTime @default(now()) @db.Timestamptz(6)

  eventData WebsiteEventData[]
  session   WebsiteSession     @relation(fields: [sessionId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  @@index([createdAt])
  @@index([sessionId])
  @@index([websiteId])
  @@index([websiteId, createdAt])
  @@index([websiteId, createdAt, urlPath])
  @@index([websiteId, createdAt, urlQuery])
  @@index([websiteId, createdAt, referrerDomain])
  @@index([websiteId, createdAt, pageTitle])
  @@index([websiteId, createdAt, eventName])
  @@index([websiteId, sessionId, createdAt])
}

model WebsiteEventData {
  id             String    @id() @default(cuid()) @db.VarChar(30)
  websiteId      String    @db.VarChar(30)
  websiteEventId String    @db.VarChar(30)
  eventKey       String    @db.VarChar(500)
  stringValue    String?   @db.VarChar(500)
  numberValue    Decimal?  @db.Decimal(19, 4)
  dateValue      DateTime? @db.Timestamptz(6)
  dataType       Int       @db.Integer
  createdAt      DateTime  @default(now()) @db.Timestamptz(6)

  website      Website      @relation(fields: [websiteId], references: [id], onUpdate: Cascade, onDelete: Cascade)
  websiteEvent WebsiteEvent @relation(fields: [websiteEventId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  @@index([createdAt])
  @@index([websiteId])
  @@index([websiteEventId])
  @@index([websiteId, createdAt])
  @@index([websiteId, createdAt, eventKey])
}

model WebsiteSessionData {
  id          String    @id() @default(cuid()) @db.VarChar(30)
  websiteId   String    @db.VarChar(30)
  sessionId   String    @db.Uuid
  stringValue String?   @db.VarChar(500)
  numberValue Decimal?  @db.Decimal(19, 4)
  dateValue   DateTime? @db.Timestamptz(6)
  dataType    Int       @db.Integer
  createdAt   DateTime  @default(now()) @db.Timestamptz(6)

  website Website        @relation(fields: [websiteId], references: [id], onUpdate: Cascade, onDelete: Cascade)
  session WebsiteSession @relation(fields: [sessionId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  @@index([createdAt])
  @@index([websiteId])
  @@index([sessionId])
}

model TelemetrySession {
  id             String   @id @unique @db.Uuid
  workspaceId    String   @db.VarChar(30)
  hostname       String?  @db.VarChar(100)
  browser        String?  @db.VarChar(20)
  os             String?  @db.VarChar(20)
  ip             String?  @db.VarChar(45)
  country        String?  @db.Char(2)
  subdivision1   String?  @db.VarChar(20)
  subdivision2   String?  @db.VarChar(50)
  city           String?  @db.VarChar(50)
  longitude      Float?
  latitude       Float?
  accuracyRadius Int?
  createdAt      DateTime @default(now()) @db.Timestamptz(6)

  telemetryEvent TelemetryEvent[]

  @@index([createdAt])
  @@index([workspaceId, createdAt])
}

model TelemetryEvent {
  id          String  @id() @default(cuid()) @db.VarChar(30)
  sessionId   String  @db.Uuid
  workspaceId String  @db.VarChar(30)
  eventName   String? @db.VarChar(100)
  urlOrigin   String  @db.VarChar(500)
  urlPath     String  @db.VarChar(500)

  /// [CommonPayload]
  payload   Json?    @db.Json // Other payload info get from query params, should be a object
  createdAt DateTime @default(now()) @db.Timestamptz(6)

  session TelemetrySession @relation(fields: [sessionId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  @@index([createdAt])
  @@index([sessionId])
  @@index([workspaceId])
  @@index([workspaceId, createdAt])
}

model Notification {
  id          String   @id() @default(cuid()) @db.VarChar(30)
  workspaceId String   @db.VarChar(30)
  name        String   @db.VarChar(100)
  type        String   @db.VarChar(100)
  /// [CommonPayload]
  payload     Json     @db.Json
  createdAt   DateTime @default(now()) @db.Timestamptz(6)

  workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  monitors Monitor[]

  @@index([workspaceId])
}

model Monitor {
  id          String   @id() @default(cuid()) @db.VarChar(30)
  workspaceId String   @db.VarChar(30)
  name        String   @db.VarChar(100)
  type        String   @db.VarChar(100)
  active      Boolean  @default(true) @db.Boolean
  interval    Int      @default(20) @db.Integer
  // TODO
  // maxRetry      Int       @default(0) @db.Integer
  // retryInterval Int       @default(0) @db.Integer
  /// [CommonPayload]
  payload     Json     @db.Json
  createdAt   DateTime @default(now()) @db.Timestamptz(6)
  updatedAt   DateTime @default(now()) @updatedAt @db.Timestamptz(6)

  workspace Workspace @relation(fields: [workspaceId], references: [id])

  websites      Website[]
  notifications Notification[]
  events        MonitorEvent[]
  datas         MonitorData[]
  status        MonitorStatus[]

  @@index([workspaceId])
}

model MonitorEvent {
  id        String   @id @default(cuid()) @db.VarChar(30)
  message   String   @db.VarChar(500)
  monitorId String   @db.VarChar(30)
  type      String   @db.VarChar(100) // UP or DOWN
  createdAt DateTime @default(now()) @db.Timestamptz(6)

  monitor Monitor @relation(fields: [monitorId], references: [id], onUpdate: Cascade, onDelete: Cascade)
}

model MonitorData {
  id        String   @id @default(cuid()) @db.VarChar(30)
  monitorId String   @db.VarChar(30)
  value     Int      @default(0) @db.Integer // -1 means error
  createdAt DateTime @default(now()) @db.Timestamptz(6)

  monitor Monitor @relation(fields: [monitorId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  @@index([createdAt])
}

// Use for record latest monitor status, for example tls status
model MonitorStatus {
  monitorId  String   @db.VarChar(30)
  statusName String   @db.VarChar(50)
  /// [CommonPayload]
  payload    Json     @db.Json
  createdAt  DateTime @default(now()) @db.Timestamptz(6)
  updatedAt  DateTime @updatedAt @db.Timestamptz(6)

  monitor Monitor @relation(fields: [monitorId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  @@id([monitorId, statusName])
}

model MonitorStatusPage {
  id          String   @id @default(cuid()) @db.VarChar(30)
  workspaceId String   @db.VarChar(30)
  slug        String   @unique // url slug
  title       String   @db.VarChar(100)
  description String   @default("") @db.VarChar(1000)
  /// [MonitorStatusPageList]
  /// @zod.custom(imports.MonitorStatusPageListSchema)
  monitorList Json     @default("[]") // monitor list
  createdAt   DateTime @default(now()) @db.Timestamptz(6)
  updatedAt   DateTime @updatedAt @db.Timestamptz(6)

  workspace Workspace @relation(fields: [workspaceId], references: [id])

  @@index([slug])
}

model WorkspaceDailyUsage {
  id                    String   @id @unique @default(cuid()) @db.VarChar(30)
  workspaceId           String   @db.VarChar(30)
  date                  DateTime @db.Date // calc every day
  websiteAcceptedCount  Int // website accept any request count
  websiteEventCount     Int // website accept event request count
  monitorExecutionCount Int // monitor exec number count
  createdAt             DateTime @default(now()) @db.Timestamptz(6)

  workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  @@unique([workspaceId, date])
}

model WorkspaceAuditLog {
  id          String                 @id @default(cuid()) @db.VarChar(30)
  workspaceId String                 @db.VarChar(30)
  content     String
  relatedId   String?
  relatedType WorkspaceAuditLogType?
  createdAt   DateTime               @default(now()) @db.Timestamptz(6)

  workspace Workspace @relation(fields: [workspaceId], references: [id], onUpdate: Cascade, onDelete: Cascade)

  @@index([createdAt])
}

enum WorkspaceAuditLogType {
  Monitor
  Notification
}
