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

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

// ─── CCP Users (control panel operators) ───────────────────

enum CcpRole {
  SUPER_ADMIN
  OPERATOR
  VIEWER
}

model CcpUser {
  id            String   @id @default(uuid())
  email         String   @unique
  password      String
  name          String
  role          CcpRole  @default(OPERATOR)
  createdAt     DateTime @default(now()) @map("created_at")
  updatedAt     DateTime @updatedAt @map("updated_at")

  refreshTokens   CcpRefreshToken[]
  auditLogs       AuditLog[]
  triggeredUpgrades InstanceUpgrade[]
  acknowledgedEvents InstanceEvent[]
  agentInviteCodes AgentInviteCode[]

  @@map("ccp_users")
}

model CcpRefreshToken {
  id        String   @id @default(uuid())
  token     String   @unique
  userId    String   @map("user_id")
  expiresAt DateTime @map("expires_at")
  createdAt DateTime @default(now()) @map("created_at")

  user CcpUser @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
  @@index([expiresAt])
  @@map("ccp_refresh_tokens")
}

// ─── Managed Instances ─────────────────────────────────────

enum InstanceStatus {
  PROVISIONING
  RUNNING
  STOPPED
  ERROR
  DESTROYING
}

model Instance {
  id              String         @id @default(uuid())
  slug            String         @unique
  name            String
  domain          String         @unique
  status          InstanceStatus @default(PROVISIONING)
  statusMessage   String?        @map("status_message")

  basePath        String         @map("base_path")
  composeProject  String         @unique @map("compose_project")
  gitBranch       String         @default("v2") @map("git_branch")
  gitCommit       String?        @map("git_commit")

  // Allocated host ports (JSON: { api: 14001, admin: 13001, postgres: 15401, nginx: 10001 })
  portConfig      Json           @map("port_config")

  // AES-256-GCM encrypted JSON blob of all instance secrets (null for registered instances)
  encryptedSecrets String?       @map("encrypted_secrets")

  // True if this instance was registered externally (not provisioned by CCP)
  isRegistered    Boolean        @default(false) @map("is_registered")

  // Remote agent management
  isRemote         Boolean       @default(false) @map("is_remote")
  agentUrl         String?       @map("agent_url")
  agentFingerprint String?       @map("agent_fingerprint")
  agentVersion     String?       @map("agent_version")
  agentLastSeen    DateTime?     @map("agent_last_seen")

  // Feature flags
  enableMedia     Boolean        @default(false) @map("enable_media")
  enableChat      Boolean        @default(false) @map("enable_chat")
  enableGancio    Boolean        @default(false) @map("enable_gancio")
  enableListmonk  Boolean        @default(false) @map("enable_listmonk")
  enableMonitoring Boolean       @default(false) @map("enable_monitoring")
  enableDevTools  Boolean        @default(false) @map("enable_dev_tools")
  enablePayments  Boolean        @default(false) @map("enable_payments")
  enableMeet      Boolean        @default(false) @map("enable_meet")
  enableSms       Boolean        @default(false) @map("enable_sms")
  enableSocial    Boolean        @default(false) @map("enable_social")
  enablePeople    Boolean        @default(false) @map("enable_people")
  enableAnalytics Boolean        @default(false) @map("enable_analytics")
  jvbAdvertiseIp  String?        @map("jvb_advertise_ip")

  // Admin config
  adminEmail      String         @map("admin_email")

  // Pangolin tunnel
  pangolinEndpoint   String?     @map("pangolin_endpoint")
  pangolinSiteId     String?     @map("pangolin_site_id")
  pangolinNewtId     String?     @map("pangolin_newt_id")
  pangolinNewtSecret String?     @map("pangolin_newt_secret")

  // SMTP
  smtpHost        String?        @map("smtp_host")
  smtpPort        Int?           @map("smtp_port")
  smtpUser        String?        @map("smtp_user")
  smtpFrom        String?        @map("smtp_from")
  emailTestMode   Boolean        @default(true) @map("email_test_mode")

  notes           String?
  createdAt       DateTime       @default(now()) @map("created_at")
  updatedAt       DateTime       @updatedAt @map("updated_at")
  lastHealthCheck DateTime?      @map("last_health_check")

  portAllocations PortAllocation[]
  healthChecks    HealthCheck[]
  backups         Backup[]
  auditLogs       AuditLog[]
  upgrades        InstanceUpgrade[]
  events          InstanceEvent[]
  agentCert       IssuedAgentCert?

  @@map("instances")
}

// ─── Port Allocation ───────────────────────────────────────

model PortAllocation {
  id         String   @id @default(uuid())
  port       Int      @unique
  instanceId String   @map("instance_id")
  service    String
  notes      String?
  createdAt  DateTime @default(now()) @map("created_at")

  instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)

  @@index([instanceId])
  @@map("port_allocations")
}

// ─── Health Checks ─────────────────────────────────────────

enum HealthStatus {
  HEALTHY
  DEGRADED
  UNHEALTHY
  UNKNOWN
}

model HealthCheck {
  id              String       @id @default(uuid())
  instanceId      String       @map("instance_id")
  status          HealthStatus
  serviceStatus   Json         @map("service_status")
  totalServices   Int          @map("total_services")
  healthyServices Int          @map("healthy_services")
  responseTimeMs  Int?         @map("response_time_ms")
  checkedAt       DateTime     @default(now()) @map("checked_at")

  instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)

  @@index([instanceId, checkedAt])
  @@map("health_checks")
}

// ─── Backups ───────────────────────────────────────────────

enum BackupStatus {
  PENDING
  IN_PROGRESS
  COMPLETED
  FAILED
}

model Backup {
  id            String       @id @default(uuid())
  instanceId    String       @map("instance_id")
  status        BackupStatus @default(PENDING)
  archivePath   String?      @map("archive_path")
  sizeBytes     BigInt?      @map("size_bytes")
  manifest      Json?
  startedAt     DateTime     @default(now()) @map("started_at")
  completedAt   DateTime?    @map("completed_at")
  errorMessage  String?      @map("error_message")
  s3Uploaded    Boolean      @default(false) @map("s3_uploaded")
  s3Key         String?      @map("s3_key")

  instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade)

  @@index([instanceId, startedAt])
  @@map("backups")
}

// ─── Audit Log ─────────────────────────────────────────────

enum AuditAction {
  INSTANCE_CREATE
  INSTANCE_UPDATE
  INSTANCE_DELETE
  INSTANCE_START
  INSTANCE_STOP
  INSTANCE_RESTART
  INSTANCE_UPGRADE
  SECRETS_VIEWED
  BACKUP_CREATE
  BACKUP_DELETE
  PANGOLIN_SETUP
  PANGOLIN_SYNC
  AGENT_CONNECT
  AGENT_REGISTER
  AGENT_APPROVE
  AGENT_REJECT
  INVITE_CREATE
  INVITE_REVOKE
  CERT_ISSUE
  CERT_REVOKE
  USER_LOGIN
  USER_CREATE
  USER_UPDATE
  USER_DELETE
  SETTINGS_UPDATE
}

model AuditLog {
  id         String      @id @default(uuid())
  userId     String?     @map("user_id")
  instanceId String?     @map("instance_id")
  action     AuditAction
  details    Json?
  ipAddress  String?     @map("ip_address")
  createdAt  DateTime    @default(now()) @map("created_at")

  user     CcpUser?  @relation(fields: [userId], references: [id], onDelete: SetNull)
  instance Instance? @relation(fields: [instanceId], references: [id], onDelete: SetNull)

  @@index([instanceId, createdAt])
  @@index([userId, createdAt])
  @@index([action, createdAt])
  @@map("audit_logs")
}

// ─── Instance Upgrades ────────────────────────────────────

enum UpgradeStatus {
  PENDING
  IN_PROGRESS
  COMPLETED
  FAILED
  ROLLED_BACK
}

model InstanceUpgrade {
  id              String        @id @default(uuid())
  instanceId      String        @map("instance_id")
  status          UpgradeStatus @default(PENDING)
  previousCommit  String?       @map("previous_commit")
  newCommit       String?       @map("new_commit")
  commitCount     Int           @default(0) @map("commit_count")
  branch          String        @default("v2")

  // Progress tracking (updated from progress.json polling)
  currentPhase    Int           @default(0) @map("current_phase")
  phaseName       String?       @map("phase_name")
  percentage      Int           @default(0)
  progressMessage String?       @map("progress_message")

  // Result
  durationSeconds Int?          @map("duration_seconds")
  errorMessage    String?       @map("error_message")
  warnings        Json?
  log             String?

  triggeredById   String?       @map("triggered_by_id")
  startedAt       DateTime      @default(now()) @map("started_at")
  completedAt     DateTime?     @map("completed_at")

  instance    Instance  @relation(fields: [instanceId], references: [id], onDelete: Cascade)
  triggeredBy CcpUser?  @relation(fields: [triggeredById], references: [id], onDelete: SetNull)

  @@index([instanceId, startedAt])
  @@map("instance_upgrades")
}

// ─── Instance Events (Errors/Warnings) ───────────────────

enum EventSeverity {
  ERROR
  WARNING
  INFO
}

model InstanceEvent {
  id               String        @id @default(uuid())
  instanceId       String        @map("instance_id")
  severity         EventSeverity
  source           String        // 'health_check', 'upgrade', 'container', 'provisioning'
  title            String
  message          String
  metadata         Json?
  acknowledged     Boolean       @default(false)
  acknowledgedAt   DateTime?     @map("acknowledged_at")
  acknowledgedById String?       @map("acknowledged_by_id")
  createdAt        DateTime      @default(now()) @map("created_at")

  instance       Instance  @relation(fields: [instanceId], references: [id], onDelete: Cascade)
  acknowledgedBy CcpUser?  @relation(fields: [acknowledgedById], references: [id], onDelete: SetNull)

  @@index([instanceId, createdAt])
  @@index([severity, acknowledged])
  @@map("instance_events")
}

// ─── CCP Settings ──────────────────────────────────────────

model CcpSetting {
  key       String   @id
  value     Json
  updatedAt DateTime @updatedAt @map("updated_at")

  @@map("ccp_settings")
}

// ─── Remote Agent Management ──────────────────────────────

model CcpCertificateAuthority {
  id            String   @id @default(uuid())
  commonName    String   @map("common_name")
  encryptedKey  String   @map("encrypted_key")
  certPem       String   @map("cert_pem")
  fingerprint   String
  createdAt     DateTime @default(now()) @map("created_at")
  expiresAt     DateTime @map("expires_at")

  issuedCerts IssuedAgentCert[]

  @@map("ccp_certificate_authority")
}

model IssuedAgentCert {
  id           String    @id @default(uuid())
  caId         String    @map("ca_id")
  instanceId   String    @unique @map("instance_id")
  commonName   String    @map("common_name")
  encryptedKey String    @map("encrypted_key")
  certPem      String    @map("cert_pem")
  fingerprint  String
  issuedAt     DateTime  @default(now()) @map("issued_at")
  expiresAt    DateTime  @map("expires_at")
  revokedAt    DateTime? @map("revoked_at")

  ca       CcpCertificateAuthority @relation(fields: [caId], references: [id])
  instance Instance                @relation(fields: [instanceId], references: [id], onDelete: Cascade)

  @@index([instanceId])
  @@map("issued_agent_certs")
}

model AgentInviteCode {
  id          String    @id @default(uuid())
  code        String    @unique
  createdById String    @map("created_by_id")
  usedById    String?   @map("used_by_id")
  expiresAt   DateTime  @map("expires_at")
  usedAt      DateTime? @map("used_at")
  createdAt   DateTime  @default(now()) @map("created_at")

  createdBy CcpUser @relation(fields: [createdById], references: [id])

  @@map("agent_invite_codes")
}

enum AgentRegistrationStatus {
  PENDING
  APPROVED
  REJECTED
  EXPIRED
}

model AgentRegistration {
  id             String                  @id @default(uuid())
  inviteCodeId   String                  @map("invite_code_id")
  slug           String
  name           String
  domain         String
  agentUrl       String                  @map("agent_url")
  basePath       String                  @map("base_path")
  composeProject String                  @map("compose_project")
  metadata       Json?
  status         AgentRegistrationStatus @default(PENDING)
  instanceId     String?                 @map("instance_id")
  approvedById   String?                 @map("approved_by_id")
  approvedAt     DateTime?               @map("approved_at")
  rejectedAt     DateTime?               @map("rejected_at")
  certBundle     Json?                   @map("cert_bundle")
  createdAt      DateTime                @default(now()) @map("created_at")

  @@index([status])
  @@map("agent_registrations")
}
