// =============================================================================
// GoodGo Platform — Prisma Schema
// PostgreSQL 16 + PostGIS
// =============================================================================

generator client {
  provider        = "prisma-client-js"
  previewFeatures = ["postgresqlExtensions"]
}

datasource db {
  provider   = "postgresql"
  extensions = [postgis]
}

// =============================================================================
// AUTH
// =============================================================================

enum UserRole {
  BUYER
  SELLER
  AGENT
  ADMIN
}

enum KYCStatus {
  NONE
  PENDING
  VERIFIED
  REJECTED
}

model User {
  id                  String    @id @default(cuid())
  email               String?
  emailHash           String?   @unique
  phone               String
  phoneHash           String?   @unique
  passwordHash        String?
  fullName            String
  avatarUrl           String?
  role                UserRole  @default(BUYER)
  kycStatus           KYCStatus @default(NONE)
  kycData             Json?
  isActive            Boolean   @default(true)
  deletedAt           DateTime?
  deletionScheduledAt DateTime?
  createdAt           DateTime  @default(now())
  updatedAt           DateTime  @updatedAt

  // MFA fields
  totpSecret      String? // Encrypted TOTP secret
  totpEnabled     Boolean   @default(false)
  totpBackupCodes String[] // Bcrypt-hashed backup codes
  totpEnabledAt   DateTime?

  agent             Agent?
  listings          Listing[]
  savedSearches     SavedSearch[]
  subscription      Subscription?
  payments          Payment[]
  reviews           Review[]
  inquiriesSent     Inquiry[]
  refreshTokens     RefreshToken[]
  oauthAccounts     OAuthAccount[]
  buyerTransactions Transaction[]  @relation("BuyerTransactions")
  buyerOrders       Order[]        @relation("BuyerOrders")
  sellerOrders      Order[]        @relation("SellerOrders")
  mfaChallenges     MfaChallenge[]
  transferListings  TransferListing[]
  reports           Report[]

  @@index([role])
  @@index([kycStatus])
  @@index([isActive])
  @@index([deletedAt])
  @@index([deletionScheduledAt])
  @@index([createdAt])
  // --- Compound indexes (query optimization) ---
  @@index([role, isActive, createdAt(sort: Desc)])
  @@index([kycStatus, createdAt])
}

model MfaChallenge {
  id           String   @id @default(cuid())
  userId       String
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  type         String // "totp" | "backup_code"
  attemptCount Int      @default(0)
  maxAttempts  Int      @default(5)
  isVerified   Boolean  @default(false)
  expiresAt    DateTime
  createdAt    DateTime @default(now())

  @@index([userId, expiresAt])
  @@index([expiresAt])
}

enum OAuthProvider {
  GOOGLE
  ZALO
}

model RefreshToken {
  id        String    @id @default(cuid())
  userId    String
  user      User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  token     String    @unique
  family    String
  expiresAt DateTime
  revokedAt DateTime?
  createdAt DateTime  @default(now())

  @@index([userId])
  @@index([family])
  @@index([expiresAt])
}

model OAuthAccount {
  id             String        @id @default(cuid())
  userId         String
  user           User          @relation(fields: [userId], references: [id], onDelete: Cascade)
  provider       OAuthProvider
  providerUserId String
  accessToken    String?
  refreshToken   String?
  expiresAt      DateTime?
  profile        Json?
  createdAt      DateTime      @default(now())
  updatedAt      DateTime      @updatedAt

  @@unique([provider, providerUserId])
  @@index([userId])
}

model Agent {
  id              String   @id @default(cuid())
  userId          String   @unique
  user            User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  licenseNumber   String?
  agency          String?
  qualityScore    Float    @default(0)
  totalDeals      Int      @default(0)
  responseTimeAvg Int?
  bio             String?
  serviceAreas    Json // ["quan-1", "quan-7", "thu-duc"]
  isVerified      Boolean  @default(false)
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt

  listings Listing[]
  leads    Lead[]

  @@index([qualityScore])
  @@index([isVerified])
}

// =============================================================================
// PROJECT DEVELOPMENTS
// =============================================================================

enum ProjectDevelopmentStatus {
  PLANNING
  UNDER_CONSTRUCTION
  COMPLETED
  HANDOVER
}

model ProjectDevelopment {
  id              String                               @id @default(cuid())
  name            String
  slug            String                               @unique
  developer       String
  developerLogo   String?
  totalUnits      Int
  completedUnits  Int                                  @default(0)
  status          ProjectDevelopmentStatus             @default(PLANNING)
  startDate       DateTime?
  completionDate  DateTime?
  description     String?                              @db.Text
  amenities       Json?
  masterPlanUrl   String?
  location        Unsupported("geometry(Point, 4326)")
  address         String
  ward            String
  district        String
  city            String
  minPrice        BigInt?
  maxPrice        BigInt?
  pricePerM2Range Json?
  totalArea       Float?
  buildingCount   Int?
  floorCount      Int?
  unitTypes       Json?
  media           Json?
  documents       Json?
  tags            String[]
  isVerified      Boolean                              @default(false)
  createdAt       DateTime                             @default(now())
  updatedAt       DateTime                             @updatedAt

  properties Property[]

  @@index([status])
  @@index([district, city])
  @@index([developer])
  @@index([location], type: Gist)
  @@index([isVerified])
  @@index([createdAt])
  @@index([district, city, status])
}

// =============================================================================
// LISTINGS
// =============================================================================

enum PropertyType {
  APARTMENT
  VILLA
  TOWNHOUSE
  LAND
  OFFICE
  SHOPHOUSE
}

enum TransactionType {
  SALE
  RENT
}

enum ListingStatus {
  DRAFT
  PENDING_REVIEW
  ACTIVE
  RESERVED
  SOLD
  RENTED
  EXPIRED
  REJECTED
}

enum Direction {
  NORTH
  SOUTH
  EAST
  WEST
  NORTHEAST
  NORTHWEST
  SOUTHEAST
  SOUTHWEST
}

model Property {
  id                   String                               @id @default(cuid())
  propertyType         PropertyType
  title                String
  description          String                               @db.Text
  address              String
  ward                 String
  district             String
  city                 String
  location             Unsupported("geometry(Point, 4326)")
  areaM2               Float
  usableAreaM2         Float?
  bedrooms             Int?
  bathrooms            Int?
  floors               Int?
  floor                Int?
  totalFloors          Int?
  direction            Direction?
  yearBuilt            Int?
  legalStatus          String?
  amenities            Json?
  nearbyPOIs           Json?
  metroDistanceM       Float?
  projectName          String?
  projectDevelopmentId String?
  projectDevelopment   ProjectDevelopment?                  @relation(fields: [projectDevelopmentId], references: [id], onDelete: SetNull)
  createdAt            DateTime                             @default(now())
  updatedAt            DateTime                             @updatedAt

  listings   Listing[]
  valuations Valuation[]
  media      PropertyMedia[]

  // --- Single-column indexes ---
  @@index([propertyType])
  @@index([district, city])
  @@index([location], type: Gist)
  @@index([projectDevelopmentId])
  // --- Compound indexes (query optimization) ---
  @@index([district, propertyType])
  @@index([district, city, propertyType])
}

model PropertyMedia {
  id         String   @id @default(cuid())
  propertyId String
  property   Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
  url        String
  type       String // "image" | "video"
  order      Int      @default(0)
  caption    String?
  aiTags     Json?
  createdAt  DateTime @default(now())

  @@index([propertyId])
}

model Listing {
  id               String          @id @default(cuid())
  propertyId       String
  property         Property        @relation(fields: [propertyId], references: [id], onDelete: Cascade)
  agentId          String?
  agent            Agent?          @relation(fields: [agentId], references: [id], onDelete: SetNull)
  sellerId         String
  seller           User            @relation(fields: [sellerId], references: [id], onDelete: Restrict)
  transactionType  TransactionType
  status           ListingStatus   @default(DRAFT)
  priceVND         BigInt
  pricePerM2       Float?
  rentPriceMonthly BigInt?
  commissionPct    Float?          @default(2.0)
  aiPriceEstimate  BigInt?
  aiConfidence     Float?
  moderationScore  Float?
  moderationNotes  String?
  viewCount        Int             @default(0)
  saveCount        Int             @default(0)
  inquiryCount     Int             @default(0)
  featuredUntil    DateTime?
  expiresAt        DateTime?
  publishedAt      DateTime?
  createdAt        DateTime        @default(now())
  updatedAt        DateTime        @updatedAt

  transactions  Transaction[]
  inquiries     Inquiry[]
  orders        Order[]
  priceHistories PriceHistory[]

  // --- Single-column indexes ---
  @@index([status])
  @@index([transactionType])
  @@index([priceVND])
  @@index([sellerId])
  @@index([propertyId])
  @@index([agentId])
  @@index([publishedAt])
  @@index([createdAt])
  @@index([featuredUntil])
  @@index([expiresAt])
  // --- Compound indexes (query optimization) ---
  @@index([sellerId, status, publishedAt(sort: Desc)])
  @@index([agentId, status])
  @@index([status, createdAt(sort: Desc)])
  @@index([status, publishedAt(sort: Desc)])
  @@index([transactionType, status, createdAt(sort: Desc)])
  @@index([status, transactionType, priceVND])
}

model PriceHistory {
  id        String   @id @default(cuid())
  listingId String
  listing   Listing  @relation(fields: [listingId], references: [id], onDelete: Cascade)
  oldPrice  BigInt
  newPrice  BigInt
  changedAt DateTime @default(now())

  @@index([listingId, changedAt(sort: Desc)])
}

// =============================================================================
// SEARCH
// =============================================================================

model SavedSearch {
  id           String    @id @default(cuid())
  userId       String
  user         User      @relation(fields: [userId], references: [id], onDelete: Cascade)
  name         String
  filters      Json
  alertEnabled Boolean   @default(true)
  lastAlertAt  DateTime?
  createdAt    DateTime  @default(now())

  @@index([userId])
}

// =============================================================================
// TRANSACTIONS
// =============================================================================

enum TransactionStatus {
  INQUIRY
  VIEWING_SCHEDULED
  OFFER_MADE
  DEPOSIT_PAID
  CONTRACT_SIGNING
  COMPLETED
  CANCELLED
}

model Transaction {
  id            String            @id @default(cuid())
  listingId     String
  listing       Listing           @relation(fields: [listingId], references: [id], onDelete: Cascade)
  buyerId       String
  buyer         User              @relation("BuyerTransactions", fields: [buyerId], references: [id], onDelete: Restrict)
  status        TransactionStatus @default(INQUIRY)
  agreedPrice   BigInt?
  depositAmount BigInt?
  timeline      Json?
  contractUrl   String?
  createdAt     DateTime          @default(now())
  updatedAt     DateTime          @updatedAt

  payments Payment[]

  @@index([listingId])
  @@index([buyerId])
  @@index([status])
}

model Inquiry {
  id        String   @id @default(cuid())
  listingId String
  listing   Listing  @relation(fields: [listingId], references: [id], onDelete: Cascade)
  userId    String
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  message   String   @db.Text
  phone     String?
  isRead    Boolean  @default(false)
  createdAt DateTime @default(now())

  @@index([listingId])
  @@index([userId])
  @@index([listingId, userId])
}

enum LeadStatus {
  NEW
  CONTACTED
  QUALIFIED
  NEGOTIATING
  CONVERTED
  LOST
}

model Lead {
  id        String     @id @default(cuid())
  agentId   String
  agent     Agent      @relation(fields: [agentId], references: [id], onDelete: Cascade)
  name      String
  phone     String
  phoneHash String?
  email     String?
  emailHash String?
  source    String
  score     Float?
  notes     Json?
  status    LeadStatus @default(NEW)
  createdAt DateTime   @default(now())
  updatedAt DateTime   @updatedAt

  @@index([agentId])
  @@index([status])
  @@index([phoneHash])
  @@index([emailHash])
}

// =============================================================================
// PAYMENTS
// =============================================================================

enum PaymentProvider {
  VNPAY
  MOMO
  ZALOPAY
  BANK_TRANSFER
}

enum PaymentStatus {
  PENDING
  PROCESSING
  COMPLETED
  FAILED
  REFUNDED
}

enum PaymentType {
  SUBSCRIPTION
  LISTING_FEE
  DEPOSIT
  FEATURED_LISTING
  AUCTION_PAYMENT
}

model Payment {
  id             String          @id @default(cuid())
  userId         String
  user           User            @relation(fields: [userId], references: [id], onDelete: Restrict)
  transactionId  String?
  transaction    Transaction?    @relation(fields: [transactionId], references: [id], onDelete: SetNull)
  orderId        String?
  order          Order?          @relation(fields: [orderId], references: [id], onDelete: SetNull)
  provider       PaymentProvider
  type           PaymentType
  amountVND      BigInt
  status         PaymentStatus   @default(PENDING)
  providerTxId   String?
  callbackData   Json?
  idempotencyKey String?
  createdAt      DateTime        @default(now())
  updatedAt      DateTime        @updatedAt

  @@unique([userId, provider, idempotencyKey], name: "Payment_idempotency_unique")
  @@index([userId])
  @@index([transactionId])
  @@index([orderId])
  @@index([status])
  @@index([providerTxId])
  @@index([createdAt])
  // --- Compound indexes (query optimization) ---
  @@index([userId, status, createdAt(sort: Desc)])
  @@index([userId, type, createdAt(sort: Desc)])
}

// =============================================================================
// ORDERS & ESCROW (Auction Settlement)
// =============================================================================

enum OrderStatus {
  CREATED
  PAYMENT_PENDING
  PAYMENT_CONFIRMED
  ESCROW_HELD
  SHIPPED
  DELIVERED
  DISPUTE
  ESCROW_RELEASED
  COMPLETED
  CANCELLED
  REFUNDED
}

enum EscrowStatus {
  PENDING
  HELD
  RELEASED
  REFUNDED
  DISPUTED
}

model Order {
  id              String      @id @default(cuid())
  buyerId         String
  buyer           User        @relation("BuyerOrders", fields: [buyerId], references: [id], onDelete: Restrict)
  sellerId        String
  seller          User        @relation("SellerOrders", fields: [sellerId], references: [id], onDelete: Restrict)
  listingId       String
  listing         Listing     @relation(fields: [listingId], references: [id], onDelete: Restrict)
  status          OrderStatus @default(CREATED)
  amountVND       BigInt
  platformFeeVND  BigInt
  sellerPayoutVND BigInt
  idempotencyKey  String?     @unique
  metadata        Json?
  createdAt       DateTime    @default(now())
  updatedAt       DateTime    @updatedAt

  payments Payment[]
  escrow   Escrow?

  @@index([buyerId])
  @@index([sellerId])
  @@index([listingId])
  @@index([status])
  @@index([createdAt(sort: Desc)])
}

model Escrow {
  id            String       @id @default(cuid())
  orderId       String       @unique
  order         Order        @relation(fields: [orderId], references: [id], onDelete: Restrict)
  amountVND     BigInt
  feeVND        BigInt
  status        EscrowStatus @default(PENDING)
  heldAt        DateTime?
  releasedAt    DateTime?
  disputeReason String?      @db.Text
  disputedAt    DateTime?
  createdAt     DateTime     @default(now())
  updatedAt     DateTime     @updatedAt

  @@index([status])
  @@index([orderId])
}

// =============================================================================
// SUBSCRIPTIONS
// =============================================================================

enum PlanTier {
  FREE
  AGENT_PRO
  INVESTOR
  ENTERPRISE
}

enum SubscriptionStatus {
  ACTIVE
  PAST_DUE
  CANCELLED
  EXPIRED
}

model Plan {
  id                  String   @id @default(cuid())
  tier                PlanTier @unique
  name                String
  priceMonthlyVND     BigInt
  priceYearlyVND      BigInt
  maxListings         Int?
  maxSavedSearches    Int?
  maxAnalyticsQueries Int?
  maxReports          Int?
  maxMediaUploads     Int?
  features            Json
  isActive            Boolean  @default(true)

  subscriptions Subscription[]
}

model Subscription {
  id                 String             @id @default(cuid())
  userId             String             @unique
  user               User               @relation(fields: [userId], references: [id], onDelete: Cascade)
  planId             String
  plan               Plan               @relation(fields: [planId], references: [id], onDelete: Restrict)
  status             SubscriptionStatus @default(ACTIVE)
  currentPeriodStart DateTime
  currentPeriodEnd   DateTime
  cancelledAt        DateTime?
  createdAt          DateTime           @default(now())
  updatedAt          DateTime           @updatedAt

  usageRecords UsageRecord[]

  @@index([planId])
  @@index([status])
}

model UsageRecord {
  id             String       @id @default(cuid())
  subscriptionId String
  subscription   Subscription @relation(fields: [subscriptionId], references: [id], onDelete: Cascade)
  metric         String
  count          Int
  periodStart    DateTime
  periodEnd      DateTime

  @@index([subscriptionId, metric])
}

// =============================================================================
// ANALYTICS
// =============================================================================

model Valuation {
  id             String   @id @default(cuid())
  propertyId     String
  property       Property @relation(fields: [propertyId], references: [id], onDelete: Cascade)
  estimatedPrice BigInt
  confidence     Float
  pricePerM2     Float
  comparables    Json
  features       Json
  modelVersion   String
  createdAt      DateTime @default(now())

  @@index([propertyId])
  @@index([propertyId, createdAt(sort: Desc)])
}

model MarketIndex {
  id             String       @id @default(cuid())
  district       String
  city           String
  propertyType   PropertyType
  period         String
  medianPrice    BigInt
  avgPriceM2     Float
  totalListings  Int
  daysOnMarket   Float
  inventoryLevel Int
  absorptionRate Float?
  yoyChange      Float?
  createdAt      DateTime     @default(now())

  @@unique([district, city, propertyType, period])
  @@index([city, period])
}

// =============================================================================
// NOTIFICATIONS
// =============================================================================

enum NotificationChannel {
  EMAIL
  SMS
  PUSH
  ZALO_OA
}

enum NotificationStatus {
  PENDING
  SENT
  FAILED
  DELIVERED
}

model NotificationLog {
  id          String              @id @default(cuid())
  userId      String
  channel     NotificationChannel
  templateKey String
  subject     String?
  body        String              @db.Text
  metadata    Json?
  status      NotificationStatus  @default(PENDING)
  errorDetail String?
  sentAt      DateTime?
  readAt      DateTime?
  createdAt   DateTime            @default(now())

  @@index([userId])
  @@index([channel, status])
  @@index([templateKey])
  @@index([createdAt])
  @@index([userId, readAt])
  @@index([userId, createdAt(sort: Desc)])
}

model NotificationPreference {
  id        String              @id @default(cuid())
  userId    String
  channel   NotificationChannel
  eventType String
  enabled   Boolean             @default(true)
  createdAt DateTime            @default(now())
  updatedAt DateTime            @updatedAt

  @@unique([userId, channel, eventType])
  @@index([userId])
}

// =============================================================================
// ADMIN AUDIT LOG
// =============================================================================

enum AdminAction {
  LISTING_APPROVED
  LISTING_REJECTED
  LISTING_BULK_APPROVED
  LISTING_BULK_REJECTED
  USER_BANNED
  USER_UNBANNED
  USER_STATUS_UPDATED
  KYC_APPROVED
  KYC_REJECTED
  SUBSCRIPTION_ADJUSTED
}

enum AuditTargetType {
  USER
  LISTING
  SUBSCRIPTION
}

model AdminAuditLog {
  id         String          @id @default(cuid())
  action     AdminAction
  actorId    String
  targetId   String
  targetType AuditTargetType
  metadata   Json?
  ipAddress  String?
  userAgent  String?
  createdAt  DateTime        @default(now())

  @@index([actorId])
  @@index([targetId, targetType])
  @@index([action])
  @@index([createdAt])
  @@index([actorId, createdAt(sort: Desc)])
  @@index([action, createdAt(sort: Desc)])
}

// =============================================================================
// NEIGHBORHOOD & POI
// =============================================================================

enum POIType {
  SCHOOL
  UNIVERSITY
  HOSPITAL
  CLINIC
  METRO_STATION
  BUS_STOP
  MALL
  MARKET
  SUPERMARKET
  PARK
  POLICE_STATION
  FIRE_STATION
  BANK
  ATM
  RESTAURANT
  CAFE
  GYM
  PHARMACY
}

model POI {
  id       String                               @id @default(cuid())
  name     String
  type     POIType
  location Unsupported("geometry(Point, 4326)")
  address  String?
  ward     String?
  district String
  city     String
  osmId    String?  @unique
  metadata Json?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([type])
  @@index([district, city])
  @@index([type, district, city])
  @@index([location], type: Gist)
  @@index([osmId])
}

model NeighborhoodScore {
  id              String   @id @default(cuid())
  district        String
  city            String
  educationScore  Float    // 0-10: schools/universities within 2km
  healthcareScore Float    // 0-10: hospitals/clinics within 3km
  transportScore  Float    // 0-10: metro/bus within 1km
  shoppingScore   Float    // 0-10: mall/market within 2km
  greeneryScore   Float    // 0-10: parks within 1km
  safetyScore     Float    // 0-10: police/fire stations + safety index
  totalScore      Float    // 0-100: weighted average
  poiCounts       Json     // { education: 12, healthcare: 5, ... }
  calculatedAt    DateTime @default(now())
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt

  @@unique([district, city])
  @@index([totalScore(sort: Desc)])
  @@index([city])
}

// =============================================================================
// REVIEWS
// =============================================================================

model Review {
  id         String   @id @default(cuid())
  userId     String
  user       User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  targetType String
  targetId   String
  rating     Int
  comment    String?  @db.Text
  createdAt  DateTime @default(now())

  @@index([targetType, targetId])
  @@index([userId])
  @@index([targetType, targetId, createdAt(sort: Desc)])
}

// =============================================================================
// INDUSTRIAL PARKS (KCN)
// =============================================================================

enum IndustrialParkStatus {
  PLANNING
  UNDER_CONSTRUCTION
  OPERATIONAL
  FULL
}

enum IndustrialPropertyType {
  INDUSTRIAL_LAND
  READY_BUILT_FACTORY
  READY_BUILT_WAREHOUSE
  LOGISTICS_CENTER
  OFFICE_IN_PARK
  DATA_CENTER
}

enum IndustrialLeaseType {
  LAND_LEASE
  FACTORY_LEASE
  WAREHOUSE_LEASE
  SUBLEASE
}

enum IndustrialListingStatus {
  DRAFT
  ACTIVE
  RESERVED
  LEASED
  EXPIRED
}

enum VietnamRegion {
  NORTH
  CENTRAL
  SOUTH
}

model IndustrialPark {
  id                String                               @id @default(cuid())
  name              String
  nameEn            String?
  slug              String                               @unique
  developer         String
  operator          String?
  status            IndustrialParkStatus                 @default(PLANNING)
  location          Unsupported("geometry(Point, 4326)")
  address           String
  district          String
  province          String
  region            VietnamRegion
  totalAreaHa       Float
  leasableAreaHa    Float
  occupancyRate     Float                                @default(0) // 0-100
  remainingAreaHa   Float
  tenantCount       Int                                  @default(0)
  establishedYear   Int?
  landRentUsdM2Year Float?
  rbfRentUsdM2Month Float?
  rbwRentUsdM2Month Float?
  managementFeeUsd  Float?
  infrastructure    Json? // { electricity, water, wastewater, telecom, roads, fire }
  connectivity      Json? // { nearestPort, airport, highway, railway, seaport }
  incentives        Json? // { taxHoliday, importDuty, landRentReduction, specialZone }
  targetIndustries  String[]
  existingTenants   Json? // [{ name, country, industry }]
  certifications    Json? // ["ISO 14001", "Green park"]
  media             Json?
  documents         Json?
  description       String?                              @db.Text
  descriptionEn     String?                              @db.Text
  isVerified        Boolean                              @default(false)
  createdAt         DateTime                             @default(now())
  updatedAt         DateTime                             @updatedAt

  listings IndustrialListing[]

  @@index([status])
  @@index([province])
  @@index([region])
  @@index([developer])
  @@index([location], type: Gist)
  @@index([isVerified])
  @@index([occupancyRate])
  @@index([landRentUsdM2Year])
  @@index([region, province, status])
  @@index([createdAt])
}

model IndustrialListing {
  id                String                               @id @default(cuid())
  parkId            String
  park              IndustrialPark                       @relation(fields: [parkId], references: [id], onDelete: Cascade)
  agentId           String?
  sellerId          String
  propertyType      IndustrialPropertyType
  leaseType         IndustrialLeaseType
  status            IndustrialListingStatus              @default(DRAFT)
  title             String
  description       String?                              @db.Text
  areaM2            Float
  ceilingHeightM    Float?
  floorLoadTonM2    Float?
  columnSpacingM    Float?
  dockCount         Int?
  craneCapacityTon  Float?
  hasMezzanine      Boolean                              @default(false)
  hasOfficeArea     Boolean                              @default(false)
  officeAreaM2      Float?
  priceUsdM2        Float?
  pricingUnit       String? // "usd/m2/month", "usd/m2/year"
  totalLeasePrice   Float?
  managementFee     Float?
  depositMonths     Int?
  minLeaseYears     Int?
  maxLeaseYears     Int?
  leaseExpiry       DateTime?
  availableFrom     DateTime?
  powerCapacityKva  Float?
  waterSupplyM3Day  Float?
  media             Json?
  viewCount         Int                                  @default(0)
  inquiryCount      Int                                  @default(0)
  publishedAt       DateTime?
  createdAt         DateTime                             @default(now())
  updatedAt         DateTime                             @updatedAt

  @@index([parkId])
  @@index([propertyType])
  @@index([leaseType])
  @@index([status])
  @@index([areaM2])
  @@index([priceUsdM2])
  @@index([sellerId])
  @@index([agentId])
  @@index([publishedAt])
  @@index([parkId, status])
  @@index([propertyType, leaseType, status])
  @@index([status, publishedAt(sort: Desc)])
}

// =============================================================================
// MESSAGING (buyer ↔ agent / seller in-app chat)
// =============================================================================

enum ConversationStatus {
  ACTIVE
  ARCHIVED
  CLOSED
}

model Conversation {
  id          String             @id @default(cuid())
  listingId   String?
  subject     String?
  status      ConversationStatus @default(ACTIVE)
  lastMessage String?            @db.Text
  lastMessageAt DateTime?
  createdAt   DateTime           @default(now())
  updatedAt   DateTime           @updatedAt

  participants ConversationParticipant[]
  messages     Message[]

  @@index([status])
  @@index([lastMessageAt(sort: Desc)])
  @@index([listingId])
}

model ConversationParticipant {
  id             String       @id @default(cuid())
  conversationId String
  conversation   Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
  userId         String
  unreadCount    Int          @default(0)
  lastReadAt     DateTime?
  joinedAt       DateTime     @default(now())

  @@unique([conversationId, userId])
  @@index([userId])
  @@index([conversationId])
}

enum MessageType {
  TEXT
  IMAGE
  FILE
  SYSTEM
}

model Message {
  id             String       @id @default(cuid())
  conversationId String
  conversation   Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
  senderId       String
  type           MessageType  @default(TEXT)
  content        String       @db.Text
  metadata       Json?
  editedAt       DateTime?
  deletedAt      DateTime?
  createdAt      DateTime     @default(now())

  @@index([conversationId, createdAt])
  @@index([senderId])
}

// =============================================================================
// TRANSFER (Furniture + Premises Handover)
// =============================================================================

enum TransferCategory {
  FURNITURE          // Nội thất (sofa, bàn, tủ, giường)
  APPLIANCE          // Thiết bị gia dụng (máy lạnh, tủ lạnh, máy giặt)
  OFFICE_EQUIPMENT   // Thiết bị văn phòng (bàn làm việc, ghế, máy in)
  KITCHEN            // Bếp + thiết bị bếp
  PREMISES           // Mặt bằng kinh doanh
  FULL_UNIT          // Chuyển nhượng trọn bộ (nội thất + mặt bằng)
}

enum TransferCondition {
  NEW                // Mới (< 6 tháng)
  LIKE_NEW           // Như mới (6-12 tháng)
  GOOD               // Tốt (1-3 năm)
  FAIR               // Khá (3-5 năm)
  WORN               // Cũ (> 5 năm)
}

enum TransferListingStatus {
  DRAFT
  PENDING_REVIEW
  ACTIVE
  RESERVED
  SOLD
  EXPIRED
  REJECTED
}

enum TransferPricingSource {
  MANUAL             // Người bán tự định giá
  AI_ESTIMATED       // AI ước tính dựa trên khấu hao + thương hiệu
  NEGOTIABLE         // Giá thương lượng
}

model TransferListing {
  id                String                               @id @default(cuid())
  sellerId          String
  seller            User                                 @relation(fields: [sellerId], references: [id], onDelete: Restrict)
  category          TransferCategory
  status            TransferListingStatus                @default(DRAFT)
  title             String
  description       String?                              @db.Text
  // Location
  address           String
  ward              String?
  district          String
  city              String
  location          Unsupported("geometry(Point, 4326)")
  // Pricing
  askingPriceVND    BigInt
  aiEstimatePriceVND BigInt?
  aiConfidence      Float?
  pricingSource     TransferPricingSource                @default(MANUAL)
  isNegotiable      Boolean                              @default(true)
  // Premises-specific fields (for PREMISES / FULL_UNIT)
  areaM2            Float?
  monthlyRentVND    BigInt?
  depositMonths     Int?
  remainingLeaseMo  Int?
  businessType      String?                              // Loại hình kinh doanh hiện tại
  footTraffic       String?                              // Mô tả lưu lượng khách
  // Metadata
  media             Json?                                // [{ url, type, order, caption }]
  moderationScore   Float?
  moderationNotes   String?
  viewCount         Int                                  @default(0)
  saveCount         Int                                  @default(0)
  inquiryCount      Int                                  @default(0)
  contactPhone      String?
  contactName       String?
  featuredUntil     DateTime?
  expiresAt         DateTime?
  publishedAt       DateTime?
  createdAt         DateTime                             @default(now())
  updatedAt         DateTime                             @updatedAt

  items             TransferItem[]

  @@index([sellerId])
  @@index([category])
  @@index([status])
  @@index([district, city])
  @@index([askingPriceVND])
  @@index([location], type: Gist)
  @@index([publishedAt])
  @@index([createdAt])
  @@index([featuredUntil])
  @@index([expiresAt])
  @@index([category, status, publishedAt(sort: Desc)])
  @@index([district, city, category, status])
  @@index([status, createdAt(sort: Desc)])
}

model TransferItem {
  id                  String                 @id @default(cuid())
  transferListingId   String
  transferListing     TransferListing        @relation(fields: [transferListingId], references: [id], onDelete: Cascade)
  name                String                 // Tên sản phẩm (e.g. "Sofa góc L 3m")
  brand               String?                // Thương hiệu
  modelName           String?                // Model / SKU
  category            TransferCategory
  condition           TransferCondition
  purchaseYear        Int?                   // Năm mua
  originalPriceVND    BigInt?                // Giá mua ban đầu
  askingPriceVND      BigInt                 // Giá bán mong muốn
  aiEstimatePriceVND  BigInt?                // AI ước tính
  aiConfidence        Float?
  quantity            Int                    @default(1)
  dimensions          Json?                  // { widthCm, heightCm, depthCm, weightKg }
  media               Json?                  // [{ url, type, order }]
  notes               String?                @db.Text
  createdAt           DateTime               @default(now())
  updatedAt           DateTime               @updatedAt

  @@index([transferListingId])
  @@index([category])
  @@index([condition])
  @@index([brand])
  @@index([askingPriceVND])
  @@index([transferListingId, category])
}

// =============================================================================
// AI REPORTS
// =============================================================================

enum ReportType {
  RESIDENTIAL_MARKET
  INDUSTRIAL_MARKET
  DISTRICT_ANALYSIS
  INVESTMENT_FEASIBILITY
  INDUSTRIAL_LOCATION
  PROPERTY_VALUATION
  PORTFOLIO
}

enum ReportStatus {
  GENERATING
  READY
  FAILED
}

model Report {
  id        String       @id @default(cuid())
  userId    String
  user      User         @relation(fields: [userId], references: [id], onDelete: Cascade)
  type      ReportType
  title     String
  params    Json         // Input parameters (city, province, period, etc.)
  content   Json?        // Structured report content (sections, charts data)
  pdfUrl    String?      // MinIO URL to generated PDF
  status    ReportStatus @default(GENERATING)
  errorMsg  String?
  createdAt DateTime     @default(now())
  updatedAt DateTime     @updatedAt

  @@index([userId, createdAt(sort: Desc)])
  @@index([userId, type])
  @@index([status])
}

model MacroeconomicData {
  id        String   @id @default(cuid())
  province  String
  indicator String   // gdp, fdi, population, urbanization, labor_force, avg_wage, industrial_output, cpi, mortgage_rate
  value     Float
  unit      String   // USD, VND, %, persons, etc.
  period    String   // e.g. "2025", "2025-Q4"
  source    String   // GSO, World Bank, SBV
  createdAt DateTime @default(now())

  @@unique([province, indicator, period])
  @@index([province])
  @@index([indicator, period])
}

model InfrastructureProject {
  id             String                                @id @default(cuid())
  name           String
  province       String
  category       String                                // metro, highway, airport, port, bridge, industrial_zone
  status         String                                // planning, under_construction, completed
  investmentVND  BigInt?
  startDate      DateTime?
  completionDate DateTime?
  description    String?                               @db.Text
  impactRadius   Float?                                // km
  location       Unsupported("geometry(Point, 4326)")?
  createdAt      DateTime                              @default(now())
  updatedAt      DateTime                              @updatedAt

  @@index([province])
  @@index([category])
  @@index([status])
  @@index([province, category])
}
