generator client {
  provider = "prisma-client-js"
  output   = "../src/generated/prisma"
}

datasource db {
  provider = "postgresql"
}

enum LocalModelRole {
  ROUTER
  LOCAL_FALLBACK_CHAT
  LOCAL_REASONING
  LOCAL_CODING
  LOCAL_FILE_GENERATION
  LOCAL_THINKING
  LOCAL_IMAGE_GENERATION
}

enum RuntimeType {
  OLLAMA
  VLLM
  LLAMA_CPP
  LOCAL_AI
  COMFYUI
}

enum ModelCategory {
  CODING
  FILE_GENERATION
  IMAGE_GENERATION
  ROUTING
  REASONING
  THINKING
  GENERAL
  MEDICAL
  MARKETING
  BUSINESS
  LITERATURE
  AGENT
  SUMMARIZATION
  JUDGE
  TRANSLATION
  VISION
  EMBEDDING
}

enum PullJobStatus {
  PENDING
  IN_PROGRESS
  INSTALLING
  COMPLETED
  FAILED
}

enum PullJobPhase {
  QUEUED
  DOWNLOADING
  INSTALLING
  FINALIZING
  DONE
}

enum DiscoverySourceType {
  OLLAMA_REGISTRY
  OLLAMA_LIBRARY
  MANUAL
}

enum DiscoveryRunStatus {
  PENDING
  RUNNING
  COMPLETED
  FAILED
  CANCELLED
}

enum CandidateStatus {
  PENDING
  APPROVED
  REJECTED
  IMPORTED
  DUPLICATE
  STALE
}

enum DownloadStatus {
  AVAILABLE
  UNAVAILABLE
  CLOUD_ONLY
  UNKNOWN
}

model LocalModel {
  id          String          @id @default(cuid())
  name        String
  tag         String
  runtime     RuntimeType
  sizeBytes   BigInt?         @map("size_bytes")
  family      String?
  parameters  String?
  quantization String?
  category    ModelCategory?
  isInstalled Boolean         @default(true) @map("is_installed")
  createdAt   DateTime        @default(now()) @map("created_at")
  updatedAt   DateTime        @updatedAt @map("updated_at")

  roles LocalModelRoleAssignment[]

  @@unique([name, tag, runtime])
  @@index([runtime])
  @@index([isInstalled])
  @@index([category])
  @@map("local_models")
}

model LocalModelRoleAssignment {
  id        String         @id @default(cuid())
  modelId   String         @map("model_id")
  role      LocalModelRole
  isActive  Boolean        @default(true) @map("is_active")
  createdAt DateTime       @default(now()) @map("created_at")

  model LocalModel @relation(fields: [modelId], references: [id], onDelete: Cascade)

  @@unique([role, isActive])
  @@index([modelId])
  @@index([role])
  @@map("local_model_roles")
}

model PullJob {
  id              String        @id @default(cuid())
  modelName       String        @map("model_name")
  runtime         RuntimeType
  status          PullJobStatus @default(PENDING)
  phase           PullJobPhase  @default(QUEUED)
  progress        Int?
  totalBytes      BigInt?       @map("total_bytes")
  downloadedBytes BigInt?       @map("downloaded_bytes")
  installStep     String?       @map("install_step")
  installAttempts Int           @default(0) @map("install_attempts")
  retryAttempts   Int           @default(0) @map("retry_attempts")
  resumedAt       DateTime?     @map("resumed_at")
  lastProgressAt  DateTime?     @map("last_progress_at")
  errorMessage    String?       @map("error_message")
  startedAt       DateTime      @default(now()) @map("started_at")
  completedAt     DateTime?     @map("completed_at")

  @@index([status])
  @@index([runtime])
  @@map("pull_jobs")
}

model RuntimeConfig {
  id        String      @id @default(cuid())
  runtime   RuntimeType @unique
  baseUrl   String      @map("base_url")
  isEnabled Boolean     @default(true) @map("is_enabled")
  createdAt DateTime    @default(now()) @map("created_at")
  updatedAt DateTime    @updatedAt @map("updated_at")

  @@map("runtime_configs")
}

model ModelCatalogEntry {
  id                 String              @id @default(cuid())
  name               String
  tag                String
  displayName        String              @map("display_name")
  category           ModelCategory
  description        String?
  sizeBytes          BigInt?             @map("size_bytes")
  parameterCount     String?             @map("parameter_count")
  quantization       String?
  runtime            RuntimeType
  ollamaName         String?             @map("ollama_name")
  sourceUrl          String?             @map("source_url")
  isRecommended      Boolean             @default(false) @map("is_recommended")
  capabilities       String[]
  businessCategories String[]            @default([]) @map("business_categories")
  hardwareProfiles   String[]            @default([]) @map("hardware_profiles")
  isDiscovered       Boolean             @default(false) @map("is_discovered")
  discoverySourceId  String?             @map("discovery_source_id")
  downloadStatus     DownloadStatus      @default(UNKNOWN) @map("download_status")
  lastVerifiedAt     DateTime?           @map("last_verified_at")
  searchBrowserScore    Float?           @map("search_browser_score")
  searchBrowserScoreAt  DateTime?        @map("search_browser_score_at")
  searchBrowserReasons  String[]         @default([]) @map("search_browser_reasons")
  createdAt          DateTime            @default(now()) @map("created_at")
  updatedAt          DateTime            @updatedAt @map("updated_at")

  discoverySource DiscoverySource? @relation(fields: [discoverySourceId], references: [id], onDelete: SetNull)

  @@unique([name, tag, runtime])
  @@index([category])
  @@index([runtime])
  @@index([isDiscovered])
  @@index([downloadStatus])
  @@index([searchBrowserScore])
  @@map("model_catalog_entries")
}

model DiscoverySource {
  id          String              @id @default(cuid())
  name        String
  type        DiscoverySourceType
  baseUrl     String              @map("base_url")
  isEnabled   Boolean             @default(true) @map("is_enabled")
  categories  String[]            @default([])
  maxResults  Int                 @default(50) @map("max_results")
  minQuality  Int                 @default(0) @map("min_quality")
  lastRunAt   DateTime?           @map("last_run_at")
  createdAt   DateTime            @default(now()) @map("created_at")
  updatedAt   DateTime            @updatedAt @map("updated_at")

  runs            ModelDiscoveryRun[]
  catalogEntries  ModelCatalogEntry[]

  @@unique([name])
  @@index([isEnabled])
  @@index([type])
  @@map("discovery_sources")
}

model ModelDiscoveryRun {
  id              String             @id @default(cuid())
  sourceId        String             @map("source_id")
  status          DiscoveryRunStatus @default(PENDING)
  isDryRun        Boolean            @default(false) @map("is_dry_run")
  discoveredCount Int                @default(0) @map("discovered_count")
  importedCount   Int                @default(0) @map("imported_count")
  skippedCount    Int                @default(0) @map("skipped_count")
  failedCount     Int                @default(0) @map("failed_count")
  errorMessage    String?            @map("error_message")
  triggeredBy     String?            @map("triggered_by")
  startedAt       DateTime           @default(now()) @map("started_at")
  completedAt     DateTime?          @map("completed_at")

  source     DiscoverySource           @relation(fields: [sourceId], references: [id], onDelete: Cascade)
  candidates ModelDiscoveryCandidate[]

  @@index([sourceId])
  @@index([status])
  @@index([startedAt])
  @@map("discovery_runs")
}

model ModelDiscoveryCandidate {
  id                 String          @id @default(cuid())
  runId              String          @map("run_id")
  name               String
  tag                String
  displayName        String          @map("display_name")
  ollamaName         String          @map("ollama_name")
  description        String?
  sizeBytes          BigInt?         @map("size_bytes")
  parameterCount     String?         @map("parameter_count")
  familyName         String?         @map("family_name")
  runtime            RuntimeType     @default(OLLAMA)
  capabilities       String[]        @default([])
  suggestedCategory  ModelCategory   @map("suggested_category")
  businessCategories String[]        @default([]) @map("business_categories")
  hardwareProfiles   String[]        @default([]) @map("hardware_profiles")
  downloadStatus     DownloadStatus  @default(UNKNOWN) @map("download_status")
  confidence         Float           @default(0)
  sourceUrl          String?         @map("source_url")
  status             CandidateStatus @default(PENDING)
  duplicateOfId      String?         @map("duplicate_of_id")
  importedEntryId    String?         @map("imported_entry_id")
  rejectionReason    String?         @map("rejection_reason")
  createdAt          DateTime        @default(now()) @map("created_at")
  updatedAt          DateTime        @updatedAt @map("updated_at")

  run ModelDiscoveryRun @relation(fields: [runId], references: [id], onDelete: Cascade)

  @@unique([runId, name, tag])
  @@index([status])
  @@index([suggestedCategory])
  @@index([downloadStatus])
  @@index([ollamaName])
  @@map("discovery_candidates")
}
