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

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

model Snl {
  id   Int     @id @default(autoincrement())
  live Boolean @default(false)
}

model User {
  id                        Int                  @id @default(autoincrement())
  createdAt                 DateTime             @default(now()) @map("created_at")
  updatedAt                 DateTime             @default(now()) @updatedAt @map("updated_at")
  name                      String?              @unique(map: "users.name_unique") @db.Citext
  email                     String?              @unique(map: "users.email_unique")
  emailVerified             DateTime?            @map("email_verified")
  emailHash                 String?              @unique(map: "users.email_hash_unique")
  image                     String?
  msats                     BigInt               @default(0)
  freeComments              Int                  @default(5)
  freePosts                 Int                  @default(2)
  checkedNotesAt            DateTime?
  foundNotesAt              DateTime?
  pubkey                    String?              @unique(map: "users.pubkey_unique")
  apiKeyHash                String?              @unique(map: "users.apikeyhash_unique") @db.Char(64)
  apiKeyEnabled             Boolean              @default(false)
  tipDefault                Int                  @default(100)
  tipRandomMin              Int?
  tipRandomMax              Int?
  bioId                     Int?
  inviteId                  String?
  tipPopover                Boolean              @default(false)
  upvotePopover             Boolean              @default(false)
  trust                     Float                @default(0)
  lastSeenAt                DateTime?
  stackedMsats              BigInt               @default(0)
  stackedMcredits           BigInt               @default(0)
  noteAllDescendants        Boolean              @default(true)
  noteDeposits              Boolean              @default(true)
  noteWithdrawals           Boolean              @default(true)
  noteEarning               Boolean              @default(true)
  noteInvites               Boolean              @default(true)
  noteItemSats              Boolean              @default(true)
  noteMentions              Boolean              @default(true)
  noteItemMentions          Boolean              @default(true)
  noteForwardedSats         Boolean              @default(true)
  lastCheckedJobs           DateTime?
  noteJobIndicator          Boolean              @default(true)
  photoId                   Int?
  upvoteTrust               Float                @default(0)
  hideInvoiceDesc           Boolean              @default(false)
  wildWestMode              Boolean              @default(false)
  satsFilter                Int                  @default(10)
  nsfwMode                  Boolean              @default(false)
  fiatCurrency              String               @default("USD")
  withdrawMaxFeeDefault     Int                  @default(10)
  autoDropBolt11s           Boolean              @default(false)
  hideFromTopUsers          Boolean              @default(false)
  turboTipping              Boolean              @default(false)
  zapUndos                  Int?
  imgproxyOnly              Boolean              @default(false)
  showImagesAndVideos       Boolean              @default(true)
  hideWalletBalance         Boolean              @default(false)
  disableFreebies           Boolean?
  referrerId                Int?
  nostrPubkey               String?
  greeterMode               Boolean              @default(false)
  nostrAuthPubkey           String?              @unique(map: "users.nostrAuthPubkey_unique")
  nostrCrossposting         Boolean              @default(false)
  slashtagId                String?              @unique(map: "users.slashtagId_unique")
  noteCowboyHat             Boolean              @default(true)
  streak                    Int?
  gunStreak                 Int?
  horseStreak               Int?
  hasSendWallet             Boolean              @default(false)
  hasRecvWallet             Boolean              @default(false)
  subs                      String[]
  hideCowboyHat             Boolean              @default(false)
  Bookmarks                 Bookmark[]
  Donation                  Donation[]
  Earn                      Earn[]
  invites                   Invite[]             @relation("Invites")
  invoices                  Invoice[]
  items                     Item[]               @relation("UserItems")
  actions                   ItemAct[]
  mentions                  Mention[]
  messages                  Message[]
  PushSubscriptions         PushSubscription[]
  ReferralAct               ReferralAct[]
  Streak                    Streak[]
  ThreadSubscriptions       ThreadSubscription[]
  SubSubscriptions          SubSubscription[]
  Upload                    Upload[]             @relation("Uploads")
  nostrRelays               UserNostrRelay[]
  withdrawls                Withdrawl[]
  bio                       Item?                @relation(fields: [bioId], references: [id])
  invite                    Invite?              @relation(fields: [inviteId], references: [id])
  photo                     Upload?              @relation(fields: [photoId], references: [id])
  referrer                  User?                @relation("referrals", fields: [referrerId], references: [id])
  referrees                 User[]               @relation("referrals")
  Account                   Account[]
  Session                   Session[]
  itemForwards              ItemForward[]
  hideBookmarks             Boolean              @default(false)
  hideGithub                Boolean              @default(true)
  hideNostr                 Boolean              @default(true)
  hideTwitter               Boolean              @default(true)
  noReferralLinks           Boolean              @default(false)
  githubId                  String?
  twitterId                 String?
  followers                 UserSubscription[]   @relation("follower")
  followees                 UserSubscription[]   @relation("followee")
  hideWelcomeBanner         Boolean              @default(false)
  hideWalletRecvPrompt      Boolean              @default(false)
  diagnostics               Boolean              @default(false)
  hideIsContributor         Boolean              @default(false)
  lnAddr                    String?
  autoWithdrawMaxFeePercent Float?
  autoWithdrawThreshold     Int?
  autoWithdrawMaxFeeTotal   Int?
  mcredits                  BigInt               @default(0)
  receiveCreditsBelowSats   Int                  @default(10)
  sendCreditsBelowSats      Int                  @default(10)
  muters                    Mute[]               @relation("muter")
  muteds                    Mute[]               @relation("muted")
  ArcOut                    Arc[]                @relation("fromUser")
  ArcIn                     Arc[]                @relation("toUser")
  Sub                       Sub[]
  SubAct                    SubAct[]
  MuteSub                   MuteSub[]
  wallets                   Wallet[]
  TerritoryTransfers        TerritoryTransfer[]  @relation("TerritoryTransfer_oldUser")
  TerritoryReceives         TerritoryTransfer[]  @relation("TerritoryTransfer_newUser")
  AncestorReplies           Reply[]              @relation("AncestorReplyUser")
  Replies                   Reply[]
  walletLogs                WalletLog[]
  Reminder                  Reminder[]
  PollBlindVote             PollBlindVote[]
  ItemUserAgg               ItemUserAgg[]
  oneDayReferrals           OneDayReferral[]     @relation("OneDayReferral_referrer")
  oneDayReferrees           OneDayReferral[]     @relation("OneDayReferral_referrees")
  vaultKeyHash              String               @default("")
  walletsUpdatedAt          DateTime?
  vaultEntries              VaultEntry[]         @relation("VaultEntries")
  proxyReceive              Boolean              @default(true)
  directReceive             Boolean              @default(true)
  DirectPaymentReceived     DirectPayment[]      @relation("DirectPaymentReceived")
  DirectPaymentSent         DirectPayment[]      @relation("DirectPaymentSent")
  UserSubTrust              UserSubTrust[]

  @@index([photoId])
  @@index([createdAt], map: "users.created_at_index")
  @@index([inviteId], map: "users.inviteId_index")
  @@index([streak])
  @@index([gunStreak])
  @@index([horseStreak])
  @@map("users")
}

enum OneDayReferralType {
  REFERRAL
  POST
  COMMENT
  PROFILE
  TERRITORY
}

model OneDayReferral {
  id         Int                @id @default(autoincrement())
  createdAt  DateTime           @default(now()) @map("created_at")
  updatedAt  DateTime           @default(now()) @updatedAt @map("updated_at")
  referrerId Int
  refereeId  Int
  referrer   User               @relation("OneDayReferral_referrer", fields: [referrerId], references: [id], onDelete: Cascade)
  referee    User               @relation("OneDayReferral_referrees", fields: [refereeId], references: [id], onDelete: Cascade)
  type       OneDayReferralType
  typeId     String
  landing    Boolean            @default(false)

  @@index([createdAt])
  @@index([referrerId])
  @@index([refereeId])
  @@index([type, typeId])
}

model UserSubTrust {
  subName            String   @db.Citext
  userId             Int
  zapPostTrust       Float    @default(0)
  subZapPostTrust    Float    @default(0)
  zapCommentTrust    Float    @default(0)
  subZapCommentTrust Float    @default(0)
  createdAt          DateTime @default(now()) @map("created_at")
  updatedAt          DateTime @default(now()) @updatedAt @map("updated_at")
  user               User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  sub                Sub      @relation(fields: [subName], references: [name], onDelete: Cascade)

  @@id([userId, subName])
}

enum WalletType {
  LIGHTNING_ADDRESS
  LND
  CLN
  LNBITS
  NWC
  PHOENIXD
  BLINK
  LNC
  WEBLN
}

model Wallet {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  userId    Int
  label     String?
  enabled   Boolean  @default(true)
  priority  Int      @default(0)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  // NOTE: this denormalized json field exists to make polymorphic joins efficient
  // when reading wallets ... it is populated by a trigger when wallet descendants update
  // otherwise reading wallets would require a join on every descendant table
  // which might not be numerous for wallets but would be for other tables
  // so this is a pattern we use only to be consistent with future polymorphic tables
  // because it gives us fast reads and type safe writes
  type                   WalletType
  wallet                 Json?                   @db.JsonB
  walletLightningAddress WalletLightningAddress?
  walletLND              WalletLND?
  walletCLN              WalletCLN?
  walletLNbits           WalletLNbits?
  walletNWC              WalletNWC?
  walletPhoenixd         WalletPhoenixd?
  walletBlink            WalletBlink?

  vaultEntries   VaultEntry[]     @relation("VaultEntries")
  withdrawals    Withdrawl[]
  InvoiceForward InvoiceForward[]
  DirectPayment  DirectPayment[]

  @@index([userId])
  @@index([priority])
}

model VaultEntry {
  id        Int      @id @default(autoincrement())
  key       String   @db.Text
  iv        String   @db.Text
  value     String   @db.Text
  userId    Int
  walletId  Int?
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade, name: "VaultEntries")
  wallet    Wallet?  @relation(fields: [walletId], references: [id], onDelete: Cascade, name: "VaultEntries")
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")

  @@unique([userId, key])
  @@index([walletId])
}

model WalletLog {
  id           Int         @id @default(autoincrement())
  createdAt    DateTime    @default(now()) @map("created_at")
  userId       Int
  user         User        @relation(fields: [userId], references: [id], onDelete: Cascade)
  wallet       WalletType?
  level        LogLevel
  message      String
  invoiceId    Int?
  invoice      Invoice?    @relation(fields: [invoiceId], references: [id])
  withdrawalId Int?
  withdrawal   Withdrawl?  @relation(fields: [withdrawalId], references: [id])
  context      Json?       @db.JsonB

  @@index([userId, createdAt])
}

model WalletLightningAddress {
  id        Int      @id @default(autoincrement())
  walletId  Int      @unique
  wallet    Wallet   @relation(fields: [walletId], references: [id], onDelete: Cascade)
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  address   String
}

model WalletLND {
  id        Int      @id @default(autoincrement())
  walletId  Int      @unique
  wallet    Wallet   @relation(fields: [walletId], references: [id], onDelete: Cascade)
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  socket    String
  macaroon  String
  cert      String?
}

model WalletCLN {
  id        Int      @id @default(autoincrement())
  walletId  Int      @unique
  wallet    Wallet   @relation(fields: [walletId], references: [id], onDelete: Cascade)
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  socket    String
  rune      String
  cert      String?
}

model WalletLNbits {
  id         Int      @id @default(autoincrement())
  walletId   Int      @unique
  wallet     Wallet   @relation(fields: [walletId], references: [id], onDelete: Cascade)
  createdAt  DateTime @default(now()) @map("created_at")
  updatedAt  DateTime @default(now()) @updatedAt @map("updated_at")
  url        String
  invoiceKey String?
}

model WalletNWC {
  id         Int      @id @default(autoincrement())
  walletId   Int      @unique
  wallet     Wallet   @relation(fields: [walletId], references: [id], onDelete: Cascade)
  createdAt  DateTime @default(now()) @map("created_at")
  updatedAt  DateTime @default(now()) @updatedAt @map("updated_at")
  nwcUrlRecv String?
}

model WalletBlink {
  id           Int      @id @default(autoincrement())
  walletId     Int      @unique
  wallet       Wallet   @relation(fields: [walletId], references: [id], onDelete: Cascade)
  createdAt    DateTime @default(now()) @map("created_at")
  updatedAt    DateTime @default(now()) @updatedAt @map("updated_at")
  apiKeyRecv   String?
  currencyRecv String?
}

model WalletPhoenixd {
  id                Int      @id @default(autoincrement())
  walletId          Int      @unique
  wallet            Wallet   @relation(fields: [walletId], references: [id], onDelete: Cascade)
  createdAt         DateTime @default(now()) @map("created_at")
  updatedAt         DateTime @default(now()) @updatedAt @map("updated_at")
  url               String
  secondaryPassword String?
}

model Mute {
  muterId   Int
  mutedId   Int
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  muter     User     @relation("muter", fields: [muterId], references: [id], onDelete: Cascade)
  muted     User     @relation("muted", fields: [mutedId], references: [id], onDelete: Cascade)

  @@id([muterId, mutedId])
  @@index([mutedId, muterId])
}

model Arc {
  fromId   Int
  fromUser User  @relation("fromUser", fields: [fromId], references: [id], onDelete: Cascade)
  toId     Int
  toUser   User  @relation("toUser", fields: [toId], references: [id], onDelete: Cascade)
  zapTrust Float

  @@id([fromId, toId])
  @@index([toId, fromId])
}

enum StreakType {
  COWBOY_HAT
  GUN
  HORSE
}

model Streak {
  id        Int        @id @default(autoincrement())
  createdAt DateTime   @default(now()) @map("created_at")
  updatedAt DateTime   @default(now()) @updatedAt @map("updated_at")
  startedAt DateTime
  endedAt   DateTime?
  userId    Int
  type      StreakType @default(COWBOY_HAT)
  user      User       @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([startedAt, userId, type])
  @@index([userId], map: "Streak.userId_index")
  @@index([type])
}

model NostrRelay {
  addr      String           @id
  createdAt DateTime         @default(now()) @map("created_at")
  updatedAt DateTime         @default(now()) @updatedAt @map("updated_at")
  users     UserNostrRelay[]
}

model UserNostrRelay {
  userId         Int
  nostrRelayAddr String
  createdAt      DateTime   @default(now()) @map("created_at")
  updatedAt      DateTime   @default(now()) @updatedAt @map("updated_at")
  NostrRelay     NostrRelay @relation(fields: [nostrRelayAddr], references: [addr], onDelete: Cascade)
  User           User       @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@id([userId, nostrRelayAddr])
}

model Donation {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  sats      Int
  userId    Int
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model ItemUpload {
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  itemId    Int
  uploadId  Int
  item      Item     @relation(fields: [itemId], references: [id], onDelete: Cascade)
  upload    Upload   @relation(fields: [uploadId], references: [id], onDelete: Cascade)

  @@id([itemId, uploadId])
  @@index([createdAt])
  @@index([itemId])
  @@index([uploadId])
}

model Upload {
  id                 Int                 @id @default(autoincrement())
  createdAt          DateTime            @default(now()) @map("created_at")
  updatedAt          DateTime            @default(now()) @updatedAt @map("updated_at")
  type               String
  size               Int
  width              Int?
  height             Int?
  userId             Int
  paid               Boolean?
  invoiceId          Int?
  invoiceActionState InvoiceActionState?
  invoice            Invoice?            @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
  user               User                @relation("Uploads", fields: [userId], references: [id], onDelete: Cascade)
  User               User[]
  ItemUpload         ItemUpload[]

  @@index([createdAt], map: "Upload.created_at_index")
  @@index([userId], map: "Upload.userId_index")
  @@index([invoiceId])
  @@index([invoiceActionState])
}

model Earn {
  id        Int       @id @default(autoincrement())
  createdAt DateTime  @default(now()) @map("created_at")
  updatedAt DateTime  @default(now()) @updatedAt @map("updated_at")
  msats     BigInt
  userId    Int
  rank      Int?
  type      EarnType?
  typeId    Int?
  user      User      @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([createdAt], map: "Earn.created_at_index")
  @@index([createdAt, userId], map: "Earn.created_at_userId_index")
  @@index([userId], map: "Earn.userId_index")
}

model LnAuth {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  k1        String   @unique(map: "LnAuth.k1_unique")
  pubkey    String?
}

model LnWith {
  id           Int      @id @default(autoincrement())
  createdAt    DateTime @default(now()) @map("created_at")
  updatedAt    DateTime @default(now()) @updatedAt @map("updated_at")
  k1           String   @unique(map: "LnWith.k1_unique")
  userId       Int
  withdrawalId Int?
}

model Invite {
  id          String   @id @default(dbgenerated("encode(gen_random_bytes(16), 'hex'::text)"))
  createdAt   DateTime @default(now()) @map("created_at")
  updatedAt   DateTime @default(now()) @updatedAt @map("updated_at")
  userId      Int
  gift        Int?
  limit       Int?
  giftedCount Int      @default(0)
  revoked     Boolean  @default(false)
  user        User     @relation("Invites", fields: [userId], references: [id], onDelete: Cascade)
  invitees    User[]

  description String?

  @@index([createdAt], map: "Invite.created_at_index")
  @@index([userId], map: "Invite.userId_index")
}

model Message {
  id     Int    @id @default(autoincrement())
  text   String
  userId Int
  user   User   @relation(fields: [userId], references: [id], onDelete: Cascade)
}

/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info.
model Item {
  id                   Int                   @id @default(autoincrement())
  createdAt            DateTime              @default(now()) @map("created_at")
  updatedAt            DateTime              @default(now()) @updatedAt @map("updated_at")
  title                String?
  text                 String?
  url                  String?
  userId               Int
  parentId             Int?
  path                 Unsupported("ltree")?
  pinId                Int?
  latitude             Float?
  location             String?
  longitude            Float?
  maxBid               Int?
  maxSalary            Int?
  minSalary            Int?
  remote               Boolean?
  subName              String?               @db.Citext
  statusUpdatedAt      DateTime?
  status               Status                @default(ACTIVE)
  company              String?
  weightedVotes        Float                 @default(0)
  subWeightedVotes     Float                 @default(0)
  boost                Int                   @default(0)
  oldBoost             Int                   @default(0)
  pollCost             Int?
  paidImgLink          Boolean               @default(false)
  commentMsats         BigInt                @default(0)
  commentMcredits      BigInt                @default(0)
  lastCommentAt        DateTime?
  lastZapAt            DateTime?
  ncomments            Int                   @default(0)
  nDirectComments      Int                   @default(0)
  msats                BigInt                @default(0)
  mcredits             BigInt                @default(0)
  cost                 Int                   @default(0)
  weightedDownVotes    Float                 @default(0)
  subWeightedDownVotes Float                 @default(0)
  bio                  Boolean               @default(false)
  freebie              Boolean               @default(false)
  deletedAt            DateTime?
  otsFile              Bytes?
  otsHash              String?
  imgproxyUrls         Json?
  bounty               Int?
  noteId               String?               @unique(map: "Item.noteId_unique")
  rootId               Int?
  bountyPaidTo         Int[]
  upvotes              Int                   @default(0)
  weightedComments     Float                 @default(0)
  Bookmark             Bookmark[]
  parent               Item?                 @relation("ParentChildren", fields: [parentId], references: [id])
  children             Item[]                @relation("ParentChildren")
  pin                  Pin?                  @relation(fields: [pinId], references: [id])
  root                 Item?                 @relation("RootDescendant", fields: [rootId], references: [id])
  descendants          Item[]                @relation("RootDescendant")
  sub                  Sub?                  @relation(fields: [subName], references: [name], onDelete: Cascade, onUpdate: Cascade)
  user                 User                  @relation("UserItems", fields: [userId], references: [id], onDelete: Cascade)
  itemActs             ItemAct[]
  mentions             Mention[]
  itemReferrers        ItemMention[]         @relation("referrer")
  itemReferees         ItemMention[]         @relation("referee")
  pollOptions          PollOption[]
  PollVote             PollVote[]
  threadSubscriptions  ThreadSubscription[]
  User                 User[]
  itemForwards         ItemForward[]
  itemUploads          ItemUpload[]
  uploadId             Int?
  invoiceId            Int?
  invoiceActionState   InvoiceActionState?
  invoicePaidAt        DateTime?
  outlawed             Boolean               @default(false)
  apiKey               Boolean               @default(false)
  pollExpiresAt        DateTime?
  Ancestors            Reply[]               @relation("AncestorReplyItem")
  Replies              Reply[]
  Reminder             Reminder[]
  invoice              Invoice?              @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
  PollBlindVote        PollBlindVote[]
  ItemUserAgg          ItemUserAgg[]
  AutoSocialPost       AutoSocialPost[]
  randPollOptions      Boolean               @default(false)

  @@index([uploadId])
  @@index([lastZapAt])
  @@index([bio], map: "Item.bio_index")
  @@index([createdAt], map: "Item.created_at_index")
  @@index([freebie], map: "Item.freebie_index")
  @@index([maxBid], map: "Item.maxBid_index")
  @@index([parentId], map: "Item.parentId_index")
  @@index([path], map: "Item.path_index", type: Gist)
  @@index([path], map: "Item.path_index0", type: Gist)
  @@index([pinId], map: "Item.pinId_index")
  @@index([rootId], map: "Item.rootId_index")
  @@index([statusUpdatedAt], map: "Item.statusUpdatedAt_index")
  @@index([status], map: "Item.status_index")
  @@index([subName], map: "Item.subName_index")
  @@index([userId], map: "Item.userId_index")
  @@index([weightedDownVotes], map: "Item.weightedDownVotes_index")
  @@index([weightedVotes], map: "Item.weightedVotes_index")
  @@index([invoiceId])
  @@index([invoiceActionState])
  @@index([cost])
  @@index([url])
  @@index([boost])
  @@index([invoicePaidAt])
}

// we use this to denormalize a user's aggregated interactions (zaps) with an item
// we need to do this to safely modify the aggregates in a read committed transaction
// because we can't lock an aggregate query to guard a potentially conflicting update
// (e.g. sum("ItemAct".msats), but we can lock a row where we store the aggregate
// this is important because zaps do not update "weightedVotes" linearly
// see: https://stackoverflow.com/questions/61781595/postgres-read-commited-doesnt-re-read-updated-row?noredirect=1#comment109279507_61781595
// or: https://www.cybertec-postgresql.com/en/transaction-anomalies-with-select-for-update/
model ItemUserAgg {
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now()) @map("created_at")
  updatedAt   DateTime @default(now()) @updatedAt @map("updated_at")
  itemId      Int
  userId      Int
  zapSats     BigInt   @default(0)
  downZapSats BigInt   @default(0)
  item        Item     @relation(fields: [itemId], references: [id], onDelete: Cascade)
  user        User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([itemId, userId])
  @@index([itemId])
  @@index([userId])
  @@index([createdAt])
}

// record auto social posts so we can avoid duplicates
model AutoSocialPost {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  itemId    Int
  item      Item     @relation(fields: [itemId], references: [id], onDelete: Cascade)

  @@index([itemId])
  @@index([createdAt])
}

// this is a denomalized table that is used to make reply notifications
// more efficient ... it is populated by a trigger when replies are created
model Reply {
  id             Int      @id @default(autoincrement())
  createdAt      DateTime @default(now()) @map("created_at")
  updatedAt      DateTime @default(now()) @updatedAt @map("updated_at")
  ancestorId     Int
  ancestorUserId Int
  itemId         Int
  userId         Int
  level          Int
  User           User     @relation(fields: [userId], references: [id])
  Item           Item     @relation(fields: [itemId], references: [id])
  AncestorUser   User     @relation("AncestorReplyUser", fields: [ancestorUserId], references: [id])
  AncestorItem   Item     @relation("AncestorReplyItem", fields: [ancestorId], references: [id])

  @@index([ancestorId])
  @@index([ancestorUserId])
  @@index([itemId])
  @@index([userId])
  @@index([level])
  @@index([createdAt])
}

// TODO: make all Item's forward 100% of sats to the OP by default
// so that forwards aren't a special case everywhere
model ItemForward {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  itemId    Int // The item from which sats are forwarded
  userId    Int // The recipient of the forwarded sats
  pct       Int // The percentage of sats from the item to forward to this user
  item      Item     @relation(fields: [itemId], references: [id], onDelete: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([itemId], map: "ItemForward.itemId_index")
  @@index([userId], map: "ItemForward.userId_index")
  @@index([createdAt], map: "ItemForward.createdAt_index")
}

model PollOption {
  id        Int        @id @default(autoincrement())
  createdAt DateTime   @default(now()) @map("created_at")
  updatedAt DateTime   @default(now()) @updatedAt @map("updated_at")
  itemId    Int
  option    String
  item      Item       @relation(fields: [itemId], references: [id], onDelete: Cascade)
  PollVote  PollVote[]

  @@index([itemId], map: "PollOption.itemId_index")
}

model PollVote {
  id                 Int                 @id @default(autoincrement())
  createdAt          DateTime            @default(now()) @map("created_at")
  updatedAt          DateTime            @default(now()) @updatedAt @map("updated_at")
  itemId             Int
  pollOptionId       Int
  invoiceId          Int?
  invoiceActionState InvoiceActionState?
  invoice            Invoice?            @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
  item               Item                @relation(fields: [itemId], references: [id], onDelete: Cascade)
  pollOption         PollOption          @relation(fields: [pollOptionId], references: [id], onDelete: Cascade)

  @@index([pollOptionId], map: "PollVote.pollOptionId_index")
  @@index([invoiceId])
  @@index([invoiceActionState])
}

model PollBlindVote {
  id                 Int                 @id @default(autoincrement())
  createdAt          DateTime            @default(now()) @map("created_at")
  updatedAt          DateTime            @default(now()) @updatedAt @map("updated_at")
  itemId             Int
  userId             Int
  invoiceId          Int?
  invoiceActionState InvoiceActionState?
  invoice            Invoice?            @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
  item               Item                @relation(fields: [itemId], references: [id], onDelete: Cascade)
  user               User                @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([itemId, userId], map: "PollBlindVote.itemId_userId_unique")
  @@index([userId], map: "PollBlindVote.userId_index")
  @@index([invoiceActionState])
}

enum BillingType {
  MONTHLY
  YEARLY
  ONCE
}

enum RankingType {
  WOT
  RECENT
  AUCTION
}

model Sub {
  name       String                @id @db.Citext
  createdAt  DateTime              @default(now()) @map("created_at")
  updatedAt  DateTime              @default(now()) @updatedAt @map("updated_at")
  userId     Int
  parentName String?               @db.Citext
  path       Unsupported("ltree")?

  postTypes        PostType[]
  rankingType      RankingType
  allowFreebies    Boolean     @default(true)
  baseCost         Int         @default(1)
  replyCost        Int         @default(1)
  rewardsPct       Int         @default(30)
  desc             String?
  status           Status      @default(ACTIVE)
  statusUpdatedAt  DateTime?
  billingType      BillingType
  billingCost      Int
  billingAutoRenew Boolean     @default(false)
  billedLastAt     DateTime    @default(now())
  billPaidUntil    DateTime?
  moderated        Boolean     @default(false)
  moderatedCount   Int         @default(0)
  nsfw             Boolean     @default(false)

  parent            Sub?                @relation("ParentChildren", fields: [parentName], references: [name])
  children          Sub[]               @relation("ParentChildren")
  user              User                @relation(fields: [userId], references: [id], onDelete: Cascade)
  Item              Item[]
  SubAct            SubAct[]
  MuteSub           MuteSub[]
  SubSubscription   SubSubscription[]
  TerritoryTransfer TerritoryTransfer[]
  UserSubTrust      UserSubTrust[]

  @@index([parentName])
  @@index([createdAt])
  @@index([userId])
  @@index([statusUpdatedAt])
  @@index([path], type: Gist)
}

model SubAct {
  id        Int        @id @default(autoincrement())
  createdAt DateTime   @default(now()) @map("created_at")
  updatedAt DateTime   @default(now()) @updatedAt @map("updated_at")
  userId    Int
  subName   String     @db.Citext
  msats     BigInt
  type      SubActType
  user      User       @relation(fields: [userId], references: [id], onDelete: Cascade)
  sub       Sub        @relation(fields: [subName], references: [name], onDelete: Cascade, onUpdate: Cascade)

  @@index([userId])
  @@index([userId, type])
  @@index([type])
  @@index([createdAt])
  @@index([createdAt, type])
  @@index([userId, createdAt, type])
}

model MuteSub {
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  subName   String   @db.Citext
  userId    Int
  sub       Sub      @relation(fields: [subName], references: [name], onDelete: Cascade, onUpdate: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@id([userId, subName])
  @@index([subName])
  @@index([createdAt])
}

model Pin {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  cron      String?
  timezone  String?
  position  Int
  Item      Item[]
}

model ReferralAct {
  id         Int      @id @default(autoincrement())
  createdAt  DateTime @default(now()) @map("created_at")
  updatedAt  DateTime @default(now()) @updatedAt @map("updated_at")
  referrerId Int
  itemActId  Int
  msats      BigInt
  itemAct    ItemAct  @relation(fields: [itemActId], references: [id], onDelete: Cascade)
  referrer   User     @relation(fields: [referrerId], references: [id], onDelete: Cascade)

  @@index([referrerId])
  @@index([itemActId])
}

model ItemAct {
  id                 Int                 @id(map: "Vote_pkey") @default(autoincrement())
  createdAt          DateTime            @default(now()) @map("created_at")
  updatedAt          DateTime            @default(now()) @updatedAt @map("updated_at")
  msats              BigInt
  act                ItemActType
  itemId             Int
  userId             Int
  invoiceId          Int?
  invoiceActionState InvoiceActionState?

  invoice     Invoice?      @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
  item        Item          @relation(fields: [itemId], references: [id], onDelete: Cascade)
  user        User          @relation(fields: [userId], references: [id], onDelete: Cascade)
  ReferralAct ReferralAct[]

  @@index([act], map: "ItemAct.act_index")
  @@index([createdAt], map: "ItemAct.created_at_index")
  @@index([createdAt, itemId, act], map: "ItemAct.created_at_itemId_act_index")
  @@index([itemId, createdAt, act], map: "ItemAct.itemId_created_at_act_index")
  @@index([itemId], map: "ItemAct.itemId_index")
  @@index([itemId, userId, act], map: "ItemAct.itemId_userId_act_index")
  @@index([userId, createdAt, act], map: "ItemAct.userId_created_at_act_index")
  @@index([userId], map: "ItemAct.userId_index")
  @@index([itemId], map: "Vote.itemId_index")
  @@index([userId], map: "Vote.userId_index")
  @@index([invoiceId])
  @@index([invoiceActionState])
}

model Mention {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  itemId    Int
  userId    Int
  item      Item     @relation(fields: [itemId], references: [id], onDelete: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([itemId, userId], map: "Mention.itemId_userId_unique")
  @@index([createdAt], map: "Mention.created_at_index")
  @@index([itemId], map: "Mention.itemId_index")
  @@index([userId], map: "Mention.userId_index")
}

enum InvoiceActionType {
  BUY_CREDITS
  ITEM_CREATE
  ITEM_UPDATE
  ZAP
  DOWN_ZAP
  BOOST
  DONATE
  POLL_VOTE
  TERRITORY_CREATE
  TERRITORY_UPDATE
  TERRITORY_BILLING
  TERRITORY_UNARCHIVE
  RECEIVE
}

enum InvoiceActionState {
  PENDING
  PENDING_HELD
  HELD
  PAID
  FAILED
  FORWARDING
  FORWARDED
  FAILED_FORWARD
  RETRYING
  CANCELING
}

model ItemMention {
  id           Int      @id @default(autoincrement())
  createdAt    DateTime @default(now()) @map("created_at")
  updatedAt    DateTime @default(now()) @updatedAt @map("updated_at")
  referrerId   Int
  refereeId    Int
  referrerItem Item     @relation("referrer", fields: [referrerId], references: [id], onDelete: Cascade)
  refereeItem  Item     @relation("referee", fields: [refereeId], references: [id], onDelete: Cascade)

  @@unique([referrerId, refereeId], map: "ItemMention.referrerId_refereeId_unique")
  @@index([createdAt], map: "ItemMention.created_at_index")
  @@index([referrerId], map: "ItemMention.referrerId_index")
  @@index([refereeId], map: "ItemMention.refereeId_index")
}

model Invoice {
  id                 Int                 @id @default(autoincrement())
  createdAt          DateTime            @default(now()) @map("created_at")
  updatedAt          DateTime            @default(now()) @updatedAt @map("updated_at")
  userId             Int
  hash               String              @unique(map: "Invoice.hash_unique")
  preimage           String?             @unique(map: "Invoice.preimage_unique")
  isHeld             Boolean?
  bolt11             String
  expiresAt          DateTime
  confirmedAt        DateTime?
  confirmedIndex     BigInt?
  cancelled          Boolean             @default(false)
  cancelledAt        DateTime?
  userCancel         Boolean?
  paymentAttempt     Int                 @default(0)
  retryPendingSince  DateTime?
  msatsRequested     BigInt
  msatsReceived      BigInt?
  desc               String?
  comment            String?
  lud18Data          Json?
  user               User                @relation(fields: [userId], references: [id], onDelete: Cascade)
  invoiceForward     InvoiceForward?
  predecessorId      Int?                @unique(map: "Invoice.predecessorId_unique")
  predecessorInvoice Invoice?            @relation("PredecessorInvoice", fields: [predecessorId], references: [id], onDelete: Cascade)
  successorInvoice   Invoice?            @relation("PredecessorInvoice")
  actionState        InvoiceActionState?
  actionType         InvoiceActionType?
  actionOptimistic   Boolean?
  actionId           Int?
  actionArgs         Json?               @db.JsonB
  actionError        String?
  actionResult       Json?               @db.JsonB
  ItemAct            ItemAct[]
  Item               Item[]
  Upload             Upload[]
  PollVote           PollVote[]
  PollBlindVote      PollBlindVote[]
  WalletLog          WalletLog[]

  @@index([createdAt], map: "Invoice.created_at_index")
  @@index([userId], map: "Invoice.userId_index")
  @@index([confirmedIndex], map: "Invoice.confirmedIndex_index")
  @@index([isHeld])
  @@index([confirmedAt])
  @@index([cancelledAt])
  @@index([actionType])
  @@index([actionState])
}

model DirectPayment {
  id         Int      @id @default(autoincrement())
  createdAt  DateTime @default(now()) @map("created_at")
  updatedAt  DateTime @default(now()) @updatedAt @map("updated_at")
  senderId   Int?
  receiverId Int?
  preimage   String?  @unique
  bolt11     String?
  hash       String?  @unique
  desc       String?
  comment    String?
  lud18Data  Json?
  msats      BigInt
  walletId   Int?
  sender     User?    @relation("DirectPaymentSent", fields: [senderId], references: [id], onDelete: Cascade)
  receiver   User?    @relation("DirectPaymentReceived", fields: [receiverId], references: [id], onDelete: Cascade)
  wallet     Wallet?  @relation(fields: [walletId], references: [id], onDelete: SetNull)

  @@index([createdAt])
  @@index([senderId])
  @@index([receiverId])
}

model InvoiceForward {
  id          Int      @id @default(autoincrement())
  createdAt   DateTime @default(now()) @map("created_at")
  updatedAt   DateTime @default(now()) @updatedAt @map("updated_at")
  bolt11      String
  maxFeeMsats Int
  walletId    Int

  // we get these values when the invoice is held
  expiryHeight Int?
  acceptHeight Int?

  // we get these values when the outgoing invoice is settled
  invoiceId   Int  @unique
  withdrawlId Int? @unique

  invoice   Invoice    @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
  wallet    Wallet     @relation(fields: [walletId], references: [id], onDelete: Cascade)
  withdrawl Withdrawl? @relation(fields: [withdrawlId], references: [id], onDelete: SetNull)

  @@index([invoiceId])
  @@index([walletId])
  @@index([withdrawlId])
}

model Withdrawl {
  id             Int              @id @default(autoincrement())
  createdAt      DateTime         @default(now()) @map("created_at")
  updatedAt      DateTime         @default(now()) @updatedAt @map("updated_at")
  userId         Int
  hash           String?
  preimage       String?
  bolt11         String?
  msatsPaying    BigInt
  msatsPaid      BigInt?
  msatsFeePaying BigInt
  msatsFeePaid   BigInt?
  status         WithdrawlStatus?
  autoWithdraw   Boolean          @default(false)
  walletId       Int?
  user           User             @relation(fields: [userId], references: [id], onDelete: Cascade)
  wallet         Wallet?          @relation(fields: [walletId], references: [id], onDelete: SetNull)
  invoiceForward InvoiceForward?
  WalletLog      WalletLog[]

  @@index([createdAt], map: "Withdrawl.created_at_index")
  @@index([userId], map: "Withdrawl.userId_index")
  @@index([hash])
  @@index([walletId])
  @@index([autoWithdraw])
  @@index([status])
}

model Account {
  id                Int      @id @default(autoincrement())
  createdAt         DateTime @default(now()) @map("created_at")
  updatedAt         DateTime @default(now()) @updatedAt @map("updated_at")
  userId            Int      @map("user_id")
  type              String   @map("provider_type")
  provider          String   @map("provider_id")
  providerAccountId String   @map("provider_account_id")
  refresh_token     String?  @map("refresh_token")
  access_token      String?  @map("access_token")
  expires_at        String?  @map("access_token_expires")
  token_type        String?
  scope             String?
  id_token          String?
  session_state     String?

  // twitter oauth 1.0 needs these https://authjs.dev/reference/core/providers_twitter#notes
  oauth_token        String?
  oauth_token_secret String?

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

  @@unique([provider, providerAccountId])
  @@index([userId], map: "accounts.user_id_index")
  @@map("accounts")
}

model OFAC {
  id          Int                      @id @default(autoincrement())
  startIP     Unsupported("ipaddress")
  endIP       Unsupported("ipaddress")
  country     String
  countryCode String
}

model Session {
  id           Int      @id @default(autoincrement())
  sessionToken String   @unique(map: "sessions.session_token_unique") @map("session_token")
  createdAt    DateTime @default(now()) @map("created_at")
  updatedAt    DateTime @default(now()) @updatedAt @map("updated_at")
  userId       Int      @map("user_id")
  expires      DateTime

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

  @@map("sessions")
}

model VerificationToken {
  id         Int      @id @default(autoincrement())
  createdAt  DateTime @default(now()) @map("created_at")
  updatedAt  DateTime @default(now()) @updatedAt @map("updated_at")
  identifier String
  token      String   @unique(map: "verification_requests.token_unique")
  expires    DateTime
  attempts   Int      @default(0)

  @@unique([identifier, token])
  @@map("verification_requests")
}

model Bookmark {
  userId    Int
  itemId    Int
  createdAt DateTime @default(now()) @map("created_at")
  item      Item     @relation(fields: [itemId], references: [id], onDelete: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@id([userId, itemId])
  @@index([createdAt], map: "Bookmark.created_at_index")
}

// TODO: make thread subscriptions for OP by default so they can
// unsubscribe from their own threads and its not a special case
model ThreadSubscription {
  userId    Int
  itemId    Int
  createdAt DateTime @default(now()) @map("created_at")
  item      Item     @relation(fields: [itemId], references: [id], onDelete: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@id([userId, itemId])
  @@index([createdAt], map: "ThreadSubscription.created_at_index")
}

model UserSubscription {
  followerId           Int
  followeeId           Int
  createdAt            DateTime  @default(now()) @map("created_at")
  updatedAt            DateTime  @default(now()) @updatedAt @map("updated_at")
  postsSubscribedAt    DateTime?
  commentsSubscribedAt DateTime?
  follower             User      @relation("follower", fields: [followerId], references: [id], onDelete: Cascade)
  followee             User      @relation("followee", fields: [followeeId], references: [id], onDelete: Cascade)

  @@id([followerId, followeeId])
  @@index([createdAt], map: "UserSubscription.created_at_index")
  @@index([followerId], map: "UserSubscription.follower_index")
  @@index([followeeId], map: "UserSubscription.followee_index")
}

model SubSubscription {
  userId    Int
  subName   String   @db.Citext
  createdAt DateTime @default(now()) @map("created_at")
  sub       Sub      @relation(fields: [subName], references: [name], onDelete: Cascade, onUpdate: Cascade)
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: Cascade)

  @@id([userId, subName])
  @@index([createdAt], map: "SubSubscription.created_at_index")
}

model PushSubscription {
  id        Int      @id @default(autoincrement())
  userId    Int
  endpoint  String
  p256dh    String
  auth      String
  createdAt DateTime @default(now()) @map("created_at")
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId], map: "PushSubscription.userId_index")
}

model Log {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  level     LogLevel
  name      String
  message   String
  env       Json?
  context   Json?

  @@index([createdAt, name], map: "Log.name_index")
}

model TerritoryTransfer {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  oldUserId Int
  newUserId Int
  subName   String   @db.Citext
  oldUser   User     @relation("TerritoryTransfer_oldUser", fields: [oldUserId], references: [id], onDelete: Cascade)
  newUser   User     @relation("TerritoryTransfer_newUser", fields: [newUserId], references: [id], onDelete: Cascade)
  sub       Sub      @relation(fields: [subName], references: [name], onDelete: Cascade)

  @@index([createdAt, newUserId], map: "TerritoryTransfer.newUserId_index")
  @@index([createdAt, oldUserId], map: "TerritoryTransfer.oldUserId_index")
}

model Reminder {
  id        Int      @id @default(autoincrement())
  createdAt DateTime @default(now()) @map("created_at")
  userId    Int
  itemId    Int
  remindAt  DateTime
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  item      Item     @relation(fields: [itemId], references: [id], onDelete: Cascade)

  @@index([userId, remindAt], map: "Reminder.userId_reminderAt_index")
}

enum EarnType {
  POST
  COMMENT
  TIP_COMMENT
  TIP_POST
  FOREVER_REFERRAL
  ONE_DAY_REFERRAL
}

enum SubActType {
  BILLING
  REVENUE
}

enum Status {
  ACTIVE
  STOPPED
  NOSATS
  GRACE
}

enum PostType {
  LINK
  DISCUSSION
  JOB
  POLL
  BOUNTY
}

enum ItemActType {
  VOTE
  BOOST
  TIP
  STREAM
  POLL
  DONT_LIKE_THIS
  FEE
}

enum WithdrawlStatus {
  INSUFFICIENT_BALANCE
  INVALID_PAYMENT
  PATHFINDING_TIMEOUT
  ROUTE_NOT_FOUND
  CONFIRMED
  UNKNOWN_FAILURE
}

enum LogLevel {
  DEBUG
  INFO
  WARN
  ERROR
  SUCCESS
}
