datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id                   Int                  @id @default(autoincrement())
  name                 String
  email                String               @unique
  passwordHash         String
  role                 String               @default("agent")
  sipIdentity          String?              @unique
  status               String               @default("AVAILABLE")
  twilioSid            String?
  fcmToken             String?
  phoneNumber          String?              @unique
  createdAt            DateTime             @default(now())
  updatedAt            DateTime             @updatedAt
  twoFASecret          String?
  twoFAEnabled         Boolean              @default(false)
  autoDialEnabled    Boolean              @default(false)
  calls                Call[]
  contacts             Contact[] // ðŸ‘ˆ Added relation
  CallSession          CallSession[]
  CallLeg              CallLeg[]
  AgentStatusHistory   AgentStatusHistory[]
  messages             Message[]
  blockedNumbers       BlockedNumber[]
  agentPrivilege       AgentPrivilege?
  agentSmtp            AgentSmtp? // ← NEW: one-to-one relation (nullable)
  verificationCodes    VerificationCode[] // ← Add this
  leads                Lead[]
  sales                Sale[]
  teamMemberships      TeamMember[]
  addedTeamMembers     TeamMember[]         @relation("TeamMemberAddedBy")
  supervisedTeams      Team[]               @relation("TeamSupervisor") // 👈 Relation for supervisor
  createdTeams         Team[]               @relation("TeamCreator") // 👈 Relation for made_by
  Voicemail            Voicemail[]
  LeadLog              LeadLog[]
  LeadRemark           LeadRemark[]
  LiveCall             LiveCall[]
  Email                Email[]
  supervisedPrivileges TeamPrivilege[]      @relation("SupervisorPrivileges") // 👈 New relation for privileges
  additionalRoleId     Int? // 👈 New: Additional role ID (nullable)
  additionalRole       Role?                @relation(fields: [additionalRoleId], references: [id]) // 👈 Relation to Role
  passwordResetTokens PasswordResetToken[]
  DNCNumber   DNCNumber[]
  Disposition Disposition[]
  createdAutoDialCampaigns AutoDialCampaign[] @relation("AutoDialCampaignCreator")
  assignedAutoDialQueue    AutoDialQueue[]    @relation("AutoDialAssignedAgent")
  assignedAutoDialBy       AutoDialQueue[]    @relation("AutoDialAssignedBy")
  autoDialAttempts         AutoDialAttempt[]  @relation("AutoDialAttemptAgent")
  assignedCampaigns AutoDialCampaignAgent[] @relation("AutoDialCampaignAgents")  // ← ADD THIS
  sessions UserSession[]
  createdScripts       CallScript[]           @relation("ScriptCreator")
  scriptAssignments    CallScriptAssignment[] @relation("ScriptAssignee")
  smsTemplates         SmsTemplate[]          @relation("UserSmsTemplates")
  createdCallIpAllowlistEntries CallIpAllowlist[] @relation("CallIpAllowlistCreator")
}
model UserSession {
  id             Int       @id @default(autoincrement())
  userId         Int
  user           User      @relation(fields: [userId], references: [id], onDelete: Cascade)

  tokenVersion   String    @unique
  deviceId       String?
  deviceName     String?
  ipAddress      String?
  userAgent      String?   @db.Text

  socketId       String?
  isActive       Boolean   @default(true)
  revokedAt      DateTime?
  lastSeenAt     DateTime  @default(now())

  createdAt      DateTime  @default(now())
  updatedAt      DateTime  @updatedAt

  @@index([userId])
  @@index([isActive])
  @@index([tokenVersion])
}
model PasswordResetToken {
  id        Int      @id @default(autoincrement())
  userId    Int
  token     String   @unique
  expiresAt DateTime
  used      Boolean  @default(false)
  createdAt DateTime @default(now())

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
  @@index([token])
  @@index([expiresAt])
}

model Call {
  id            Int             @id @default(autoincrement())
  callSid       String          @unique
  fromNumber    String
  toNumber      String
  direction     String
  status        String
  startTime     DateTime
  endTime       DateTime?
  duration      Int?
  cost          Float?
  recordingUrl  String?
  userId        Int?
  user          User?           @relation(fields: [userId], references: [id])
  createdAt     DateTime        @default(now())
  CallRecording CallRecording[]
}

//
// ðŸš€ NEW TABLE: CONTACT
//
model Contact {
  id        Int  @id @default(autoincrement())
  addedById Int
  addedBy   User @relation(fields: [addedById], references: [id])

  firstName String
  lastName  String
  nickName  String?
  company   String?
  title     String?
  email     String?
  source    String?
  birthdate DateTime?
  website   String?
  notes     String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  addresses ContactAddress[]
  phones    ContactPhone[]
  autoDialQueue AutoDialQueue[]

}

//
// ðŸ“Œ NEW TABLE: CONTACT ADDRESS
//
model ContactAddress {
  id        Int     @id @default(autoincrement())
  contactId Int
  contact   Contact @relation(fields: [contactId], references: [id])

  address String
  city    String?
  state   String?
  zip     String?
  label   String?

  createdAt DateTime @default(now())
}

//
// ðŸ“ž NEW TABLE: CONTACT PHONE
//
model ContactPhone {
  id        Int     @id @default(autoincrement())
  contactId Int
  contact   Contact @relation(fields: [contactId], references: [id])

  numberE164 String
  label      String?
  isPrimary  Boolean @default(false)

  createdAt DateTime @default(now())
  autoDialQueue AutoDialQueue[]

  @@index([numberE164, contactId])
  @@index([numberE164])
}

model CallRecording {
  id        Int      @id @default(autoincrement())
  callId    Int
  call      Call     @relation(fields: [callId], references: [id])
  agentId   Int?
  url       String
  createdAt DateTime @default(now())

  @@index([callId])
}

model CallRecordingTemp {
  id             Int    @id @default(autoincrement())
  recordingSid   String @unique
  conferenceName String
  status         String
}

model CallSession {
  id         Int       @id @default(autoincrement())
  sessionId  String    @unique
  direction  String // inbound | outbound
  fromNumber String
  toNumber   String
  type       String? // answered | missed | voicemail | abandoned | transferred | connected
  status     String // ongoing | completed
  startTime  DateTime
  endTime    DateTime?
  duration   Int?

  userId       Int? // primary agent who handled call
  user         User?   @relation(fields: [userId], references: [id])
  recordingUrl String?
  recordingDuration Int?
  conferenceName    String?

  // V2 Routing fields
  routeType         RouteType?
  providerType      ProviderType?
  primaryRoute      RouteType?
  finalRoute        RouteType?
  failoverUsed      Boolean   @default(false)
  countryCode       String?
  costEstimate      Float?

  // ── Enterprise billing (actuals, set on completion) ──
  actualCost        Float?    // provider cost (twilio legs + commio + recording)
  marginAmount      Float?    // platform fee charged
  totalCharged      Float?    // actualCost + marginAmount (wallet deduction)
  carrier           String?   // commio | twilio | mixed

  legs              CallLeg[]
  transcripts       CallTranscript[]
  attempts          CallAttempt[]
  routeSelections   RouteSelectionLog[]
  statusLogs        CallStatusLog[]
  createdAt         DateTime         @default(now())
  updatedAt         DateTime         @updatedAt
  Lead        Lead[]
  Sale        Sale[]
  autoDialQueue    AutoDialQueue[]
  autoDialAttempts AutoDialAttempt[]

  @@index([direction])
  @@index([startTime])
  @@index([userId])
  @@index([conferenceName])
  @@index([routeType])
  @@index([providerType])
}

model CallLeg {
  id            Int         @id @default(autoincrement())
  callSid       String      @unique
  callSessionId Int
  session       CallSession @relation(fields: [callSessionId], references: [id])

  fromNumber  String
  toNumber    String
  direction   String? // inbound/outbound/internal
  status      String // ringing, answered, completed, busy, no-answer
  startTime   DateTime
  connectedAt DateTime?
  endTime     DateTime?

  userId Int? // which agent owned this leg
  user   User? @relation(fields: [userId], references: [id])

  isTransfer   Boolean @default(false)
  transferFrom String?
  transferTo   String?
  transferType String? // blind | supervised

  recordingUrl String?

  // V2 Routing fields
  routeType     RouteType?
  providerType  ProviderType?
  byocTrunkSid  String?
  carrierName   String?
  failureCode   String?
  failureReason String?

  createdAt DateTime @default(now())

  @@index([callSessionId])
  @@index([callSid])
  @@index([routeType])
  @@index([providerType])
}

model AgentStatusHistory {
  id        Int      @id @default(autoincrement())
  userId    Int
  status    String // READY | PAUSED | ON_CALL | ON_HOLD
  createdAt DateTime @default(now())

  user User @relation(fields: [userId], references: [id])

  @@index([userId])
  @@index([status])
  @@index([createdAt])
}

model Message {
  id      String @id @db.VarChar(36)
  agentId Int?
  agent   User?  @relation(fields: [agentId], references: [id])

  from      String           @db.VarChar(20)
  to        String           @db.VarChar(20)
  body      String           @db.Text
  direction MessageDirection
  status    MessageStatus    @default(sent)
  createdAt DateTime         @default(now())

  @@index([agentId])
}

enum MessageDirection {
  inbound
  outbound
}

enum MessageStatus {
  sent
  delivered
  failed
  received
  read
}

model BlockedNumber {
  id        Int      @id @default(autoincrement())
  number    String   @unique // E.164 format
  reason    String?
  blockedBy Int // user.id who blocked
  blockedAt DateTime @default(now())

  blocker User @relation(fields: [blockedBy], references: [id])

  @@index([number])
}

model CallTranscript {
  id String @id @default(cuid())

  callSessionId Int
  callSession   CallSession @relation(fields: [callSessionId], references: [id])

  speaker String // agent | customer
  text    String

  createdAt DateTime @default(now())

  @@index([callSessionId])
}

//
// 🚀 NEW MODEL: AgentPrivilege
//
model AgentPrivilege {
  id     Int  @id @default(autoincrement())
  userId Int  @unique
  user   User @relation(fields: [userId], references: [id], onDelete: Cascade)

  makeCall        Boolean @default(false)
  transcription   Boolean @default(false)
  transfer        Boolean @default(false)
  recording       Boolean @default(false)
  loginIp         Boolean @default(false)
  loginIpAddress  String? // optional IP if loginIp = true
  mms             Boolean @default(false)
  twoFA           Boolean @default(false)
  endCallPop      Boolean @default(false)
  emailPrivilege  Boolean @default(false)
  additionalEmail String? // optional email if emailPrivilege = true
  targetEnable    Boolean @default(false)
  targetValue     String? // optional email if emailPrivilege = true

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([userId])
}

model AgentSmtp {
  id     Int  @id @default(autoincrement())
  userId Int  @unique
  user   User @relation(fields: [userId], references: [id], onDelete: Cascade)

  host   String
  port   Int
  secure Boolean

  username String
  password String

  fromEmail String
  fromName  String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([userId])
}

model VerificationCode {
  id        Int      @id @default(autoincrement())
  userId    Int
  code      String   @db.VarChar(6) // 6-digit OTP
  expiresAt DateTime
  createdAt DateTime @default(now())

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
  @@index([code])
}

model Lead {
  id Int @id @default(autoincrement())

  clientName    String
  clientPhone   String
  clientAddress String?

  dispositionId Int?
  disposition   Disposition? @relation(fields: [dispositionId], references: [id])

  nextFollowupDate DateTime?
  alternatePhone   String?
  comments         String?

  tags Json?

  addedById Int
  addedBy   User @relation(fields: [addedById], references: [id])

  callSessionId Int?
  callSession   CallSession? @relation(fields: [callSessionId], references: [id])

  createdAt DateTime     @default(now())
  updatedAt DateTime     @updatedAt
  logs      LeadLog[]
  remarks   LeadRemark[]

  @@index([clientPhone])
  @@index([addedById])
  @@index([dispositionId])
}

model Sale {
  id Int @id @default(autoincrement())

  clientName    String
  clientPhone   String
  clientAddress String?

  services    Json
  amount      Float
  currency    String
  paymentType String
  billingDate DateTime?

  businessName        String?
  businessAddress     String?
  businessDescription String?
  businessNiche       String?

  tags Json?

  addedById Int
  addedBy   User @relation(fields: [addedById], references: [id])

  callSessionId Int?
  callSession   CallSession? @relation(fields: [callSessionId], references: [id])

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([clientPhone])
  @@index([addedById])
}

model Team {
  id           Int      @id @default(autoincrement())
  name         String
  description  String?
  supervisorId Int // Foreign key to User (supervisor)
  madeById     Int // Foreign key to User (creator)
  status       String   @default("active") // active | inactive
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  // Relations
  supervisor User            @relation("TeamSupervisor", fields: [supervisorId], references: [id])
  madeBy     User            @relation("TeamCreator", fields: [madeById], references: [id])
  members    TeamMember[]
  privileges TeamPrivilege[] // 👈 New relation for privileges

  @@index([supervisorId])
  @@index([madeById])
}

model Voicemail {
  id            Int       @id @default(autoincrement())
  callSid       String    @unique // Twilio CallSid for uniqueness
  fromNumber    String // Caller's number (E.164)
  toNumber      String // Agent's number (E.164)
  recordingSid  String? // Twilio RecordingSid (for fetching URL if needed)
  recordingUrl  String? // Full URL to MP3 (e.g., https://api.twilio.com/...mp3)
  transcription String? // Transcribed text (from Twilio)
  duration      Int       @default(0) // In seconds
  createdAt     DateTime  @default(now())
  listenedAt    DateTime? // When agent listened (for read/unread)
  userId        Int // Agent's user ID
  user          User      @relation(fields: [userId], references: [id])

  @@index([userId])
  @@index([createdAt])
}

model LeadLog {
  id     Int  @id @default(autoincrement())
  leadId Int
  lead   Lead @relation(fields: [leadId], references: [id], onDelete: Cascade)

  action  String // created | updated | status-changed | followup-set
  message String? // human readable log
  oldData Json?
  newData Json?

  userId Int
  user   User @relation(fields: [userId], references: [id])

  createdAt DateTime @default(now())

  @@index([leadId])
  @@index([userId])
}

enum CallStatus {
  connected
  not_connected
}

model LeadRemark {
  id         Int         @id @default(autoincrement())
  leadId     Int
  lead       Lead        @relation(fields: [leadId], references: [id], onDelete: Cascade)
  callStatus CallStatus? @default(connected)

  userId Int
  user   User @relation(fields: [userId], references: [id])

  note           String
  attachmentUrl  String? // full URL
  attachmentType String? // image/pdf/doc/etc

  createdAt DateTime @default(now())

  @@index([leadId])
  @@index([userId])
}

model Disposition {
  id       Int     @id @default(autoincrement())
  name     String
  color    String // Hex color e.g. #FF0000
  sequence Int     @default(0) // 👈 NEW COLUMN (ordering priority)
  status   Boolean @default(true) // true = active, false = inactive

  createdById Int
  createdBy   User @relation(fields: [createdById], references: [id], onDelete: Cascade)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  leads Lead[]

  @@index([createdById])
  @@index([status])
}

model LiveCall {
  id Int @id @default(autoincrement())

  conferenceName String @unique
  direction      String // inbound | outbound

  customerNumber String
  customerName   String? // 👈 resolved at start time

  agentId   Int?
  agentName String?

  isTransfer Boolean @default(false)

  startedAt DateTime @default(now())

  createdAt DateTime @default(now())

  agent User? @relation(fields: [agentId], references: [id])

  @@index([conferenceName])
  @@index([agentId])
  @@index([startedAt])
}

model Email {
  id        String   @id @default(uuid())
  userId    Int?
  user      User?    @relation(fields: [userId], references: [id], onDelete: SetNull)
  messageId String? // unique header se
  from      String
  to        String // primary recipient (agent email)
  cc        String? // comma separated or JSON
  subject   String?
  text      String?  @db.Text
  html      String?  @db.Text
  direction String   @default("inbound") // inbound | outbound
  status    String   @default("received") // received | sent | failed etc
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  attachments EmailAttachment[]

  @@index([userId])
  @@index([createdAt])
}

model EmailAttachment {
  id      Int    @id @default(autoincrement())
  emailId String
  email   Email  @relation(fields: [emailId], references: [id], onDelete: Cascade)

  filename    String
  contentType String
  size        Int
  path        String // e.g. /uploads/emails/{emailId}/{filename}

  createdAt DateTime @default(now())
}

// New Model: TeamPrivilege
model TeamPrivilege {
  id                 Int      @id @default(autoincrement())
  teamId             Int
  team               Team     @relation(fields: [teamId], references: [id], onDelete: Cascade)
  supervisorId       Int
  supervisor         User     @relation("SupervisorPrivileges", fields: [supervisorId], references: [id], onDelete: Cascade)
  viewDashboard      Boolean  @default(true)
  addMembers         Boolean  @default(true)
  listenLiveCalls    Boolean  @default(true)
  viewReports        Boolean  @default(true)
  editTeamSettings   Boolean  @default(false)
  assignQueues       Boolean  @default(false)
  monitorPerformance Boolean  @default(true)
  manageAgentStatus  Boolean  @default(false)
  bargeCalls         Boolean  @default(false)
  whisperToAgents    Boolean  @default(false)
  canListenRecording Boolean  @default(false)
  canAddLead Boolean  @default(false)
  exportData         Boolean  @default(false)
  createdAt          DateTime @default(now())
  updatedAt          DateTime @updatedAt

  @@unique([teamId]) // Since one privilege per team
  @@index([teamId])
  @@index([supervisorId])
}

model Role {
  id          Int     @id @default(autoincrement())
  name        String  @unique
  description String?

  // Bound privileges (similar to AgentPrivilege, but separate)
  makeCall          Boolean @default(false)
  showCallDNC       Boolean @default(false)
  canCallDNC        Boolean @default(false)
  transcription     Boolean @default(false)
  transfer          Boolean @default(false)
  recording         Boolean @default(false)
  loginIp           Boolean @default(false)
  loginIpAddress    String? // optional IP if loginIp = true
  mms               Boolean @default(false)
  twoFA             Boolean @default(false)
  endCallPop        Boolean @default(false)
  emailPrivilege    Boolean @default(false)
  additionalEmail   String? // optional email if emailPrivilege = true
  targetEnable      Boolean @default(false)
  targetValue       String? // optional value if targetEnable = true
  // ─── New: Module-level Access Privileges ───
  viewDashboard     Boolean @default(false) // /dashboard
  viewTeamDashboard Boolean @default(false) // /team-dashboard
  viewCallLogs      Boolean @default(false) // /logs
  viewContacts      Boolean @default(false) // /contact
  viewMessages      Boolean @default(false) // /messages
  viewEmails        Boolean @default(false) // /emails
  viewLeads         Boolean @default(false) // leads section
  viewSales         Boolean @default(false) // /sale
  viewKpis          Boolean @default(false) // /kpis
  viewCompanyContacts          Boolean @default(false) // /companyContacts
  editProfile          Boolean @default(false) 
  changePassword          Boolean @default(false) 
  composeEmail          Boolean @default(false) 
  canMakeChatGroup          Boolean @default(false) 
  canDeleteContact          Boolean @default(false) 
  canEditContact          Boolean @default(false) 
  accessAutoDialAdmin          Boolean @default(false) 
  accessDisposition          Boolean @default(false) 
  teamMessages          Boolean @default(false) 
  teamCalls          Boolean @default(false) 

  // ─── Admin Module Privileges ───
  viewAdminDashboard Boolean  @default(false)
  manageAgents       Boolean  @default(false) // /admin/agents
  manageTeams        Boolean  @default(false) // /admin/teams
  manageNumbers      Boolean  @default(false) // /admin/numbers
  manageLeadsAdmin   Boolean  @default(false) // /admin/leads
  viewCallLogsAdmin  Boolean  @default(false) // /admin/call-logs
  viewReports        Boolean  @default(false) // /admin/reports
  manageRoles        Boolean  @default(false) // /admin/settings/roles
  manageBilling      Boolean  @default(false) // /admin/settings/billing
  liveCallsAccess    Boolean  @default(false) // /livecalls
  // ─── Script privileges ───
  manageScripts      Boolean  @default(false) // create / edit / delete / assign scripts
  viewScript         Boolean  @default(false) // see script button in call window
  smsTemplates       Boolean  @default(false) // create & use SMS templates in Messages
  createdAt          DateTime @default(now())
  updatedAt          DateTime @updatedAt

  users User[] // Users assigned this additional role
}

// ─── Call Script ──────────────────────────────────────────────────
model CallScript {
  id          Int      @id @default(autoincrement())
  title       String
  description String?
  content     String   @db.LongText  // Tiptap HTML
  isActive    Boolean  @default(true)
  createdById Int
  createdBy   User     @relation("ScriptCreator", fields: [createdById], references: [id])
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  assignments CallScriptAssignment[]
}

model CallScriptAssignment {
  id       Int        @id @default(autoincrement())
  scriptId Int
  userId   Int
  script   CallScript @relation(fields: [scriptId], references: [id], onDelete: Cascade)
  user     User       @relation("ScriptAssignee", fields: [userId], references: [id], onDelete: Cascade)
  assignedAt DateTime @default(now())

  @@unique([scriptId, userId])
}

model SmsTemplate {
  id        Int      @id @default(autoincrement())
  userId    Int
  user      User     @relation("UserSmsTemplates", fields: [userId], references: [id], onDelete: Cascade)
  title     String
  body      String   @db.LongText
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

model TeamMember {
  id Int @id @default(autoincrement())

  userId Int
  teamId Int

  addedById Int? // kisne add kiya
  addedAt   DateTime @default(now())

  roleInTeam String? // optional: agent | supervisor | viewer etc

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
  team Team @relation(fields: [teamId], references: [id], onDelete: Cascade)

  addedBy User? @relation("TeamMemberAddedBy", fields: [addedById], references: [id])

  @@unique([userId, teamId]) // same user ek team mein dobara na aaye
  @@index([userId])
  @@index([teamId])
}

model DNCNumber {
  id       Int      @id @default(autoincrement())
  number   String   @unique // E.164 format recommended: +12025550123
  reason   String? // optional: "customer request", "legal", "internal policy", etc.
  markedBy Int // who added it (admin / supervisor / agent)
  markedAt DateTime @default(now())

  // Relations
  markedByUser User @relation(fields: [markedBy], references: [id], onDelete: Cascade)

  @@index([number])
  @@index([markedBy])
  @@index([markedAt])
}
model AutoDialCampaign {
  id          Int      @id @default(autoincrement())
  name        String
  description String?

  createdById Int
  createdBy   User @relation("AutoDialCampaignCreator", fields: [createdById], references: [id])

  enabled Boolean @default(false)
  status  String  @default("draft")
  // draft | active | paused | completed | cancelled

  dialingMode String @default("progressive")
  // preview | progressive | power
  assignmentRule String @default("round_robin")   // ← ADD THIS LINE

  maxRetries        Int @default(2)
  retryDelayMinutes Int @default(30)

  callerId String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  queueItems AutoDialQueue[]
  campaignAgents AutoDialCampaignAgent[]  

  @@index([createdById])
  @@index([enabled])
  @@index([status])
}
model AutoDialCampaignAgent {
  id         Int              @id @default(autoincrement())
  campaignId Int
  campaign   AutoDialCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  agentId    Int
  agent      User             @relation("AutoDialCampaignAgents", fields: [agentId], references: [id], onDelete: Cascade)
  createdAt  DateTime         @default(now())

  @@unique([campaignId, agentId])
  @@index([campaignId])
  @@index([agentId])
}
model AutoDialQueue {
  id Int @id @default(autoincrement())

  campaignId Int
  campaign   AutoDialCampaign @relation(fields: [campaignId], references: [id], onDelete: Cascade)

  contactId Int?
  contact   Contact? @relation(fields: [contactId], references: [id], onDelete: SetNull)

  contactPhoneId Int?
  contactPhone   ContactPhone? @relation(fields: [contactPhoneId], references: [id], onDelete: SetNull)

  phoneNumber String

  assignedAgentId Int?
  assignedAgent   User? @relation("AutoDialAssignedAgent", fields: [assignedAgentId], references: [id], onDelete: SetNull)

  assignedById Int?
  assignedBy   User? @relation("AutoDialAssignedBy", fields: [assignedById], references: [id], onDelete: SetNull)

  status String @default("pending")
  // pending | reserved | dialing | connected | completed | no_answer | busy | failed | retry_scheduled | dnc | blocked | skipped | cancelled

  priority Int @default(0)

  attempts   Int @default(0)
  maxRetries Int @default(2)

  nextAttemptAt DateTime?

  lastCallSessionId Int?
  lastCallSession   CallSession? @relation(fields: [lastCallSessionId], references: [id], onDelete: SetNull)

  lastCallSid String?
  lastError   String?

  reservedAt DateTime?
  reservedBy String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  attemptsLog AutoDialAttempt[]

  @@index([campaignId])
  @@index([assignedAgentId])
  @@index([status])
  @@index([phoneNumber])
  @@index([nextAttemptAt])
}

model AutoDialAttempt {
  id Int @id @default(autoincrement())

  queueId Int
  queue   AutoDialQueue @relation(fields: [queueId], references: [id], onDelete: Cascade)

  agentId Int?
  agent   User? @relation("AutoDialAttemptAgent", fields: [agentId], references: [id], onDelete: SetNull)

  callSessionId Int?
  callSession   CallSession? @relation(fields: [callSessionId], references: [id], onDelete: SetNull)

  callSid String?

  status String
  // initiated | ringing | connected | completed | no_answer | busy | failed

  startedAt   DateTime @default(now())
  connectedAt DateTime?
  endedAt     DateTime?
  duration    Int?

  errorMessage String?

  createdAt DateTime @default(now())

  @@index([queueId])
  @@index([agentId])
  @@index([callSessionId])
  @@index([status])
}

model Company {
  id          Int      @id @default(autoincrement())
  name        String
  slug        String   @unique // e.g. "acme-corp" for subdomain or identifier
  email       String   @unique
  phone       String?
  address     String?
  isActive    Boolean  @default(true)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  wallet      CompanyWallet?
  transactions WalletTransaction[]
  payments     StripePayment[]

  @@index([slug])
  @@index([isActive])
}

// One wallet per company
model CompanyWallet {
  id          Int      @id @default(autoincrement())
  companyId   Int      @unique
  company     Company  @relation(fields: [companyId], references: [id], onDelete: Cascade)

  balance     Float    @default(0.0)   // Current balance (can go negative to NEGATIVE_BALANCE_LIMIT)
  freeBalance Float    @default(0.0)   // From FREE_BALANCE env — used first, then real balance
  currency    String   @default("USD")

  // Soft limits pulled from .env at company creation time
  // Stored here so per-company override is possible later
  negativeLimit Float  @default(-5.0)  // from NEGATIVE_BALANCE_LIMIT env
  
  // Usage limits (0 = unlimited, set from .env at creation)
  maxOutboundMinutes  Int?   // null = unlimited
  maxOutboundSms      Int?
  maxOutboundMms      Int?

  // Current period usage counters (reset monthly if needed)
  usedOutboundMinutes Float  @default(0)
  usedOutboundSms     Int    @default(0)
  usedOutboundMms     Int    @default(0)

  isFrozen    Boolean  @default(false)  // true when balance <= negativeLimit
  frozenAt    DateTime?
  lastTopupAt DateTime?

  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@index([companyId])
  @@index([isFrozen])
}

// Every debit/credit transaction
model WalletTransaction {
  id          Int      @id @default(autoincrement())
  companyId   Int
  company     Company  @relation(fields: [companyId], references: [id], onDelete: Cascade)

  type        String
  // debit_call | debit_recording | debit_transcription | debit_sms | debit_mms
  // debit_platform_fee (dialer margin/fee)
  // credit_topup | credit_free | credit_refund | credit_manual

  amount      Float    // always positive; direction determined by type prefix
  balanceBefore Float
  balanceAfter  Float

  // Reference to what caused this transaction
  callSessionId Int?
  callSid       String?
  messageId     String?  // for SMS/MMS

  // Twilio raw pricing details (stored for audit)
  twilioCallDuration  Float?   // in seconds
  twilioCallCost      Float?
  twilioRecordingCost Float?
  twilioTranscriptionCost Float?
  twilioSmsCost       Float?

  // ── Enterprise billing breakdown ──
  baseCost      Float?   // provider cost before platform fee
  twilioCost    Float?   // sum of all Twilio legs (agent + customer)
  commioCost    Float?   // Commio BYOC carrier cost (env-rated)
  marginPercent Float?   // DIALER_MARGIN_PERCENT at billing time
  marginAmount  Float?   // platform fee amount (our revenue)
  carrier       String?  // commio | twilio | mixed

  description String?
  meta        Json?    // any extra data

  createdAt   DateTime @default(now())

  @@index([companyId])
  @@index([type])
  @@index([createdAt])
  @@index([callSessionId])
}

// Stripe topup payments
model StripePayment {
  id                  Int      @id @default(autoincrement())
  companyId           Int
  company             Company  @relation(fields: [companyId], references: [id], onDelete: Cascade)

  stripePaymentIntentId String  @unique
  stripeChargeId        String?
  amount              Float    // Amount paid in USD
  currency            String   @default("USD")
  status              String   @default("pending")
  // pending | succeeded | failed | refunded

  walletCredited      Boolean  @default(false)  // has wallet been updated
  walletCreditedAt    DateTime?

  metadata            Json?
  createdAt           DateTime @default(now())
  updatedAt           DateTime @updatedAt

  @@index([companyId])
  @@index([status])
  @@index([stripePaymentIntentId])
}

// ═══════════════════════════════════════════════════════════
// V2 Call Flow Models & Enums
// ═══════════════════════════════════════════════════════════

enum RouteType {
  TWILIO_DIRECT
  BYOC_COMMIO
}

enum ProviderType {
  TWILIO
  COMMIO
}

enum AttemptStatus {
  QUEUED
  INITIATED
  RINGING
  ANSWERED
  COMPLETED
  FAILED
  NO_ANSWER
  BUSY
  CANCELED
}

model CallAttempt {
  id              Int           @id @default(autoincrement())
  callSessionId   Int
  callSession     CallSession   @relation(fields: [callSessionId], references: [id], onDelete: Cascade)

  attemptNo       Int
  routeType       RouteType
  providerType    ProviderType
  twilioCallSid   String?       @unique
  byocTrunkSid    String?
  carrierName     String?
  status          AttemptStatus @default(QUEUED)

  requestPayload  Json?
  responsePayload Json?
  errorCode       String?
  errorMessage    String?
  startedAt       DateTime      @default(now())
  connectedAt     DateTime?
  endedAt         DateTime?

  @@unique([callSessionId, attemptNo])
  @@index([callSessionId])
  @@index([status])
}

model RouteSelectionLog {
  id             Int         @id @default(autoincrement())
  callSessionId  Int
  callSession    CallSession @relation(fields: [callSessionId], references: [id], onDelete: Cascade)

  selectedRoute  RouteType
  primaryRoute   RouteType
  fallbackRoute  RouteType?
  configSource   String      @default("ENV")
  reason         String
  destination    String
  countryCode    String?
  allowedCountry Boolean
  createdAt      DateTime    @default(now())

  @@index([callSessionId])
}

model CallStatusLog {
  id              Int       @id @default(autoincrement())
  callSessionId   Int?
  callSession     CallSession? @relation(fields: [callSessionId], references: [id], onDelete: SetNull)

  callSid         String
  status          String    // initiated, ringing, answered, in-progress, completed, failed, declined, no-answer, busy
  timestamp       DateTime  @default(now())

  from            String?
  to              String?
  direction       String?   // inbound, outbound
  duration        Int?      // in seconds
  errorCode       String?
  errorMessage    String?

  // Twilio raw data
  callDuration    Int?
  accountSid      String?
  parentCallSid   String?   // For child legs (agent/customer in conference)

  metadata        Json?     // Any additional data

  createdAt       DateTime  @default(now())

  @@index([callSid])
  @@index([callSessionId])
  @@index([status])
  @@index([timestamp])
}

// ── Commio (thinQ) CDR local cache ──
// Live /outbound/cdrs API ko har billing request par hit karne ke bajaye
// ek baar fetch karke yahan store karte hain (scheduled sync + one-time
// backfill) — commio.service.ts ab yahan se query karta hai (instant,
// no external HTTP round-trip per request).
model CommioCdr {
  id            Int      @id @default(autoincrement())
  commioCallId  String   @unique // Commio CDR "callId" — SIP Call-ID, Twilio Insights sip_call_id se 1:1 match
  fromDid       String   // caller-ID digits (no +)
  toDid         String   // destination digits (no +)
  cdrTime       DateTime // Commio "time" field — jab call hui
  retail        Float?   // rate per minute
  billable      Int?     // billed seconds
  totalRetail   Float    // ✅ actual charged amount — yehi humari cost hai
  sipCode       String?
  sipReason     String?
  raw           Json     // poora original CDR row (audit / future fields ke liye)
  ingestedAt    DateTime @default(now())
  updatedAt     DateTime @updatedAt

  @@index([toDid, cdrTime])
  @@index([fromDid])
  @@index([cdrTime])
}

// ── Outbound call source-IP allow-list ──────────────────────────────────
// When this table has one or more ACTIVE rows, an outbound call can only
// be placed from a request whose IP falls within one of the active
// ranges — see middleware/callIpRestriction.ts. An empty (or all-inactive)
// table means the restriction is OFF: this keeps existing behavior
// unrestricted by default rather than needing a separate on/off toggle
// that an admin could forget to flip.
model CallIpAllowlist {
  id          Int      @id @default(autoincrement())
  label       String? // e.g. "Main Office", "Karachi Branch"
  ipStart     String  // dotted IPv4, e.g. "192.168.1.1" — single IP: ipStart == ipEnd
  ipEnd       String  // dotted IPv4, e.g. "192.168.1.50"
  isActive    Boolean  @default(true)
  createdById Int?
  createdBy   User?    @relation("CallIpAllowlistCreator", fields: [createdById], references: [id])
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@index([isActive])
}