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

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

// ============================================
// 钱包账户表 (状态表)
// ============================================
model WalletAccount {
  id              BigInt   @id @default(autoincrement()) @map("wallet_id")
  accountSequence String   @unique @map("account_sequence") @db.VarChar(20)  // 跨服务关联标识 (全局唯一业务ID)
  userId          BigInt   @unique @map("user_id")  // 保留兼容

  // USDT 余额
  usdtAvailable   Decimal  @default(0) @map("usdt_available") @db.Decimal(20, 8)
  usdtFrozen      Decimal  @default(0) @map("usdt_frozen") @db.Decimal(20, 8)

  // DST 余额
  dstAvailable    Decimal  @default(0) @map("dst_available") @db.Decimal(20, 8)
  dstFrozen       Decimal  @default(0) @map("dst_frozen") @db.Decimal(20, 8)

  // BNB 余额
  bnbAvailable    Decimal  @default(0) @map("bnb_available") @db.Decimal(20, 8)
  bnbFrozen       Decimal  @default(0) @map("bnb_frozen") @db.Decimal(20, 8)

  // OG 余额
  ogAvailable     Decimal  @default(0) @map("og_available") @db.Decimal(20, 8)
  ogFrozen        Decimal  @default(0) @map("og_frozen") @db.Decimal(20, 8)

  // RWAD 余额
  rwadAvailable   Decimal  @default(0) @map("rwad_available") @db.Decimal(20, 8)
  rwadFrozen      Decimal  @default(0) @map("rwad_frozen") @db.Decimal(20, 8)

  // 算力
  hashpower       Decimal  @default(0) @map("hashpower") @db.Decimal(20, 8)

  // 待领取收益
  pendingUsdt         Decimal   @default(0) @map("pending_usdt") @db.Decimal(20, 8)
  pendingHashpower    Decimal   @default(0) @map("pending_hashpower") @db.Decimal(20, 8)
  pendingExpireAt     DateTime? @map("pending_expire_at")

  // 可结算收益
  settleableUsdt      Decimal  @default(0) @map("settleable_usdt") @db.Decimal(20, 8)
  settleableHashpower Decimal  @default(0) @map("settleable_hashpower") @db.Decimal(20, 8)

  // 已结算总额
  settledTotalUsdt      Decimal  @default(0) @map("settled_total_usdt") @db.Decimal(20, 8)
  settledTotalHashpower Decimal  @default(0) @map("settled_total_hashpower") @db.Decimal(20, 8)

  // 已过期总额
  expiredTotalUsdt      Decimal  @default(0) @map("expired_total_usdt") @db.Decimal(20, 8)
  expiredTotalHashpower Decimal  @default(0) @map("expired_total_hashpower") @db.Decimal(20, 8)

  // 状态
  status    String   @default("ACTIVE") @map("status") @db.VarChar(20)

  // 是否已认种过（认种后分享权益直接进入可结算）
  hasPlanted Boolean @default(false) @map("has_planted")

  // 乐观锁版本号
  version   Int      @default(0) @map("version")

  createdAt DateTime @default(now()) @map("created_at")
  updatedAt DateTime @updatedAt @map("updated_at")

  @@map("wallet_accounts")
  @@index([userId])
  @@index([usdtAvailable(sort: Desc)])
  @@index([hashpower(sort: Desc)])
  @@index([status])
}

// ============================================
// 账本流水表 (行为表, append-only)
// ============================================
model LedgerEntry {
  id              BigInt   @id @default(autoincrement()) @map("entry_id")
  accountSequence String   @map("account_sequence") @db.VarChar(20)  // 跨服务关联标识
  userId          BigInt   @map("user_id")  // 保留兼容

  // 流水类型
  entryType String   @map("entry_type") @db.VarChar(50)

  // 金额变动 (正数入账, 负数支出)
  amount    Decimal  @map("amount") @db.Decimal(20, 8)
  assetType String   @map("asset_type") @db.VarChar(20)

  // 余额快照 (操作后余额)
  balanceAfter Decimal? @map("balance_after") @db.Decimal(20, 8)

  // 关联引用
  refOrderId String? @map("ref_order_id") @db.VarChar(100)
  refTxHash  String? @map("ref_tx_hash") @db.VarChar(100)

  // 备注 (使用 Text 类型，无长度限制)
  memo String? @map("memo") @db.Text

  // 扩展数据
  payloadJson Json? @map("payload_json")

  createdAt DateTime @default(now()) @map("created_at")

  @@map("wallet_ledger_entries")
  @@index([accountSequence, createdAt(sort: Desc)])
  @@index([userId, createdAt(sort: Desc)])
  @@index([entryType])
  @@index([assetType])
  @@index([refOrderId])
  @@index([refTxHash])
  @@index([createdAt])
}

// ============================================
// 充值订单表
// ============================================
model DepositOrder {
  id              BigInt   @id @default(autoincrement()) @map("order_id")
  accountSequence String   @map("account_sequence") @db.VarChar(20)  // 跨服务关联标识
  userId          BigInt   @map("user_id")  // 保留兼容

  // 充值信息
  chainType String   @map("chain_type") @db.VarChar(20)
  amount    Decimal  @map("amount") @db.Decimal(20, 8)
  txHash    String   @unique @map("tx_hash") @db.VarChar(100)

  // 状态
  status      String    @default("PENDING") @map("status") @db.VarChar(20)
  confirmedAt DateTime? @map("confirmed_at")

  createdAt DateTime @default(now()) @map("created_at")

  @@map("deposit_orders")
  @@index([accountSequence])
  @@index([userId])
  @@index([txHash])
  @@index([status])
  @@index([chainType])
}

// ============================================
// 结算订单表
// ============================================
model SettlementOrder {
  id        BigInt   @id @default(autoincrement()) @map("order_id")
  userId    BigInt   @map("user_id")

  // 结算信息
  usdtAmount      Decimal @map("usdt_amount") @db.Decimal(20, 8)
  settleCurrency  String  @map("settle_currency") @db.VarChar(10)

  // SWAP 信息
  swapTxHash     String?  @map("swap_tx_hash") @db.VarChar(100)
  receivedAmount Decimal? @map("received_amount") @db.Decimal(20, 8)

  // 状态
  status    String    @default("PENDING") @map("status") @db.VarChar(20)
  settledAt DateTime? @map("settled_at")

  createdAt DateTime @default(now()) @map("created_at")

  @@map("settlement_orders")
  @@index([userId])
  @@index([status])
  @@index([settleCurrency])
  @@index([createdAt])
}

// ============================================
// 提现订单表
// ============================================
model WithdrawalOrder {
  id              BigInt   @id @default(autoincrement()) @map("order_id")
  orderNo         String   @unique @map("order_no") @db.VarChar(50)
  accountSequence String   @map("account_sequence") @db.VarChar(20)  // 跨服务关联标识
  userId          BigInt   @map("user_id")

  // 提现信息
  amount    Decimal @map("amount") @db.Decimal(20, 8)    // 提现金额
  fee       Decimal @map("fee") @db.Decimal(20, 8)       // 手续费
  chainType String  @map("chain_type") @db.VarChar(20)   // 目标链 (BSC/KAVA)
  toAddress String  @map("to_address") @db.VarChar(100)  // 提现目标地址

  // 交易信息
  txHash    String? @map("tx_hash") @db.VarChar(100)     // 链上交易哈希

  // 内部转账标识
  isInternalTransfer Boolean @default(false) @map("is_internal_transfer")  // 是否为内部转账（ID转ID）
  toAccountSequence  String? @map("to_account_sequence") @db.VarChar(20)   // 接收方ID（内部转账时有值）
  toUserId           BigInt? @map("to_user_id")                            // 接收方用户ID（内部转账时有值）

  // 状态
  status       String    @default("PENDING") @map("status") @db.VarChar(20)
  errorMessage String?   @map("error_message") @db.VarChar(500)

  // 时间戳
  frozenAt      DateTime? @map("frozen_at")
  broadcastedAt DateTime? @map("broadcasted_at")
  confirmedAt   DateTime? @map("confirmed_at")
  createdAt     DateTime  @default(now()) @map("created_at")

  @@map("withdrawal_orders")
  @@index([accountSequence])
  @@index([userId])
  @@index([status])
  @@index([chainType])
  @@index([txHash])
  @@index([createdAt])
}

// ============================================
// 待领取奖励表 (逐笔记录)
// 每笔待领取奖励独立跟踪过期时间
// ============================================
model PendingReward {
  id              BigInt   @id @default(autoincrement()) @map("pending_reward_id")
  accountSequence String   @map("account_sequence") @db.VarChar(20)  // 跨服务关联标识
  userId          BigInt   @map("user_id")  // 保留兼容

  // 奖励金额
  usdtAmount      Decimal  @map("usdt_amount") @db.Decimal(20, 8)
  hashpowerAmount Decimal  @map("hashpower_amount") @db.Decimal(20, 8)

  // 来源信息
  sourceOrderId   String   @map("source_order_id") @db.VarChar(100)   // 触发奖励的认种订单
  allocationType  String   @map("allocation_type") @db.VarChar(50)    // 分配类型 (SHARE_RIGHT, TEAM_RIGHT 等)

  // 过期时间
  expireAt        DateTime @map("expire_at")

  // 状态: PENDING(待领取) / SETTLED(已结算) / EXPIRED(已过期)
  status          String   @default("PENDING") @map("status") @db.VarChar(20)

  // 结算/过期时间
  settledAt       DateTime? @map("settled_at")
  expiredAt       DateTime? @map("expired_at")

  createdAt       DateTime @default(now()) @map("created_at")

  @@map("pending_rewards")
  @@index([accountSequence, status])
  @@index([userId, status])
  @@index([status, expireAt])  // 用于定时任务查询过期奖励
  @@index([sourceOrderId])
  @@index([createdAt])
}

// ============================================
// 法币提现订单表
// 用于法币提现 (银行卡/支付宝/微信)
// 与 WithdrawalOrder (区块链划转) 分开管理
// ============================================
model FiatWithdrawalOrder {
  id              BigInt   @id @default(autoincrement()) @map("order_id")
  orderNo         String   @unique @map("order_no") @db.VarChar(50)
  accountSequence String   @map("account_sequence") @db.VarChar(20)
  userId          BigInt   @map("user_id")

  // 提现金额信息
  amount    Decimal @map("amount") @db.Decimal(20, 8)    // 提现金额 (绿积分, 1:1 人民币)
  fee       Decimal @map("fee") @db.Decimal(20, 8)       // 手续费

  // 收款方式: BANK_CARD / ALIPAY / WECHAT
  paymentMethod String @map("payment_method") @db.VarChar(20)

  // 银行卡信息
  bankName       String? @map("bank_name") @db.VarChar(100)
  bankCardNo     String? @map("bank_card_no") @db.VarChar(50)
  cardHolderName String? @map("card_holder_name") @db.VarChar(100)

  // 支付宝信息
  alipayAccount  String? @map("alipay_account") @db.VarChar(100)
  alipayRealName String? @map("alipay_real_name") @db.VarChar(100)

  // 微信信息
  wechatAccount  String? @map("wechat_account") @db.VarChar(100)
  wechatRealName String? @map("wechat_real_name") @db.VarChar(100)

  // 状态: PENDING -> FROZEN -> REVIEWING -> APPROVED -> PAYING -> COMPLETED
  //       或 REJECTED / FAILED / CANCELLED
  status       String    @default("PENDING") @map("status") @db.VarChar(20)
  errorMessage String?   @map("error_message") @db.VarChar(500)

  // 审核信息
  reviewedBy   String?   @map("reviewed_by") @db.VarChar(100)
  reviewedAt   DateTime? @map("reviewed_at")
  reviewRemark String?   @map("review_remark") @db.VarChar(500)

  // 打款信息
  paidBy        String?   @map("paid_by") @db.VarChar(100)
  paidAt        DateTime? @map("paid_at")
  paymentProof  String?   @map("payment_proof") @db.VarChar(500)
  paymentRemark String?   @map("payment_remark") @db.VarChar(500)

  // 详细备注 (操作记录)
  detailMemo    String?   @map("detail_memo") @db.Text

  // 时间戳
  frozenAt      DateTime? @map("frozen_at")
  completedAt   DateTime? @map("completed_at")
  createdAt     DateTime  @default(now()) @map("created_at")

  @@map("fiat_withdrawal_orders")
  @@index([accountSequence])
  @@index([userId])
  @@index([status])
  @@index([paymentMethod])
  @@index([reviewedBy])
  @@index([createdAt])
}

// ============================================
// 提取手续费配置表
// ============================================
model WithdrawalFeeConfig {
  id          BigInt   @id @default(autoincrement()) @map("config_id")

  // 费率类型: FIXED(固定金额) / PERCENTAGE(百分比)
  feeType     String   @map("fee_type") @db.VarChar(20)

  // 费率值: 固定金额时为具体数值，百分比时为小数 (如 0.001 = 0.1%)
  feeValue    Decimal  @map("fee_value") @db.Decimal(20, 8)

  // 最小手续费 (百分比模式下生效)
  minFee      Decimal  @default(0) @map("min_fee") @db.Decimal(20, 8)

  // 最大手续费 (百分比模式下生效，0表示不限制)
  maxFee      Decimal  @default(0) @map("max_fee") @db.Decimal(20, 8)

  // 是否启用
  isActive    Boolean  @default(true) @map("is_active")

  // 备注
  memo        String?  @map("memo") @db.VarChar(200)

  createdAt   DateTime @default(now()) @map("created_at")
  updatedAt   DateTime @updatedAt @map("updated_at")

  @@map("withdrawal_fee_configs")
  @@index([isActive])
}

// ============================================
// 已处理事件表 (幂等性检查)
// 用于确保 Kafka 事件不会被重复处理
// ============================================
model ProcessedEvent {
  id          BigInt   @id @default(autoincrement()) @map("processed_id")

  // 事件标识 (聚合根ID + 事件类型)
  eventId     String   @map("event_id") @db.VarChar(200)
  eventType   String   @map("event_type") @db.VarChar(100)

  // 来源服务
  sourceService String @map("source_service") @db.VarChar(50)

  // 处理时间
  processedAt DateTime @default(now()) @map("processed_at")

  @@unique([eventId, eventType])
  @@map("processed_events")
  @@index([eventId])
  @@index([eventType])
  @@index([processedAt])
}

// ============================================
// 系统账户转出订单表
// 用于记录从系统账户(S开头/区域账户)转出到用户账户的订单
// ============================================
model SystemWithdrawalOrder {
  id              BigInt   @id @default(autoincrement()) @map("order_id")
  orderNo         String   @unique @map("order_no") @db.VarChar(50)

  // 转出方(系统账户)
  fromAccountSequence String  @map("from_account_sequence") @db.VarChar(20)  // 系统账户序列号
  fromAccountName     String  @map("from_account_name") @db.VarChar(100)     // 系统账户名称

  // 接收方(用户账户)
  toAccountSequence String  @map("to_account_sequence") @db.VarChar(20)      // 用户充值ID
  toUserId          BigInt  @map("to_user_id")                               // 用户ID
  toUserName        String? @map("to_user_name") @db.VarChar(100)            // 用户姓名
  toAddress         String  @map("to_address") @db.VarChar(100)              // 用户区块链地址

  // 金额
  amount    Decimal @map("amount") @db.Decimal(20, 8)
  chainType String  @default("KAVA") @map("chain_type") @db.VarChar(20)

  // 交易信息
  txHash    String? @map("tx_hash") @db.VarChar(100)

  // 状态: PENDING -> FROZEN -> BROADCASTED -> CONFIRMED / FAILED
  status       String  @default("PENDING") @map("status") @db.VarChar(20)
  errorMessage String? @map("error_message") @db.VarChar(500)

  // 操作者
  operatorId   String  @map("operator_id") @db.VarChar(100)
  operatorName String? @map("operator_name") @db.VarChar(100)

  // 备注
  memo String? @map("memo") @db.Text

  // 时间戳
  frozenAt      DateTime? @map("frozen_at")
  broadcastedAt DateTime? @map("broadcasted_at")
  confirmedAt   DateTime? @map("confirmed_at")
  createdAt     DateTime  @default(now()) @map("created_at")

  @@map("system_withdrawal_orders")
  @@index([fromAccountSequence])
  @@index([toAccountSequence])
  @@index([toUserId])
  @@index([status])
  @@index([txHash])
  @@index([operatorId])
  @@index([createdAt])
}

// ============================================
// 线下结算扣减记录表
// 记录哪些 REWARD_SETTLED 流水已被"线下结算"扣减过
// 用于全额扣减模式：当管理员不输入金额时，
// 自动扣减用户所有未处理的结算收益
// ============================================
model OfflineSettlementDeduction {
  id                BigInt   @id @default(autoincrement()) @map("deduction_id")
  accountSequence   String   @map("account_sequence") @db.VarChar(20)
  userId            BigInt   @map("user_id")

  // 关联的原结算流水ID (wallet_ledger_entries.entry_id)
  originalLedgerEntryId BigInt @unique @map("original_ledger_entry_id")

  // 原结算金额
  originalAmount    Decimal  @map("original_amount") @db.Decimal(20, 8)

  // 实际扣减的金额 (通常与原金额相同)
  deductedAmount    Decimal  @map("deducted_amount") @db.Decimal(20, 8)

  // 生成的扣减流水ID
  deductionLedgerEntryId BigInt @map("deduction_ledger_entry_id")

  // 扣减订单号
  deductionOrderNo  String   @map("deduction_order_no") @db.VarChar(50)

  // 关联的待办操作ID
  pendingActionId   String?  @map("pending_action_id") @db.VarChar(100)

  // 操作者
  operatorId        String   @map("operator_id") @db.VarChar(100)

  // 备注
  memo              String?  @map("memo") @db.VarChar(500)

  createdAt         DateTime @default(now()) @map("created_at")

  @@map("offline_settlement_deductions")
  @@index([accountSequence])
  @@index([userId])
  @@index([pendingActionId])
  @@index([deductionOrderNo])
  @@index([createdAt])
}

// ============================================
// Outbox 事件表 (可靠事件发布)
// [2026-01-07] 新增：实现 Outbox Pattern 确保事件发布的原子性
// ============================================
model OutboxEvent {
  id BigInt @id @default(autoincrement()) @map("outbox_id")

  // 事件信息
  eventType String @map("event_type") @db.VarChar(100)
  topic     String @map("topic") @db.VarChar(100)
  key       String @map("key") @db.VarChar(200)
  payload   Json   @map("payload")

  // 聚合根信息 (用于幂等性检查和事件确认)
  aggregateId   String @map("aggregate_id") @db.VarChar(100)
  aggregateType String @map("aggregate_type") @db.VarChar(50)

  // 发布状态: PENDING(待发送), SENT(已发送待确认), CONFIRMED(已确认), FAILED(失败)
  status     String  @default("PENDING") @map("status") @db.VarChar(20)
  retryCount Int     @default(0) @map("retry_count")
  maxRetries Int     @default(5) @map("max_retries")
  lastError  String? @map("last_error") @db.Text

  // 时间戳
  createdAt   DateTime  @default(now()) @map("created_at")
  publishedAt DateTime? @map("published_at")
  nextRetryAt DateTime? @map("next_retry_at")

  @@index([status, createdAt])
  @@index([status, nextRetryAt])
  @@index([aggregateType, aggregateId])
  @@index([topic])
  @@map("outbox_events")
}
