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

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

// ============================================
// 认种订单表 (状态表)
// ============================================
model PlantingOrder {
  id              BigInt @id @default(autoincrement()) @map("order_id")
  orderNo         String @unique @map("order_no") @db.VarChar(50)
  userId          BigInt @map("user_id")
  accountSequence String @map("account_sequence") @db.VarChar(20)

  // 认种信息
  treeCount       Int     @map("tree_count")
  totalAmount     Decimal @map("total_amount") @db.Decimal(20, 8)
  priceSupplement Int     @default(0) @map("price_supplement") // 总部运营成本压力涨价（每棵树加价金额），归总部 (S0000000001)

  // 转让锁定（纯新增，不影响现有逻辑）
  transferLockedCount Int @default(0) @map("transfer_locked_count")

  // 省市选择 (不可修改)
  selectedProvince        String?   @map("selected_province") @db.VarChar(10)
  selectedCity            String?   @map("selected_city") @db.VarChar(10)
  provinceCitySelectedAt  DateTime? @map("province_city_selected_at")
  provinceCityConfirmedAt DateTime? @map("province_city_confirmed_at")

  // 订单状态
  status String @default("CREATED") @map("status") @db.VarChar(30)

  // 底池信息
  poolInjectionBatchId       BigInt?   @map("pool_injection_batch_id")
  poolInjectionScheduledTime DateTime? @map("pool_injection_scheduled_time")
  poolInjectionActualTime    DateTime? @map("pool_injection_actual_time")
  poolInjectionTxHash        String?   @map("pool_injection_tx_hash") @db.VarChar(100)

  // 挖矿
  miningEnabledAt DateTime? @map("mining_enabled_at")

  // 时间戳
  createdAt       DateTime  @default(now()) @map("created_at")
  paidAt          DateTime? @map("paid_at")
  fundAllocatedAt DateTime? @map("fund_allocated_at")
  updatedAt       DateTime  @updatedAt @map("updated_at")

  // 关联
  fundAllocations FundAllocation[]
  batch           PoolInjectionBatch? @relation(fields: [poolInjectionBatchId], references: [id])

  @@index([userId])
  @@index([accountSequence])
  @@index([orderNo])
  @@index([status])
  @@index([poolInjectionBatchId])
  @@index([selectedProvince, selectedCity])
  @@index([createdAt])
  @@index([paidAt])
  @@map("planting_orders")
}

// ============================================
// 资金分配明细表 (行为表, append-only)
// ============================================
model FundAllocation {
  id      BigInt @id @default(autoincrement()) @map("allocation_id")
  orderId BigInt @map("order_id")

  // 分配信息
  targetType      String  @map("target_type") @db.VarChar(50)
  amount          Decimal @map("amount") @db.Decimal(20, 8)
  targetAccountId String? @map("target_account_id") @db.VarChar(100)

  // 元数据
  metadata Json? @map("metadata")

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

  // 关联
  order PlantingOrder @relation(fields: [orderId], references: [id])

  @@index([orderId])
  @@index([targetType, targetAccountId])
  @@index([createdAt])
  @@map("fund_allocations")
}

// ============================================
// 用户持仓表 (状态表)
// ============================================
model PlantingPosition {
  id     BigInt @id @default(autoincrement()) @map("position_id")
  userId BigInt @unique @map("user_id")

  // 持仓统计
  totalTreeCount     Int @default(0) @map("total_tree_count")
  effectiveTreeCount Int @default(0) @map("effective_tree_count")
  pendingTreeCount   Int @default(0) @map("pending_tree_count")

  // 挖矿状态
  firstMiningStartAt DateTime? @map("first_mining_start_at")

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

  // 关联
  distributions PositionDistribution[]

  @@index([userId])
  @@index([totalTreeCount])
  @@map("planting_positions")
}

// ============================================
// 持仓省市分布表
// ============================================
model PositionDistribution {
  id     BigInt @id @default(autoincrement()) @map("distribution_id")
  userId BigInt @map("user_id")

  // 省市信息
  provinceCode String? @map("province_code") @db.VarChar(10)
  cityCode     String? @map("city_code") @db.VarChar(10)

  // 数量
  treeCount Int @default(0) @map("tree_count")

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

  // 关联
  position PlantingPosition @relation(fields: [userId], references: [userId])

  @@unique([userId, provinceCode, cityCode])
  @@index([userId])
  @@index([provinceCode])
  @@index([cityCode])
  @@map("position_province_city_distribution")
}

// ============================================
// 底池注入批次表 (状态表)
// ============================================
model PoolInjectionBatch {
  id      BigInt @id @default(autoincrement()) @map("batch_id")
  batchNo String @unique @map("batch_no") @db.VarChar(50)

  // 批次时间窗口 (5天)
  startDate DateTime @map("start_date") @db.Date
  endDate   DateTime @map("end_date") @db.Date

  // 统计信息
  orderCount  Int     @default(0) @map("order_count")
  totalAmount Decimal @default(0) @map("total_amount") @db.Decimal(20, 8)

  // 注入状态
  status                 String    @default("PENDING") @map("status") @db.VarChar(20)
  scheduledInjectionTime DateTime? @map("scheduled_injection_time")
  actualInjectionTime    DateTime? @map("actual_injection_time")
  injectionTxHash        String?   @map("injection_tx_hash") @db.VarChar(100)

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

  // 关联
  orders PlantingOrder[]

  @@index([batchNo])
  @@index([startDate, endDate])
  @@index([status])
  @@index([scheduledInjectionTime])
  @@map("pool_injection_batches")
}

// ============================================
// 认种事件表 (行为表, append-only)
// ============================================
model PlantingEvent {
  id        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")
  version    Int      @default(1) @map("version")

  @@index([aggregateType, aggregateId])
  @@index([eventType])
  @@index([userId])
  @@index([occurredAt])
  @@map("planting_events")
}

// ============================================
// Outbox 事件发件箱表 (Outbox Pattern)
// 保证事件发布的可靠性：
// 1. 业务数据和 Outbox 记录在同一个事务中写入
// 2. 后台任务轮询 Outbox 表并发布到 Kafka
// 3. 发布成功后标记为已处理
// ============================================
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)

  // 发布状态
  status     String  @default("PENDING") @map("status") @db.VarChar(20) // PENDING, PUBLISHED, FAILED
  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")
}

// ============================================
// 支付补偿表 (用于处理支付失败需要补偿的订单)
// 当订单支付过程中发生以下情况时创建补偿记录:
// 1. 资金已冻结/扣款但数据库事务失败
// 2. 数据库事务成功但确认扣款失败
// 3. 确认扣款成功但资金分配失败
// ============================================
model PaymentCompensation {
  id      BigInt @id @default(autoincrement()) @map("compensation_id")
  orderNo String @map("order_no") @db.VarChar(50)
  userId  BigInt @map("user_id")

  // 补偿类型
  compensationType String @map("compensation_type") @db.VarChar(50) // UNFREEZE, REFUND, RETRY_CONFIRM, RETRY_ALLOCATE

  // 金额信息
  amount Decimal @map("amount") @db.Decimal(20, 8)

  // 状态
  status String @default("PENDING") @map("status") @db.VarChar(20) // PENDING, PROCESSING, COMPLETED, FAILED

  // 失败信息
  failureReason String? @map("failure_reason") @db.Text
  failureStage  String? @map("failure_stage") @db.VarChar(50) // FREEZE, DB_TRANSACTION, CONFIRM, ALLOCATE

  // 重试信息
  retryCount  Int       @default(0) @map("retry_count")
  maxRetries  Int       @default(5) @map("max_retries")
  nextRetryAt DateTime? @map("next_retry_at")
  lastError   String?   @map("last_error") @db.Text

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

  @@unique([orderNo, compensationType])
  @@index([status, nextRetryAt])
  @@index([userId])
  @@index([orderNo])
  @@index([createdAt])
  @@map("payment_compensations")
}

// ============================================
// 合同模板表
// 存储电子认种合同的模板内容，支持版本管理
// ============================================
model ContractTemplate {
  id      Int    @id @default(autoincrement()) @map("template_id")
  version String @unique @map("version") @db.VarChar(20) // 版本号: v1.0.0

  // 合同内容
  title   String @map("title") @db.VarChar(200) // 合同标题
  content String @map("content") @db.Text // 合同HTML内容，包含占位符

  // 生效时间
  effectiveFrom DateTime  @map("effective_from")
  effectiveTo   DateTime? @map("effective_to") // null表示永久有效
  isActive      Boolean   @default(true) @map("is_active")

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

  // 关联
  signingTasks ContractSigningTask[]

  @@index([isActive, effectiveFrom])
  @@map("contract_templates")
}

// ============================================
// 合同签署任务表
// 独立模块，不影响现有认种流程
// 通过订阅 OrderPaid 事件创建签署任务
// ============================================
model ContractSigningTask {
  id BigInt @id @default(autoincrement()) @map("task_id")

  // 关联信息
  orderNo         String @unique @map("order_no") @db.VarChar(50)
  contractNo      String @unique @map("contract_no") @db.VarChar(30) // 合同编号: accountSequence-yyyyMMddHHmm
  userId          BigInt @map("user_id")
  accountSequence String @map("account_sequence") @db.VarChar(20)

  // 合同信息
  templateId      Int    @map("template_id")
  contractVersion String @map("contract_version") @db.VarChar(20)
  contractContent String @map("contract_content") @db.Text // 已填入用户信息的完整合同

  // 用户信息快照 (签署时填入合同的信息)
  userPhoneNumber  String? @map("user_phone_number") @db.VarChar(20)
  userRealName     String? @map("user_real_name") @db.VarChar(50)
  userIdCardNumber String? @map("user_id_card_number") @db.VarChar(50) // 脱敏存储

  // 订单信息快照
  treeCount    Int     @map("tree_count")
  totalAmount  Decimal @map("total_amount") @db.Decimal(20, 8)
  provinceCode String  @map("province_code") @db.VarChar(10)
  provinceName String  @map("province_name") @db.VarChar(50)
  cityCode     String  @map("city_code") @db.VarChar(10)
  cityName     String  @map("city_name") @db.VarChar(50)

  // 签署状态
  // PENDING: 待签署
  // SCROLLED: 已滚动到底部
  // ACKNOWLEDGED: 已确认法律效力
  // SIGNED: 已签署完成
  // UNSIGNED_TIMEOUT: 超时未签署
  status    String   @default("PENDING") @map("status") @db.VarChar(30)
  expiresAt DateTime @map("expires_at") // 24小时过期时间

  // 签署进度时间戳
  scrolledToBottomAt DateTime? @map("scrolled_to_bottom_at")
  acknowledgedAt     DateTime? @map("acknowledged_at")
  signedAt           DateTime? @map("signed_at")

  // 签名数据
  signatureCloudUrl String? @map("signature_cloud_url") @db.VarChar(500)
  signatureHash     String? @map("signature_hash") @db.VarChar(64) // SHA256
  signedPdfUrl      String? @map("signed_pdf_url") @db.VarChar(500) // 已签署合同PDF的云存储URL

  // 法律合规证据链
  signingIpAddress   String? @map("signing_ip_address") @db.VarChar(50)
  signingDeviceInfo  String? @map("signing_device_info") @db.Text // JSON格式
  signingUserAgent   String? @map("signing_user_agent") @db.VarChar(500)
  signingLatitude    Decimal? @map("signing_latitude") @db.Decimal(10, 8)
  signingLongitude   Decimal? @map("signing_longitude") @db.Decimal(11, 8)

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

  // 关联
  template ContractTemplate @relation(fields: [templateId], references: [id])

  @@index([userId])
  @@index([status])
  @@index([expiresAt])
  @@index([status, expiresAt])
  @@map("contract_signing_tasks")
}

// ============================================
// Debezium 心跳表
// 用于 CDC replication slot 的 WAL 位置推进
// 由 Debezium heartbeat.action.query 自动更新
// 防止因业务表长期无写入导致 WAL 堆积
// ============================================
model DebeziumHeartbeat {
  id Int      @id @default(1)
  ts DateTime @default(now()) @updatedAt @map("ts")

  @@map("debezium_heartbeat")
}

// ============================================
// 预种订单表 (3566 预种计划)
// 每次购买一份预种创建一条记录
// ============================================
model PrePlantingOrder {
  id              BigInt    @id @default(autoincrement()) @map("order_id")
  orderNo         String    @unique @map("order_no") @db.VarChar(50)
  userId          BigInt    @map("user_id")
  accountSequence String    @map("account_sequence") @db.VarChar(20)

  // 购买信息
  portionCount    Int       @default(1) @map("portion_count")
  pricePerPortion Decimal   @default(3566) @map("price_per_portion") @db.Decimal(20, 8)
  totalAmount     Decimal   @map("total_amount") @db.Decimal(20, 8)
  priceSupplement Int       @default(0) @map("price_supplement") // 总部运营成本压力涨价（每份加价金额），归总部 (S0000000001)

  // 省市选择 (购买时即选择，后续复用)
  provinceCode    String    @map("province_code") @db.VarChar(10)
  cityCode        String    @map("city_code") @db.VarChar(10)

  // 订单状态: CREATED → PAID → MERGED
  status          String    @default("CREATED") @map("status") @db.VarChar(20)

  // 合并关联
  mergedToMergeId BigInt?   @map("merged_to_merge_id")
  mergedAt        DateTime? @map("merged_at")

  // 时间戳
  createdAt       DateTime  @default(now()) @map("created_at")
  paidAt          DateTime? @map("paid_at")
  updatedAt       DateTime  @updatedAt @map("updated_at")

  @@index([userId])
  @@index([accountSequence])
  @@index([orderNo])
  @@index([status])
  @@index([mergedToMergeId])
  @@index([createdAt])
  @@map("pre_planting_orders")
}

// ============================================
// 预种持仓表 (每用户一条)
// 跟踪用户累计购买份数和合并状态
// ============================================
model PrePlantingPosition {
  id                BigInt    @id @default(autoincrement()) @map("position_id")
  userId            BigInt    @unique @map("user_id")
  accountSequence   String    @unique @map("account_sequence") @db.VarChar(20)

  // 持仓统计
  totalPortions     Int       @default(0) @map("total_portions")
  availablePortions Int       @default(0) @map("available_portions")
  mergedPortions    Int       @default(0) @map("merged_portions")
  totalTreesMerged  Int       @default(0) @map("total_trees_merged")

  // 省市 (首次购买时选择，后续续购锁定复用，不可更改)
  // provinceCode/cityCode：用于业务校验（续购省市一致性校验）和合并记录关联
  // provinceName/cityName：仅用于展示，2026-02-28 新增（migration: 20260228000000）
  provinceCode      String?   @map("province_code") @db.VarChar(10)
  provinceName      String?   @map("province_name") @db.VarChar(50)
  cityCode          String?   @map("city_code") @db.VarChar(10)
  cityName          String?   @map("city_name") @db.VarChar(50)

  // 首次购买时间 (1年冻结起点)
  firstPurchaseAt   DateTime? @map("first_purchase_at")

  // 时间戳
  createdAt         DateTime  @default(now()) @map("created_at")
  updatedAt         DateTime  @updatedAt @map("updated_at")

  @@index([userId])
  @@index([accountSequence])
  @@index([totalTreesMerged])
  @@map("pre_planting_positions")
}

// ============================================
// 预种合并记录表
// 5 份预种合并为 1 棵树，不进入现有 planting_orders 表
// ============================================
model PrePlantingMerge {
  id              BigInt    @id @default(autoincrement()) @map("merge_id")
  mergeNo         String    @unique @map("merge_no") @db.VarChar(50)
  userId          BigInt    @map("user_id")
  accountSequence String    @map("account_sequence") @db.VarChar(20)

  // 合并来源
  sourceOrderNos  Json      @map("source_order_nos")
  treeCount       Int       @default(1) @map("tree_count")

  // 省市 (从 PrePlantingPosition 带入)
  provinceCode    String?   @map("province_code") @db.VarChar(10)
  cityCode        String?   @map("city_code") @db.VarChar(10)

  // 合同签署: PENDING → SIGNED → EXPIRED
  contractStatus  String    @default("PENDING") @map("contract_status") @db.VarChar(20)
  contractSignedAt DateTime? @map("contract_signed_at")

  // 挖矿
  miningEnabledAt DateTime? @map("mining_enabled_at")

  // 时间戳
  mergedAt        DateTime  @default(now()) @map("merged_at")
  updatedAt       DateTime  @updatedAt @map("updated_at")

  @@index([userId])
  @@index([accountSequence])
  @@index([mergeNo])
  @@index([contractStatus])
  @@map("pre_planting_merges")
}

// ============================================
// 预种分配记录表
// 记录每笔预种订单的 10 类权益分配明细
// 独立于 reward-service，不经过现有分配流程
// ============================================
model PrePlantingRewardEntry {
  id                       BigInt   @id @default(autoincrement()) @map("entry_id")

  // 来源
  sourceOrderNo            String   @map("source_order_no") @db.VarChar(50)
  sourceAccountSequence    String   @map("source_account_sequence") @db.VarChar(20)

  // 接收者
  recipientAccountSequence String   @map("recipient_account_sequence") @db.VarChar(20)

  // 权益信息
  rightType                String   @map("right_type") @db.VarChar(50)
  usdtAmount               Decimal  @map("usdt_amount") @db.Decimal(20, 8)

  // 状态: SETTLED / PENDING / EXPIRED
  rewardStatus             String   @default("SETTLED") @map("reward_status") @db.VarChar(20)

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

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

  @@index([sourceOrderNo])
  @@index([sourceAccountSequence])
  @@index([recipientAccountSequence])
  @@index([rightType])
  @@index([rewardStatus])
  @@index([createdAt])
  @@map("pre_planting_reward_entries")
}

// ============================================
// 转让记录表（纯新增，树转让功能）
// 记录每笔树转让在 planting-service 中的状态
// LOCKED → TRANSFERRED → ROLLED_BACK
// ============================================
model PlantingTransferRecord {
  id                   BigInt    @id @default(autoincrement())
  transferOrderNo      String    @unique @map("transfer_order_no") @db.VarChar(50)

  // ========== 原订单信息 ==========
  sourceOrderNo        String    @map("source_order_no") @db.VarChar(50)
  sourceOrderId        BigInt    @map("source_order_id")

  // ========== 卖方 ==========
  fromUserId           BigInt    @map("from_user_id")
  fromAccountSequence  String    @map("from_account_sequence") @db.VarChar(20)

  // ========== 买方（LOCK 阶段为空，EXECUTE 阶段填入）==========
  toUserId             BigInt?   @map("to_user_id")
  toAccountSequence    String?   @map("to_account_sequence") @db.VarChar(20)

  // ========== 转让内容 ==========
  treeCount            Int       @map("tree_count")
  selectedProvince     String    @map("selected_province") @db.VarChar(10)
  selectedCity         String    @map("selected_city") @db.VarChar(10)
  contributionPerTree  Decimal   @map("contribution_per_tree") @db.Decimal(20, 10)
  originalAdoptionDate DateTime  @map("original_adoption_date") @db.Date
  originalExpireDate   DateTime  @map("original_expire_date") @db.Date

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

  lockedAt             DateTime? @map("locked_at")
  transferredAt        DateTime? @map("transferred_at")
  rolledBackAt         DateTime? @map("rolled_back_at")
  createdAt            DateTime  @default(now()) @map("created_at")

  @@index([sourceOrderNo])
  @@index([fromUserId])
  @@index([toUserId])
  @@index([status])
  @@map("planting_transfer_records")
}
