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

datasource db {
  provider = "postgresql"
}

model cf_api_keys {
  id              Int            @id @default(autoincrement())
  key             String         @unique @db.VarChar(255)
  suffix          String         @db.VarChar(4)
  name            String         @db.VarChar(255)
  created_at      DateTime       @default(now()) @db.Timestamptz(6)
  last_used       DateTime?      @db.Timestamptz(6)
  user_id         String?
  organization_id String?
  tier            String?
  user            users?         @relation(fields: [user_id], references: [user_id])
  organization    organizations? @relation(fields: [organization_id], references: [id], onDelete: SetNull)

  optimization_events optimization_events[]

  @@index([user_id])
  @@index([organization_id])
}

model users {
  user_id              String   @id
  github_username      String   @unique
  email                String?
  name                 String?
  onboarding_completed Boolean               @default(false)
  privacy_mode         Boolean               @default(false)
  created_at           DateTime              @default(now()) @db.Timestamptz(6)
  referral_source      String?               @db.VarChar(255)
  additional_comments  String?               @db.Text

  api_keys             cf_api_keys[]
  subscriptions        subscriptions?
  optimization_events  optimization_events[]
  comments             comments[]
  repository_members   repository_members[]
  organization_members organization_members[]
}

model optimization_features {
  trace_id                        String    @id @db.Char(36)
  original_code                   String?
  optimizations_raw               Json?
  optimizations_post              Json?
  explanations_post               Json?
  speedup_ratio                   Json?
  optimized_line_profiler_results Json?     @default("{}")
  original_runtime                Float?
  optimized_runtime               Json?
  is_correct                      Json?
  generated_test                  String[]
  test_framework                  String?
  created_at                      DateTime? @default(now()) @db.Timestamptz(6)
  aiservice_commit_id             String?
  metadata                        Json?
  explanations_raw                Json?
  user_id                         String?
  experiment_metadata             Json?
  instrumented_generated_test     String[]
  instrumented_perf_test          String[]
  pull_request                    Json?
  dependency_code                 String?
  line_profiler_results           String?
  final_explanation               String?
  approval_required               Boolean   @default(false)
  approval_status                 String? // "pending", "approved", "rejected"
  approval_user                   String?
  approval_timestamp              DateTime?
  slack_message_ts                String?
  organization                    String?
  repository                      String?
  ranking                         Json?
  optimizations_origin            Json?
  review_quality                  String? // Hight, Med, low
  review_explanation              String?
  calling_fn_details              String?

  @@index([approval_status])
  @@index([approval_required])
  @@index([organization])
}

model subscriptions {
  id                           String    @id @default(uuid())
  user_id                      String    @unique
  stripe_customer_id           String?   @unique
  stripe_subscription_id       String?
  plan_type                    String
  optimizations_used           Int       @default(0)
  optimizations_limit          Int
  total_lifetime_optimizations Int       @default(0)
  subscription_status          String
  current_period_start         DateTime?
  current_period_end           DateTime?
  created_at                   DateTime  @default(now())
  updated_at                   DateTime  @updatedAt
  cancel_at_period_end         Boolean   @default(false)
  cancellation_request_date    DateTime?
  user                         users     @relation(fields: [user_id], references: [user_id])
}

model django_content_type {
  id        Int    @id @default(autoincrement())
  app_label String @db.VarChar(100)
  model     String @db.VarChar(100)

  @@unique([app_label, model], map: "django_content_type_app_label_model_76bd3d3b_uniq")
}

model django_migrations {
  id      BigInt   @id @default(autoincrement())
  app     String   @db.VarChar(255)
  name    String   @db.VarChar(255)
  applied DateTime @db.Timestamptz(6)
}

model pr_code_context_hash_cache {
  id           Int      @id @default(autoincrement())
  repo         String
  owner        String
  pr_number    Int
  context_hash String
  created_at   DateTime @default(now())
  updated_at   DateTime @updatedAt

  @@unique([repo, owner, pr_number, context_hash], name: "repo_owner_pr_number_context_hash")
}

model cf_app_installations {
  id              String   @id @default(uuid())
  installation_id Int      @unique
  account_id      Int
  account_login   String
  account_type    String // 'User' or 'Organization'
  created_at      DateTime @default(now()) @db.Timestamptz(6)
  updated_at      DateTime @updatedAt
  is_active       Boolean  @default(true)

  repositories repositories[]
}

model repositories {
  id                  String    @id @default(uuid())
  github_repo_id      String    @unique
  installation_id     Int
  name                String
  full_name           String    @unique
  is_private          Boolean
  is_active           Boolean   @default(true)
  has_github_action   Boolean   @default(false)
  created_at          DateTime  @default(now()) @db.Timestamptz(6)
  last_optimized      DateTime?
  organization_id     String?
  optimizations_limit Int? // Custom limit for this repository
  optimizations_used  Int       @default(0) // Track usage
  added_by            String?

  installation_info   cf_app_installations  @relation(fields: [installation_id], references: [installation_id], onDelete: Cascade)
  organization        organizations?        @relation(fields: [organization_id], references: [id], onDelete: SetNull)
  optimization_events optimization_events[]
  repository_members  repository_members[]

  @@index([installation_id])
  @@index([organization_id])
}

model repository_members {
  id            String   @id @default(uuid())
  repository_id String
  user_id       String
  role          String // 'owner', 'admin', 'member'
  added_at      DateTime @default(now()) @db.Timestamptz(6)
  added_by      String?

  repository repositories @relation(fields: [repository_id], references: [id], onDelete: Cascade)
  user       users        @relation(fields: [user_id], references: [user_id], onDelete: Cascade)

  @@unique([repository_id, user_id])
  @@index([user_id])
  @@index([repository_id])
}

model optimization_events {
  id                    String   @id @default(uuid())
  event_type            String // 'pr_created', 'pr_merged', 'pr_closed', 'no-pr'
  user_id               String? // Can be null for system events
  repository_id         String?
  baseBranch            String?
  trace_id              String   @unique
  pr_id                 String?  @unique
  pr_url                String?
  api_key_id            Int?
  metadata              Json?
  is_optimization_found Boolean?
  is_staging            Boolean? @default(false)
  staging_storage_type  String? // "plain_text", "git_branch"
  status                String? // "approved", "rejected"
  current_username      String? // for the current user who did this event, we create this to know the active user in github action
  function_name         String?
  file_path             String?
  speedup_x             Float?
  speedup_pct           Float?
  llm_cost              Float?
  feedback              Json?
  created_at            DateTime @default(now()) @db.Timestamptz(6)

  // Relations
  user       users?        @relation(fields: [user_id], references: [user_id], onDelete: SetNull)
  repository repositories? @relation(fields: [repository_id], references: [id], onDelete: SetNull)
  api_key    cf_api_keys?  @relation(fields: [api_key_id], references: [id], onDelete: SetNull)
  comments   comments[]

  @@index([event_type, created_at])
  @@index([repository_id, event_type])
  @@index([user_id, event_type])
  @@index([trace_id])
  @@index([current_username])
  @@index([current_username, event_type])
  @@index([repository_id, user_id])
  @@index([api_key_id])
  @@index([is_staging])
  // Covers the optimization-prs query: filter by repo + optimization found + event type, sort by created_at DESC
  @@index([repository_id, is_optimization_found, event_type, created_at(sort: Desc)])
  @@index([is_optimization_found, event_type, created_at(sort: Desc)])
  // Covers the "active repos in last 30 days" groupBy: seek by repo, range-scan on date
  @@index([repository_id, created_at(sort: Desc)])
  // Covers PR query UNION branches for personal accounts (user_id / current_username path)
  @@index([user_id, is_optimization_found, event_type, created_at(sort: Desc)])
  @@index([current_username, is_optimization_found, event_type, created_at(sort: Desc)])
}

model comments {
  id                    String   @id @default(uuid())
  optimization_event_id String
  content               String   @db.Text
  created_at            DateTime @default(now()) @db.Timestamptz(6)
  author_user_id        String?

  optimization_event optimization_events @relation(fields: [optimization_event_id], references: [id], onDelete: Cascade)
  author             users?              @relation(fields: [author_user_id], references: [user_id], onDelete: SetNull)

  @@index([optimization_event_id])
  @@index([author_user_id])
}

model organizations {
  id                      String   @id @default(uuid())
  name                    String   @unique
  github_org_id           String?  @unique
  description             String?
  website                 String?
  created_at              DateTime  @default(now()) @db.Timestamptz(6)
  updated_at              DateTime  @updatedAt
  auto_add_github_members Boolean   @default(true)
  added_by                String?
  privacy_mode            Boolean   @default(false)
  subscription            Boolean   @default(false)

  repositories         repositories[]
  organization_members organization_members[]
  api_keys             cf_api_keys[]

  @@index([github_org_id])
  @@index([name])
}

model organization_members {
  id              String   @id @default(uuid())
  organization_id String
  user_id         String
  role            String
  added_at        DateTime @default(now()) @db.Timestamptz(6)
  added_by        String?

  organization organizations @relation(fields: [organization_id], references: [id], onDelete: Cascade)
  user         users         @relation(fields: [user_id], references: [user_id], onDelete: Cascade)

  @@unique([organization_id, user_id])
  @@index([user_id])
  @@index([organization_id])
}

// ==================== Observability Tables ====================

model llm_calls {
  id         String @id @default(uuid()) @db.Uuid
  trace_id   String @db.Char(36)
  call_type  String @db.VarChar(50) // 'optimization', 'test_generation', 'ranking', 'refinement'
  model_name String @db.VarChar(100) // 'gpt-4', 'gpt-3.5-turbo', etc.

  // Prompt capture (full content for prompt engineering analysis)
  system_prompt String @db.Text
  user_prompt   String @db.Text
  messages      Json // Full messages array sent to LLM

  // Request configuration
  temperature  Float?
  n_candidates Int? // Number of completions requested
  max_tokens   Int?

  // Response capture
  raw_response    String? @db.Text // Full LLM response before parsing
  parsed_response Json? // Structured parsed response

  // Token usage and cost
  prompt_tokens     Int?
  completion_tokens Int?
  total_tokens      Int?
  llm_cost          Float?

  // Timing
  latency_ms Int?

  // Status and errors
  status        String  @db.VarChar(20) // 'success', 'failed', 'partial_success'
  retry_count   Int     @default(0)
  error_type    String? @db.VarChar(50)
  error_message String? @db.Text

  // Parsing results
  parsing_status       String? @db.VarChar(20) // 'success', 'failed', 'partial'
  candidates_generated Int?    @default(0)
  candidates_valid     Int?    @default(0) // Passed syntax validation
  parsing_errors       Json? // Details of parsing failures

  // Context
  user_id        String?
  python_version String? @db.VarChar(20)
  is_async       Boolean @default(false)
  context        Json? // Additional metadata (optimization_id, speedup, etc.)

  created_at DateTime @default(now()) @db.Timestamptz(6)

  // Relations
  optimization_errors optimization_errors[]

  @@index([trace_id])
  @@index([trace_id, created_at(sort: Desc)])
  @@index([call_type])
  @@index([model_name])
  @@index([status])
  @@index([created_at(sort: Desc)])
  @@index([call_type, status])
  @@index([parsing_status])
}

model optimization_errors {
  id          String  @id @default(uuid())
  trace_id    String  @db.Char(36)
  llm_call_id String? @db.Uuid

  // Error classification
  error_type     String @db.VarChar(50) // 'validation', 'llm_api', 'llm_parsing', 'test_failure', 'compilation'
  error_category String @db.VarChar(50) // 'user_error', 'system_error', 'llm_error', 'infrastructure'
  severity       String @db.VarChar(20) // 'critical', 'error', 'warning', 'info'

  // Error details
  error_message String  @db.Text
  error_code    String? @db.VarChar(50)
  stack_trace   String? @db.Text
  context       Json? // Additional error context including test failure details

  created_at DateTime @default(now()) @db.Timestamptz(6)

  // Relations
  llm_call llm_calls? @relation(fields: [llm_call_id], references: [id], onDelete: SetNull)

  @@index([trace_id])
  @@index([error_type])
  @@index([error_category])
  @@index([severity])
  @@index([llm_call_id])
}
