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

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

enum OrgRole {
  ORG_ADMIN
  MEMBER
  VIEWER
}

enum JobStatus {
  OPEN
  IN_PROGRESS
  COMPLETED
  CANCELLED
}

enum JobSource {
  WEB
  DEVICE
}

enum UtilityType {
  ELECTRIC
  GAS
  WATER
  SEWER
  TELECOM
  CATV
  FIBER
  STEAM
  UNKNOWN
}

enum GpsFixType {
  NONE
  AUTONOMOUS
  DGPS
  FLOAT_RTK
  FIXED_RTK
}

model Organization {
  id          String          @id @default(cuid())
  name        String
  slug        String          @unique
  createdAt   DateTime        @default(now()) @db.Timestamptz(6)
  updatedAt   DateTime        @updatedAt @db.Timestamptz(6)
  memberships OrgMembership[]
  jobs        Job[]
  devices     Device[]
  apiKeys     ApiKey[]

  @@map("organizations")
}

model User {
  id           String          @id @default(cuid())
  email        String          @unique
  passwordHash String
  name         String
  createdAt    DateTime        @default(now()) @db.Timestamptz(6)
  updatedAt    DateTime        @updatedAt @db.Timestamptz(6)
  memberships  OrgMembership[]
  assignedJobs Job[]           @relation("JobAssignee")
  createdJobs  Job[]           @relation("JobCreator")
  apiKeys      ApiKey[]

  @@map("users")
}

model OrgMembership {
  id        String   @id @default(cuid())
  orgId     String
  userId    String
  role      OrgRole  @default(MEMBER)
  createdAt DateTime @default(now()) @db.Timestamptz(6)

  org  Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
  user User         @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([orgId, userId])
  @@map("org_memberships")
}

model Job {
  id                String    @id @default(cuid())
  orgId             String
  ticketNumber      String
  title             String
  description       String?
  address           String?
  status            JobStatus @default(OPEN)
  source            JobSource @default(WEB)
  assignedToId      String?
  createdById       String?
  createdByDeviceId String?
  dueAt             DateTime? @db.Timestamptz(6)
  startedAt         DateTime? @db.Timestamptz(6)
  completedAt       DateTime? @db.Timestamptz(6)
  createdAt         DateTime  @default(now()) @db.Timestamptz(6)
  updatedAt         DateTime  @updatedAt @db.Timestamptz(6)

  org        Organization  @relation(fields: [orgId], references: [id], onDelete: Cascade)
  assignedTo User?         @relation("JobAssignee", fields: [assignedToId], references: [id])
  createdBy  User?         @relation("JobCreator", fields: [createdById], references: [id])
  points     LocatePoint[]

  @@unique([orgId, ticketNumber])
  @@index([orgId, status])
  @@map("jobs")
}

model Device {
  id           String    @id @default(cuid())
  orgId        String
  name         String
  serialNumber String?
  mqttUsername String    @unique
  isActive     Boolean   @default(true)
  lastSeenAt   DateTime? @db.Timestamptz(6)
  createdAt    DateTime  @default(now()) @db.Timestamptz(6)
  updatedAt    DateTime  @updatedAt @db.Timestamptz(6)

  org    Organization  @relation(fields: [orgId], references: [id], onDelete: Cascade)
  points LocatePoint[]

  @@map("devices")
}

model LocatePoint {
  id          BigInt      @id @default(autoincrement())
  jobId       String
  deviceId    String?
  lat         Decimal     @db.Decimal(10, 8)
  lng         Decimal     @db.Decimal(11, 8)
  altitude    Decimal?    @db.Decimal(8, 3)
  fixType     GpsFixType  @default(NONE)
  hAccuracy   Decimal?    @db.Decimal(7, 3)
  depth       Decimal?    @db.Decimal(6, 3)
  utilityType UtilityType @default(UNKNOWN)
  sequence    Int?
  recordedAt  DateTime    @db.Timestamptz(6)
  receivedAt  DateTime    @default(now()) @db.Timestamptz(6)
  raw         Json?
  geom        Unsupported("geometry(Point, 4326)")?

  job    Job     @relation(fields: [jobId], references: [id], onDelete: Cascade)
  device Device? @relation(fields: [deviceId], references: [id], onDelete: SetNull)

  @@index([jobId, recordedAt])
  @@map("locate_points")
}

model ApiKey {
  id          String    @id @default(cuid())
  orgId       String
  name        String
  keyPrefix   String
  keyHash     String    @unique
  scopes      String[]
  createdById String?
  expiresAt   DateTime? @db.Timestamptz(6)
  lastUsedAt  DateTime? @db.Timestamptz(6)
  revokedAt   DateTime? @db.Timestamptz(6)
  createdAt   DateTime  @default(now()) @db.Timestamptz(6)

  org       Organization @relation(fields: [orgId], references: [id], onDelete: Cascade)
  createdBy User?        @relation(fields: [createdById], references: [id])

  @@index([keyPrefix])
  @@map("api_keys")
}

// Raw MQTT ingest log (every message on devices/#, matched or not)
model DeviceEvent {
  id         Int      @id @default(autoincrement())
  topic      String
  payload    String
  receivedAt DateTime @default(now()) @map("received_at") @db.Timestamptz(6)

  @@map("device_events")
}
