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

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

model UserAccount {
  userId          BigInt   @id @default(autoincrement()) @map("user_id")
  accountSequence String   @unique @map("account_sequence") @db.VarChar(12)  // 格式: D + YYMMDD + 5位序号

  phoneNumber     String?  @unique @map("phone_number") @db.VarChar(20)
  phoneVerified   Boolean  @default(true) @map("phone_verified")  // 手机号是否验证（注册时跳过验证码则为false）
  phoneVerifiedAt DateTime? @map("phone_verified_at")  // 手机号验证时间
  email           String?  @unique @db.VarChar(100)  // 绑定的邮箱地址
  emailVerified   Boolean  @default(false) @map("email_verified")  // 邮箱是否验证
  emailVerifiedAt DateTime? @map("email_verified_at")  // 邮箱验证时间
  passwordHash    String?  @map("password_hash") @db.VarChar(100)  // bcrypt 哈希密码
  nickname        String   @db.VarChar(100)
  avatarUrl       String?  @map("avatar_url") @db.Text

  inviterSequence String?  @map("inviter_sequence") @db.VarChar(12)  // 推荐人序列号
  referralCode    String   @unique @map("referral_code") @db.VarChar(10)

  // ========== 三层认证状态 ==========
  // 层级1: 实名认证 (二要素: 姓名+身份证号)
  realNameVerified    Boolean   @default(false) @map("real_name_verified")
  realNameVerifiedAt  DateTime? @map("real_name_verified_at")
  realName            String?   @map("real_name") @db.VarChar(100)  // 真实姓名（加密存储）
  idCardNumber        String?   @map("id_card_number") @db.VarChar(50)  // 身份证号（加密存储）

  // 层级2: 实人认证 (人脸活体检测)
  faceVerified        Boolean   @default(false) @map("face_verified")
  faceVerifiedAt      DateTime? @map("face_verified_at")
  faceCertifyId       String?   @map("face_certify_id") @db.VarChar(100)  // 阿里云认证ID

  // 层级3: KYC (证件照上传验证)
  kycVerified         Boolean   @default(false) @map("kyc_verified")
  kycVerifiedAt       DateTime? @map("kyc_verified_at")
  idCardFrontUrl      String?   @map("id_card_front_url") @db.VarChar(500)
  idCardBackUrl       String?   @map("id_card_back_url") @db.VarChar(500)

  // 综合 KYC 状态 (兼容旧逻辑)
  // NOT_STARTED, REAL_NAME_VERIFIED, FACE_VERIFIED, KYC_VERIFIED, COMPLETED, REJECTED
  kycStatus           String    @default("NOT_STARTED") @map("kyc_status") @db.VarChar(20)
  kycProvider         String?   @map("kyc_provider") @db.VarChar(50)  // 服务提供商: ALIYUN
  kycRequestId        String?   @map("kyc_request_id") @db.VarChar(100)  // 第三方请求ID
  kycRejectedReason   String?   @map("kyc_rejected_reason") @db.VarChar(500)  // 拒绝原因

  status          String   @default("ACTIVE") @db.VarChar(20)

  registeredAt    DateTime @default(now()) @map("registered_at")
  lastLoginAt     DateTime? @map("last_login_at")
  updatedAt       DateTime @updatedAt @map("updated_at")

  devices         UserDevice[]
  walletAddresses WalletAddress[]

  @@index([phoneNumber], name: "idx_phone")
  @@index([email], name: "idx_email")
  @@index([accountSequence], name: "idx_sequence")
  @@index([referralCode], name: "idx_referral_code")
  @@index([inviterSequence], name: "idx_inviter")
  @@index([kycStatus], name: "idx_kyc_status")
  @@index([status], name: "idx_status")
  @@map("user_accounts")
}

model UserDevice {
  id           BigInt   @id @default(autoincrement())
  userId       BigInt   @map("user_id")
  deviceId     String   @map("device_id") @db.VarChar(100)
  deviceName   String?  @map("device_name") @db.VarChar(100)

  // Hardware Info - 设备硬件信息 (JSON 存储完整前端传递的设备信息)
  deviceInfo   Json?    @map("device_info")  // 完整的设备信息 JSON
  platform     String?  @db.VarChar(20)   // ios, android, web (冗余字段，便于查询)
  deviceModel  String?  @map("device_model") @db.VarChar(100)  // iPhone 15 Pro, Pixel 8
  osVersion    String?  @map("os_version") @db.VarChar(50)     // iOS 17.2, Android 14
  appVersion   String?  @map("app_version") @db.VarChar(20)    // 1.0.0
  screenWidth  Int?     @map("screen_width")
  screenHeight Int?     @map("screen_height")
  locale       String?  @db.VarChar(10)   // zh-CN, en-US
  timezone     String?  @db.VarChar(50)   // Asia/Shanghai

  addedAt      DateTime @default(now()) @map("added_at")
  lastActiveAt DateTime @default(now()) @map("last_active_at")

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

  @@unique([userId, deviceId], name: "uk_user_device")
  @@index([deviceId], name: "idx_device")
  @@index([userId], name: "idx_user")
  @@index([lastActiveAt], name: "idx_last_active")
  @@index([platform], name: "idx_platform")
  @@map("user_devices")
}

model WalletAddress {
  addressId         BigInt   @id @default(autoincrement()) @map("address_id")
  userId            BigInt   @map("user_id")

  chainType         String   @map("chain_type") @db.VarChar(20)
  address           String   @db.VarChar(100)
  publicKey         String   @map("public_key") @db.VarChar(130)  // MPC公钥 (压缩/非压缩格式)

  // MPC 签名验证字段 - 防止地址被恶意篡改
  addressDigest     String   @map("address_digest") @db.VarChar(66)  // SHA256摘要
  mpcSignatureR     String   @map("mpc_signature_r") @db.VarChar(66) // 签名R分量
  mpcSignatureS     String   @map("mpc_signature_s") @db.VarChar(66) // 签名S分量
  mpcSignatureV     Int      @map("mpc_signature_v")                 // 签名恢复ID

  status            String   @default("ACTIVE") @db.VarChar(20)

  boundAt           DateTime @default(now()) @map("bound_at")

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

  @@unique([userId, chainType], name: "uk_user_chain")
  @@unique([chainType, address], name: "uk_chain_address")
  @@index([userId], name: "idx_wallet_user")
  @@index([address], name: "idx_address")
  @@index([publicKey], name: "idx_public_key")
  @@map("wallet_addresses")
}

model AccountSequenceGenerator {
  id              Int      @id @default(autoincrement())
  dateKey         String   @unique @map("date_key") @db.VarChar(6)  // 格式: YYMMDD
  currentSequence Int      @default(0) @map("current_sequence")     // 当日序号 (0-99999)
  updatedAt       DateTime @updatedAt @map("updated_at")

  @@map("account_sequence_generator")
}

model UserEvent {
  eventId       BigInt   @id @default(autoincrement()) @map("event_id")
  eventType     String   @map("event_type") @db.VarChar(50)
  
  aggregateId   String   @map("aggregate_id") @db.VarChar(100)
  aggregateType String   @map("aggregate_type") @db.VarChar(50)
  
  eventData     Json     @map("event_data")
  
  userId        BigInt?  @map("user_id")
  occurredAt    DateTime @default(now()) @map("occurred_at") @db.Timestamp(6)
  version       Int      @default(1)
  
  @@index([aggregateType, aggregateId], name: "idx_aggregate")
  @@index([eventType], name: "idx_event_type")
  @@index([userId], name: "idx_event_user")
  @@index([occurredAt], name: "idx_occurred")
  @@map("user_events")
}

model DeviceToken {
  id               BigInt    @id @default(autoincrement())
  userId           BigInt    @map("user_id")
  deviceId         String    @map("device_id") @db.VarChar(100)

  refreshTokenHash String    @unique @map("refresh_token_hash") @db.VarChar(64)

  expiresAt        DateTime  @map("expires_at")
  createdAt        DateTime  @default(now()) @map("created_at")
  revokedAt        DateTime? @map("revoked_at")

  @@index([userId, deviceId], name: "idx_user_device_token")
  @@index([expiresAt], name: "idx_expires")
  @@map("device_tokens")
}

model DeadLetterEvent {
  id            BigInt   @id @default(autoincrement())
  topic         String   @db.VarChar(100)
  eventId       String   @map("event_id") @db.VarChar(100)
  eventType     String   @map("event_type") @db.VarChar(50)

  aggregateId   String   @map("aggregate_id") @db.VarChar(100)
  aggregateType String   @map("aggregate_type") @db.VarChar(50)
  payload       Json?

  errorMessage  String   @map("error_message") @db.Text
  errorStack    String?  @map("error_stack") @db.Text
  retryCount    Int      @default(0) @map("retry_count")

  createdAt     DateTime @default(now()) @map("created_at")
  processedAt   DateTime? @map("processed_at")

  @@index([topic], name: "idx_topic")
  @@index([eventType], name: "idx_dead_letter_event_type")
  @@index([createdAt], name: "idx_dead_letter_created")
  @@index([processedAt], name: "idx_processed")
  @@map("dead_letter_events")
}

model SmsCode {
  id          BigInt   @id @default(autoincrement())
  phoneNumber String   @map("phone_number") @db.VarChar(20)
  code        String   @db.VarChar(10)
  purpose     String   @db.VarChar(50)

  expiresAt   DateTime @map("expires_at")
  createdAt   DateTime @default(now()) @map("created_at")
  usedAt      DateTime? @map("used_at")

  @@index([phoneNumber, purpose], name: "idx_phone_purpose")
  @@index([expiresAt], name: "idx_sms_expires")
  @@map("sms_codes")
}

// 邮箱验证码
model EmailCode {
  id          BigInt   @id @default(autoincrement())
  email       String   @db.VarChar(100)
  code        String   @db.VarChar(10)
  purpose     String   @db.VarChar(50)  // BIND_EMAIL, UNBIND_EMAIL

  expiresAt   DateTime @map("expires_at")
  createdAt   DateTime @default(now()) @map("created_at")
  usedAt      DateTime? @map("used_at")

  @@index([email, purpose], name: "idx_email_purpose")
  @@index([expiresAt], name: "idx_email_expires")
  @@map("email_codes")
}

// MPC 密钥分片存储 - 服务端持有的分片
model MpcKeyShare {
  shareId           BigInt   @id @default(autoincrement()) @map("share_id")
  userId            BigInt   @unique @map("user_id")  // 一个用户只有一组MPC密钥

  publicKey         String   @unique @map("public_key") @db.VarChar(130)  // MPC公钥
  partyIndex        Int      @map("party_index")           // 当前分片的参与方索引 (0=服务端)
  threshold         Int      @default(2)                    // 阈值 t
  totalParties      Int      @default(3) @map("total_parties") // 总参与方 n

  // 加密存储的分片数据 (AES加密)
  encryptedShareData String  @map("encrypted_share_data") @db.Text

  status            String   @default("ACTIVE") @db.VarChar(20)  // ACTIVE, ROTATED, REVOKED

  createdAt         DateTime @default(now()) @map("created_at")
  rotatedAt         DateTime? @map("rotated_at")  // 最后一次密钥轮换时间

  @@index([publicKey], name: "idx_mpc_public_key")
  @@index([status], name: "idx_mpc_status")
  @@map("mpc_key_shares")
}

// MPC 会话记录 - 用于审计和追踪
model MpcSession {
  sessionId         String   @id @map("session_id") @db.VarChar(50)
  sessionType       String   @map("session_type") @db.VarChar(20)  // KEYGEN, SIGNING, ROTATION

  userId            BigInt?  @map("user_id")
  publicKey         String?  @map("public_key") @db.VarChar(130)

  status            String   @default("PENDING") @db.VarChar(20)  // PENDING, IN_PROGRESS, COMPLETED, FAILED
  errorMessage      String?  @map("error_message") @db.Text

  // 签名相关 (仅SIGNING类型)
  messageHash       String?  @map("message_hash") @db.VarChar(66)
  signatureR        String?  @map("signature_r") @db.VarChar(66)
  signatureS        String?  @map("signature_s") @db.VarChar(66)
  signatureV        Int?     @map("signature_v")

  createdAt         DateTime @default(now()) @map("created_at")
  completedAt       DateTime? @map("completed_at")

  @@index([sessionType], name: "idx_session_type")
  @@index([userId], name: "idx_session_user")
  @@index([status], name: "idx_session_status")
  @@index([createdAt], name: "idx_session_created")
  @@map("mpc_sessions")
}

// 账户恢复助记词 - 与钱包公钥关联，支持挂失和更换
model RecoveryMnemonic {
  id                BigInt   @id @default(autoincrement())
  userId            BigInt   @map("user_id")
  publicKey         String   @map("public_key") @db.VarChar(130)  // 关联的钱包公钥

  // 助记词存储 (加密)
  encryptedMnemonic String   @map("encrypted_mnemonic") @db.Text  // AES加密的助记词
  mnemonicHash      String   @map("mnemonic_hash") @db.VarChar(64)  // SHA256哈希（用于验证）

  // 状态管理
  status            String   @default("ACTIVE") @db.VarChar(20)  // ACTIVE, REVOKED, REPLACED
  isBackedUp        Boolean  @default(false) @map("is_backed_up")  // 用户是否已备份

  // 挂失/更换相关
  revokedAt         DateTime? @map("revoked_at")
  revokedReason     String?   @map("revoked_reason") @db.VarChar(200)
  replacedById      BigInt?   @map("replaced_by_id")  // 被哪个新助记词替代

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

  @@unique([userId, status], name: "uk_user_active_mnemonic")  // 一个用户只有一个ACTIVE助记词
  @@index([userId], name: "idx_recovery_user")
  @@index([publicKey], name: "idx_recovery_public_key")
  @@index([status], name: "idx_recovery_status")
  @@map("recovery_mnemonics")
}

// TOTP 二次验证 - 用于敏感操作 (提现、转账等)
model UserTotp {
  id                BigInt   @id @default(autoincrement())
  userId            BigInt   @unique @map("user_id")

  // TOTP 密钥 (AES加密存储)
  encryptedSecret   String   @map("encrypted_secret") @db.VarChar(100)

  // 状态
  isEnabled         Boolean  @default(false) @map("is_enabled")  // 是否已启用
  isVerified        Boolean  @default(false) @map("is_verified")  // 用户是否已验证过一次

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

  @@index([userId], name: "idx_totp_user")
  @@map("user_totp")
}

// 推荐链接 - 用于追踪不同渠道的邀请
model ReferralLink {
  linkId            BigInt   @id @default(autoincrement()) @map("link_id")
  userId            BigInt   @map("user_id")
  referralCode      String   @map("referral_code") @db.VarChar(10)

  shortCode         String   @unique @map("short_code") @db.VarChar(10)  // 短链码
  channel           String?  @db.VarChar(50)   // 渠道: wechat, telegram, twitter, etc.
  campaignId        String?  @map("campaign_id") @db.VarChar(50)  // 活动ID

  clickCount        Int      @default(0) @map("click_count")  // 点击次数
  registerCount     Int      @default(0) @map("register_count")  // 注册转化数

  createdAt         DateTime @default(now()) @map("created_at")
  expiresAt         DateTime? @map("expires_at")  // 过期时间 (可选)

  @@index([userId], name: "idx_referral_link_user")
  @@index([referralCode], name: "idx_referral_link_code")
  @@index([channel], name: "idx_referral_link_channel")
  @@index([createdAt], name: "idx_referral_link_created")
  @@map("referral_links")
}

// ============================================
// 管理员账户表 - 用于后台管理系统登录
// ============================================
model AdminAccount {
  id            BigInt   @id @default(autoincrement())
  email         String   @unique @db.VarChar(100)
  passwordHash  String   @map("password_hash") @db.VarChar(100)  // bcrypt 哈希
  nickname      String   @db.VarChar(100)
  role          String   @default("admin") @db.VarChar(20)  // super_admin, admin, operator
  status        String   @default("ACTIVE") @db.VarChar(20)  // ACTIVE, INACTIVE, LOCKED

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

  @@index([email], name: "idx_admin_email")
  @@index([role], name: "idx_admin_role")
  @@index([status], name: "idx_admin_status")
  @@map("admin_accounts")
}

// ============================================
// Outbox 事件表 - 保证事件可靠发送
// 使用 Outbox Pattern 确保领域事件100%送达
// ============================================
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")
}

// ============================================
// KYC 验证尝试记录表
// 记录所有 KYC 验证尝试，用于审计和追踪
// ============================================
model KycVerificationAttempt {
  id              BigInt    @id @default(autoincrement())
  userId          BigInt    @map("user_id")

  // 验证类型: PHONE (手机验证), ID_CARD (身份证二要素), ID_PHOTO (证件照OCR), FACE (人脸活体)
  verificationType String   @map("verification_type") @db.VarChar(20)

  // 第三方服务信息
  provider        String?   @map("provider") @db.VarChar(50)  // ALIYUN, TENCENT, SMS
  requestId       String?   @map("request_id") @db.VarChar(100)

  // 输入数据 (加密存储敏感信息)
  inputData       Json?     @map("input_data")  // { realName: "张**", idCardNumber: "1234********5678" }

  // 验证结果: PENDING, SUCCESS, FAILED
  status          String    @default("PENDING") @db.VarChar(20)
  failureReason   String?   @map("failure_reason") @db.VarChar(500)
  responseData    Json?     @map("response_data")  // 第三方返回的原始数据

  createdAt       DateTime  @default(now()) @map("created_at")
  completedAt     DateTime? @map("completed_at")

  @@index([userId], name: "idx_kyc_attempt_user")
  @@index([status], name: "idx_kyc_attempt_status")
  @@index([requestId], name: "idx_kyc_request_id")
  @@index([createdAt], name: "idx_kyc_attempt_created")
  @@map("kyc_verification_attempts")
}

// ============================================
// KYC 配置表
// 用于管理后台控制各认证步骤的开关
// ============================================
model KycConfig {
  id              Int       @id @default(autoincrement())
  configKey       String    @unique @map("config_key") @db.VarChar(50)
  configValue     String    @map("config_value") @db.VarChar(100)
  description     String?   @db.VarChar(500)
  updatedBy       String?   @map("updated_by") @db.VarChar(50)
  updatedAt       DateTime  @updatedAt @map("updated_at")

  @@map("kyc_configs")
}

// ============================================
// 用户待办操作表
// 用于后台配置指定用户登录后需要执行的特定操作
// 完全可选，不影响现有任何功能
// ============================================
model UserPendingAction {
  id            BigInt    @id @default(autoincrement())
  userId        BigInt    @map("user_id")

  // 操作代码: ADOPTION_WIZARD, SETTLE_REWARDS, BIND_PHONE, FORCE_KYC 等
  // 前端根据此代码决定跳转到哪个页面/流程
  actionCode    String    @map("action_code") @db.VarChar(50)

  // 操作参数 (JSON格式，完全自定义)
  // 例如: { "treeType": "durian", "rewardIds": ["r1", "r2"], "amount": 100 }
  actionParams  Json?     @map("action_params")

  // 操作状态: PENDING(待执行), COMPLETED(已完成), CANCELLED(已取消)
  status        String    @default("PENDING") @db.VarChar(20)

  // 优先级 (数值越大优先级越高，前端按此排序执行)
  priority      Int       @default(0)

  // 可选过期时间 (过期后前端可忽略)
  expiresAt     DateTime? @map("expires_at")

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

  // 创建人 (后台管理员)
  createdBy     String?   @map("created_by") @db.VarChar(50)

  @@index([userId, status], name: "idx_user_pending_action")
  @@index([actionCode], name: "idx_action_code")
  @@index([status], name: "idx_pending_action_status")
  @@map("user_pending_actions")
}
