// =============================================================================
// 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?   @unique
  phone        String    @unique
  passwordHash String?
  fullName     String
  avatarUrl    String?
  role         UserRole  @default(BUYER)
  kycStatus    KYCStatus @default(NONE)
  kycData      Json?
  isActive     Boolean   @default(true)
  createdAt    DateTime  @default(now())
  updatedAt    DateTime  @updatedAt

  agent              Agent?
  listings           Listing[]
  savedSearches      SavedSearch[]
  subscription       Subscription?
  payments           Payment[]
  reviews            Review[]
  inquiriesSent      Inquiry[]
  refreshTokens      RefreshToken[]
  oauthAccounts      OAuthAccount[]
  buyerTransactions  Transaction[]   @relation("BuyerTransactions")

  @@index([phone])
  @@index([role])
  @@index([kycStatus])
  @@index([isActive])
  @@index([createdAt])
}

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])
  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])
}

// =============================================================================
// 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?
  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)

  // --- 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])
  agentId          String?
  agent            Agent?          @relation(fields: [agentId], references: [id])
  sellerId         String
  seller           User            @relation(fields: [sellerId], references: [id])
  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[]

  // --- 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)])
}

// =============================================================================
// 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])
  buyerId       String
  buyer         User              @relation("BuyerTransactions", fields: [buyerId], references: [id])
  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])
  userId    String
  user      User     @relation(fields: [userId], references: [id])
  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])
  name      String
  phone     String
  email     String?
  source    String
  score     Float?
  notes     Json?
  status    LeadStatus @default(NEW)
  createdAt DateTime   @default(now())
  updatedAt DateTime   @updatedAt

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

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

enum PaymentProvider {
  VNPAY
  MOMO
  ZALOPAY
  BANK_TRANSFER
}

enum PaymentStatus {
  PENDING
  PROCESSING
  COMPLETED
  FAILED
  REFUNDED
}

enum PaymentType {
  SUBSCRIPTION
  LISTING_FEE
  DEPOSIT
  FEATURED_LISTING
}

model Payment {
  id            String          @id @default(cuid())
  userId        String
  user          User            @relation(fields: [userId], references: [id])
  transactionId String?
  transaction   Transaction?    @relation(fields: [transactionId], references: [id])
  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([status])
  @@index([providerTxId])
  @@index([createdAt])
}

// =============================================================================
// 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?
  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])
  planId             String
  plan               Plan               @relation(fields: [planId], references: [id])
  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])
  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])
  estimatedPrice BigInt
  confidence     Float
  pricePerM2     Float
  comparables    Json
  features       Json
  modelVersion   String
  createdAt      DateTime @default(now())

  @@index([propertyId])
}

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])
}

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])
}

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

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

  @@index([targetType, targetId])
  @@index([userId])
}
