// =============================================================================
// MOPC Platform - Prisma Schema
// =============================================================================
// This schema defines the database structure for the Monaco Ocean Protection
// Challenge jury voting platform.

generator client {
  provider      = "prisma-client-js"
  binaryTargets = ["native", "windows", "linux-musl-openssl-3.0.x"]
}

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

// =============================================================================
// ENUMS
// =============================================================================

enum UserRole {
  SUPER_ADMIN
  PROGRAM_ADMIN
  JURY_MEMBER
  MENTOR
  OBSERVER
  APPLICANT
  AWARD_MASTER
  AUDIENCE
}

enum UserStatus {
  NONE
  INVITED
  ACTIVE
  SUSPENDED
}

enum ProgramStatus {
  DRAFT
  ACTIVE
  ARCHIVED
}

enum ProjectStatus {
  SUBMITTED
  ELIGIBLE
  ASSIGNED
  SEMIFINALIST
  FINALIST
  REJECTED
}

enum EvaluationStatus {
  NOT_STARTED
  DRAFT
  SUBMITTED
  LOCKED
}

enum AssignmentMethod {
  MANUAL
  BULK
  AI_SUGGESTED
  AI_AUTO
  ALGORITHM
}

enum FileType {
  EXEC_SUMMARY
  PRESENTATION
  VIDEO
  OTHER
  BUSINESS_PLAN
  VIDEO_PITCH
  SUPPORTING_DOC
}

enum SubmissionSource {
  MANUAL
  CSV
  NOTION
  TYPEFORM
  PUBLIC_FORM
}

enum SettingType {
  STRING
  NUMBER
  BOOLEAN
  JSON
  SECRET
}

enum SettingCategory {
  AI
  BRANDING
  EMAIL
  STORAGE
  SECURITY
  DEFAULTS
  WHATSAPP
  AUDIT_CONFIG
  LOCALIZATION
  DIGEST
  ANALYTICS
  INTEGRATIONS
  COMMUNICATION
  FEATURE_FLAGS
}

enum NotificationChannel {
  EMAIL
  WHATSAPP
  BOTH
  NONE
}

enum ResourceType {
  PDF
  VIDEO
  DOCUMENT
  LINK
  OTHER
}

enum CohortLevel {
  ALL
  SEMIFINALIST
  FINALIST
}

enum PartnerVisibility {
  ADMIN_ONLY
  JURY_VISIBLE
  PUBLIC
}

enum PartnerType {
  SPONSOR
  PARTNER
  SUPPORTER
  MEDIA
  OTHER
}

enum OverrideReasonCode {
  DATA_CORRECTION
  POLICY_EXCEPTION
  JURY_CONFLICT
  SPONSOR_DECISION
  ADMIN_DISCRETION
}

// =============================================================================
// COMPETITION / ROUND ENGINE ENUMS
// =============================================================================

enum CompetitionStatus {
  DRAFT
  ACTIVE
  CLOSED
  ARCHIVED
}

enum RoundType {
  INTAKE
  FILTERING
  EVALUATION
  SUBMISSION
  MENTORING
  LIVE_FINAL
  DELIBERATION
}

enum RoundStatus {
  ROUND_DRAFT
  ROUND_ACTIVE
  ROUND_CLOSED
  ROUND_ARCHIVED
}

enum ProjectRoundStateValue {
  PENDING
  IN_PROGRESS
  PASSED
  REJECTED
  COMPLETED
  WITHDRAWN
}

enum AdvancementRuleType {
  AUTO_ADVANCE
  SCORE_THRESHOLD
  TOP_N
  ADMIN_SELECTION
  AI_RECOMMENDED
}

enum CapMode {
  HARD
  SOFT
  NONE
}

enum DeadlinePolicy {
  HARD_DEADLINE
  FLAG
  GRACE
}

enum JuryGroupMemberRole {
  CHAIR
  MEMBER
  OBSERVER
}

enum AssignmentIntentSource {
  INVITE
  ADMIN
  SYSTEM
}

enum AssignmentIntentStatus {
  INTENT_PENDING
  HONORED
  OVERRIDDEN
  EXPIRED
  CANCELLED
}

enum MentorMessageRole {
  MENTOR_ROLE
  APPLICANT_ROLE
  ADMIN_ROLE
}

enum SubmissionPromotionSource {
  MENTOR_FILE
  ADMIN_REPLACEMENT
}

enum DeliberationMode {
  SINGLE_WINNER_VOTE
  FULL_RANKING
}

enum DeliberationStatus {
  DELIB_OPEN
  VOTING
  TALLYING
  RUNOFF
  DELIB_LOCKED
}

enum TieBreakMethod {
  TIE_RUNOFF
  TIE_ADMIN_DECIDES
  SCORE_FALLBACK
}

enum DeliberationParticipantStatus {
  REQUIRED
  ABSENT_EXCUSED
  REPLACED
  REPLACEMENT_ACTIVE
}

enum AwardEligibilityMode {
  SEPARATE_POOL
  STAY_IN_MAIN
}

// =============================================================================
// APPLICANT SYSTEM ENUMS
// =============================================================================

enum CompetitionCategory {
  STARTUP // Existing companies
  BUSINESS_CONCEPT // Students/graduates
}

enum OceanIssue {
  POLLUTION_REDUCTION
  CLIMATE_MITIGATION
  TECHNOLOGY_INNOVATION
  SUSTAINABLE_SHIPPING
  BLUE_CARBON
  HABITAT_RESTORATION
  COMMUNITY_CAPACITY
  SUSTAINABLE_FISHING
  CONSUMER_AWARENESS
  OCEAN_ACIDIFICATION
  OTHER
}

enum TeamMemberRole {
  LEAD // Primary contact / team lead
  MEMBER // Regular team member
  ADVISOR // Advisor/mentor from team side
}

enum MentorAssignmentMethod {
  MANUAL
  AI_SUGGESTED
  AI_AUTO
  ALGORITHM
}

// =============================================================================
// USERS & AUTHENTICATION
// =============================================================================

model User {
  id             String     @id @default(cuid())
  email          String     @unique
  name           String?
  emailVerified  DateTime? // Required by NextAuth Prisma adapter
  role           UserRole   @default(JURY_MEMBER)
  status         UserStatus @default(INVITED)
  expertiseTags  String[]   @default([])
  maxAssignments Int? // Per-round limit
  country        String? // User's home country (for mentor matching)
  metadataJson   Json?      @db.JsonB

  // Profile
  bio                  String? // User bio for matching with project descriptions
  profileImageKey      String? // Storage key (e.g., "avatars/user123/1234567890.jpg")
  profileImageProvider String? // Storage provider used: 's3' or 'local'

  // Phone and notification preferences (Phase 2)
  phoneNumber            String?
  phoneNumberVerified    Boolean             @default(false)
  notificationPreference NotificationChannel @default(EMAIL)
  whatsappOptIn          Boolean             @default(false)

  // Onboarding (Phase 2B)
  onboardingCompletedAt DateTime?

  // Password authentication (hybrid auth)
  passwordHash    String? // bcrypt hashed password
  passwordSetAt   DateTime? // When password was set
  mustSetPassword Boolean   @default(true) // Force setup on first login

  // Invitation token for one-click invite acceptance
  inviteToken          String?   @unique
  inviteTokenExpiresAt DateTime?

  // Digest & availability preferences
  digestFrequency   String @default("none") // 'none' | 'daily' | 'weekly'
  preferredWorkload Int?
  availabilityJson  Json?  @db.JsonB // { startDate?: string, endDate?: string }

  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
  lastLoginAt DateTime?

  // Relations
  assignments         Assignment[]
  auditLogs           AuditLog[]
  gracePeriods        GracePeriod[]
  grantedGracePeriods GracePeriod[]      @relation("GrantedBy")
  notificationLogs    NotificationLog[]
  createdResources    LearningResource[] @relation("ResourceCreatedBy")
  resourceAccess      ResourceAccess[]
  submittedProjects   Project[]          @relation("ProjectSubmittedBy")
  liveVotes           LiveVote[]

  // Team membership & mentorship
  teamMemberships   TeamMember[]
  mentorAssignments MentorAssignment[] @relation("MentorAssignments")

  // Awards
  awardJurorships AwardJuror[]
  awardVotes      AwardVote[]

  // Filtering overrides
  filteringOverrides FilteringResult[] @relation("FilteringOverriddenBy")

  // Award overrides
  awardEligibilityOverrides AwardEligibility[] @relation("AwardEligibilityOverriddenBy")
  awardWinnerOverrides      SpecialAward[]     @relation("AwardOverriddenBy")

  // In-app notifications
  notifications               InAppNotification[]        @relation("UserNotifications")
  notificationSettingsUpdated NotificationEmailSetting[] @relation("NotificationSettingUpdater")

  // Reminder logs
  reminderLogs ReminderLog[]

  // Conflict of interest
  conflictsOfInterest ConflictOfInterest[]
  coiReviews          ConflictOfInterest[] @relation("COIReviewedBy")

  // Evaluation summaries
  generatedSummaries EvaluationSummary[] @relation("EvaluationSummaryGeneratedBy")

  // Mentor messages
  mentorMessages MentorMessage[] @relation("MentorMessageSender")

  // Wizard templates
  wizardTemplates WizardTemplate[] @relation("WizardTemplateCreatedBy")

  // Mentor notes
  mentorNotes MentorNote[] @relation("MentorNoteAuthor")

  // Milestone completions
  milestoneCompletions MentorMilestoneCompletion[] @relation("MilestoneCompletedBy")

  // Evaluation discussions
  closedDiscussions  EvaluationDiscussion[] @relation("DiscussionClosedBy")
  discussionComments DiscussionComment[]    @relation("DiscussionCommentAuthor")

  // Messaging
  sentMessages     Message[]          @relation("MessageSender")
  receivedMessages MessageRecipient[] @relation("MessageRecipient")
  messageTemplates MessageTemplate[]  @relation("MessageTemplateCreator")

  // Webhooks
  webhooks Webhook[] @relation("WebhookCreator")

  // Digest logs
  digestLogs DigestLog[] @relation("DigestLog")

  // NextAuth relations
  accounts Account[]
  sessions Session[]

  // ── Competition/Round architecture relations ──
  juryGroupMemberships              JuryGroupMember[]
  mentorFilesUploaded               MentorFile[]               @relation("MentorFileUploader")
  mentorFilesPromoted               MentorFile[]               @relation("MentorFilePromoter")
  mentorFileComments                MentorFileComment[]        @relation("MentorFileCommentAuthor")
  resultLocksCreated                ResultLock[]               @relation("ResultLockCreator")
  resultUnlockEvents                ResultUnlockEvent[]        @relation("ResultUnlocker")
  assignmentExceptionsApproved      AssignmentException[]      @relation("AssignmentExceptionApprover")
  submissionPromotions              SubmissionPromotionEvent[] @relation("SubmissionPromoter")
  deliberationReplacements          DeliberationParticipant[]  @relation("DeliberationReplacement")

  @@index([role])
  @@index([status])
}

// NextAuth.js required models
model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String? @db.Text
  access_token      String? @db.Text
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String? @db.Text
  session_state     String?

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

  @@unique([provider, providerAccountId])
  @@index([userId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime

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

  @@index([userId])
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}

// =============================================================================
// PROGRAMS & ROUNDS
// =============================================================================

model Program {
  id           String        @id @default(cuid())
  name         String // e.g., "Monaco Ocean Protection Challenge"
  slug         String?       @unique // URL-friendly identifier for edition-wide applications
  year         Int // e.g., 2026
  status       ProgramStatus @default(DRAFT)
  description  String?
  settingsJson Json?         @db.JsonB

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  projects          Project[]
  learningResources LearningResource[]
  partners          Partner[]
  specialAwards     SpecialAward[]
  taggingJobs       TaggingJob[]
  wizardTemplates   WizardTemplate[]
  mentorMilestones  MentorMilestone[]
  competitions      Competition[]

  @@unique([name, year])
  @@index([status])
}

model WizardTemplate {
  id          String   @id @default(cuid())
  name        String
  description String?
  config      Json     @db.JsonB
  isGlobal    Boolean  @default(false)
  programId   String?
  program     Program? @relation(fields: [programId], references: [id], onDelete: Cascade)
  createdBy   String
  creator     User     @relation("WizardTemplateCreatedBy", fields: [createdBy], references: [id])
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  @@index([programId])
  @@index([isGlobal])
}

// =============================================================================
// EVALUATION FORMS
// =============================================================================

model EvaluationForm {
  id      String @id @default(cuid())
  roundId String
  version Int    @default(1)

  // Form configuration
  // criteriaJson: Array of { id, label, description, scale, weight, required }
  criteriaJson Json    @db.JsonB
  // scalesJson: { "1-5": { min, max, labels }, "1-10": { min, max, labels } }
  scalesJson   Json?   @db.JsonB
  isActive     Boolean @default(false)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round       Round        @relation(fields: [roundId], references: [id], onDelete: Cascade)
  evaluations Evaluation[]

  @@unique([roundId, version])
  @@index([roundId, isActive])
}

// =============================================================================
// PROJECTS
// =============================================================================

model Project {
  id        String        @id @default(cuid())
  programId String
  roundId   String?
  status    ProjectStatus @default(SUBMITTED)

  // Core fields
  title       String
  teamName    String?
  description String? @db.Text

  // Competition category
  competitionCategory CompetitionCategory?
  oceanIssue          OceanIssue?

  // Location
  country        String?
  geographicZone String? // "Europe, France"

  // Institution (for students/Business Concepts)
  institution String?

  // Mentorship
  wantsMentorship Boolean @default(false)

  // Founding date
  foundedAt DateTime? // When the project/company was founded

  // Submission links (external, from CSV)
  phase1SubmissionUrl String?
  phase2SubmissionUrl String?

  // Referral tracking
  referralSource String?

  // Internal admin fields
  internalComments  String? @db.Text
  applicationStatus String? // "Received", etc.

  // Submission tracking
  submissionSource  SubmissionSource @default(MANUAL)
  submittedByEmail  String?
  submittedAt       DateTime?
  submittedByUserId String?

  // Project branding
  logoKey      String? // Storage key (e.g., "logos/project456/1234567890.png")
  logoProvider String? // Storage provider used: 's3' or 'local'

  // Draft support
  isDraft        Boolean   @default(false)
  draftDataJson  Json?     @db.JsonB // Form data for drafts
  draftExpiresAt DateTime?

  // Flexible fields
  tags            String[] @default([]) // "Ocean Conservation", "Tech", etc.
  metadataJson    Json?    @db.JsonB // Custom fields from Typeform, etc.
  externalIdsJson Json?    @db.JsonB // Typeform ID, Notion ID, etc.

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  program               Program                @relation(fields: [programId], references: [id], onDelete: Cascade)
  files                 ProjectFile[]
  assignments           Assignment[]
  submittedBy           User?                  @relation("ProjectSubmittedBy", fields: [submittedByUserId], references: [id], onDelete: SetNull)
  teamMembers           TeamMember[]
  mentorAssignment      MentorAssignment?
  filteringResults      FilteringResult[]
  awardEligibilities    AwardEligibility[]
  awardVotes            AwardVote[]
  wonAwards             SpecialAward[]         @relation("AwardWinner")
  projectTags           ProjectTag[]
  statusHistory         ProjectStatusHistory[]
  mentorMessages        MentorMessage[]
  evaluationSummaries   EvaluationSummary[]
  evaluationDiscussions EvaluationDiscussion[]
  cohortProjects        CohortProject[]

  // ── Competition/Round architecture relations ──
  projectRoundStates    ProjectRoundState[]
  assignmentIntents     AssignmentIntent[]
  deliberationVotes     DeliberationVote[]
  deliberationResults   DeliberationResult[]
  submissionPromotions  SubmissionPromotionEvent[]

  @@index([programId])
  @@index([status])
  @@index([tags])
  @@index([submissionSource])
  @@index([submittedByUserId])
  @@index([competitionCategory])
  @@index([oceanIssue])
  @@index([country])
}

model FileRequirement {
  id                String   @id @default(cuid())
  roundId           String
  name              String
  description       String?
  acceptedMimeTypes String[] // e.g. ["application/pdf", "video/*"]
  maxSizeMB         Int? // Max file size in MB
  isRequired        Boolean  @default(true)
  sortOrder         Int      @default(0)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round Round         @relation(fields: [roundId], references: [id], onDelete: Cascade)
  files ProjectFile[]

  @@index([roundId])
}

model ProjectFile {
  id            String  @id @default(cuid())
  projectId     String
  roundId       String? // Which round this file was submitted for
  requirementId String? // FK to FileRequirement (if uploaded against a requirement)

  // File info
  fileType  FileType
  fileName  String
  mimeType  String
  size      Int // bytes
  pageCount Int? // Number of pages (PDFs, presentations, etc.)

  // MinIO location
  bucket    String
  objectKey String

  isLate Boolean @default(false) // Uploaded after round deadline

  // Versioning
  version      Int     @default(1)
  replacedById String? // FK to the newer file that replaced this one

  // ── Competition/Round architecture fields ──
  submissionWindowId         String? // FK to SubmissionWindow
  submissionFileRequirementId String? // FK to SubmissionFileRequirement

  createdAt DateTime @default(now())

  // Relations
  project                   Project                    @relation(fields: [projectId], references: [id], onDelete: Cascade)
  requirement               FileRequirement?           @relation(fields: [requirementId], references: [id], onDelete: SetNull)
  replacedBy                ProjectFile?               @relation("FileVersions", fields: [replacedById], references: [id], onDelete: SetNull)
  replacements              ProjectFile[]              @relation("FileVersions")
  submissionWindow          SubmissionWindow?          @relation(fields: [submissionWindowId], references: [id], onDelete: SetNull)
  submissionFileRequirement SubmissionFileRequirement? @relation(fields: [submissionFileRequirementId], references: [id], onDelete: SetNull)
  promotedFrom              MentorFile?                @relation("PromotedFromMentorFile")

  @@unique([bucket, objectKey])
  @@index([projectId])
  @@index([fileType])
  @@index([requirementId])
  @@index([submissionWindowId])
  @@index([submissionFileRequirementId])
}

// =============================================================================
// ASSIGNMENTS & EVALUATIONS
// =============================================================================

model Assignment {
  id        String @id @default(cuid())
  userId    String
  projectId String
  roundId   String

  // Assignment info
  method      AssignmentMethod @default(MANUAL)
  isRequired  Boolean          @default(true)
  isCompleted Boolean          @default(false)

  // AI assignment metadata
  aiConfidenceScore   Float? // 0-1 confidence from AI
  expertiseMatchScore Float? // 0-1 match score
  aiReasoning         String? @db.Text

  createdAt DateTime @default(now())
  createdBy String? // Admin who created the assignment

  // Competition/Round architecture — jury group link
  juryGroupId String?

  // Relations
  user               User                  @relation(fields: [userId], references: [id], onDelete: Cascade)
  project            Project               @relation(fields: [projectId], references: [id], onDelete: Cascade)
  round              Round                 @relation(fields: [roundId], references: [id], onDelete: Cascade)
  juryGroup          JuryGroup?            @relation(fields: [juryGroupId], references: [id], onDelete: SetNull)
  evaluation         Evaluation?
  conflictOfInterest ConflictOfInterest?
  exceptions         AssignmentException[]

  @@unique([userId, projectId, roundId])
  @@index([roundId])
  @@index([userId])
  @@index([projectId])
  @@index([isCompleted])
  @@index([projectId, userId])
  @@index([juryGroupId])
}

model Evaluation {
  id           String @id @default(cuid())
  assignmentId String @unique
  formId       String

  // Status
  status EvaluationStatus @default(NOT_STARTED)

  // Scores
  // criterionScoresJson: { "criterion_id": score, ... }
  criterionScoresJson Json?    @db.JsonB
  globalScore         Int? // 1-10
  binaryDecision      Boolean? // Yes/No for semi-finalist
  feedbackText        String?  @db.Text

  // Versioning (currently unused - evaluations are updated in-place.
  // TODO: Implement proper versioning by creating new rows on re-submission
  // if version history is needed for audit purposes)
  version Int @default(1)

  // Timestamps
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
  submittedAt DateTime?

  // Relations
  assignment Assignment     @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
  form       EvaluationForm @relation(fields: [formId], references: [id], onDelete: Cascade)

  @@index([status])
  @@index([submittedAt])
  @@index([formId])
  @@index([status, formId])
}

// =============================================================================
// GRACE PERIODS
// =============================================================================

model GracePeriod {
  id        String  @id @default(cuid())
  roundId   String
  userId    String
  projectId String? // Optional: specific project or all projects in round

  extendedUntil DateTime
  reason        String?  @db.Text
  grantedById   String

  createdAt DateTime @default(now())

  // Relations
  round     Round @relation(fields: [roundId], references: [id], onDelete: Cascade)
  user      User  @relation(fields: [userId], references: [id], onDelete: Cascade)
  grantedBy User  @relation("GrantedBy", fields: [grantedById], references: [id])

  @@index([roundId])
  @@index([userId])
  @@index([extendedUntil])
  @@index([grantedById])
  @@index([projectId])
  @@index([roundId, userId, extendedUntil])
}

// =============================================================================
// SYSTEM SETTINGS
// =============================================================================

model SystemSettings {
  id       String          @id @default(cuid())
  key      String          @unique
  value    String          @db.Text
  type     SettingType     @default(STRING)
  category SettingCategory

  description String?
  isSecret    Boolean @default(false) // If true, value is encrypted

  updatedAt DateTime @updatedAt
  updatedBy String?

  @@index([category])
}

// =============================================================================
// AUDIT LOGGING
// =============================================================================

model AuditLog {
  id     String  @id @default(cuid())
  userId String?

  // Event info
  action     String // "CREATE", "UPDATE", "DELETE", "LOGIN", "EXPORT", etc.
  entityType String // "Round", "Project", "Evaluation", etc.
  entityId   String?

  // Details
  detailsJson      Json? @db.JsonB // Before/after values, additional context
  previousDataJson Json? @db.JsonB // Previous state for tracking changes

  // Request info
  ipAddress String?
  userAgent String?
  sessionId String?

  timestamp DateTime @default(now())

  // Relations
  user User? @relation(fields: [userId], references: [id], onDelete: SetNull)

  @@index([userId])
  @@index([action])
  @@index([entityType, entityId])
  @@index([timestamp])
  @@index([entityType, entityId, timestamp])
  @@index([sessionId])
}

// =============================================================================
// AI USAGE TRACKING
// =============================================================================

model AIUsageLog {
  id        String   @id @default(cuid())
  createdAt DateTime @default(now())

  // Who/what triggered it
  userId     String?
  action     String // ASSIGNMENT, FILTERING, AWARD_ELIGIBILITY, MENTOR_MATCHING
  entityType String? // Round, Project, Award
  entityId   String?

  // What was used
  model            String // gpt-4o, gpt-4o-mini, o1, etc.
  promptTokens     Int
  completionTokens Int
  totalTokens      Int

  // Cost tracking
  estimatedCostUsd Decimal? @db.Decimal(10, 6)

  // Request context
  batchSize      Int?
  itemsProcessed Int?

  // Status
  status       String // SUCCESS, PARTIAL, ERROR
  errorMessage String?

  // Detailed data (optional)
  detailsJson Json? @db.JsonB

  @@index([userId])
  @@index([action])
  @@index([createdAt])
  @@index([model])
}

// =============================================================================
// NOTIFICATION LOG (Phase 2)
// =============================================================================

model NotificationLog {
  id         String              @id @default(cuid())
  userId     String
  channel    NotificationChannel
  provider   String? // META, TWILIO, SMTP
  type       String // MAGIC_LINK, REMINDER, ANNOUNCEMENT, JURY_INVITATION
  status     String // PENDING, SENT, DELIVERED, FAILED
  externalId String? // Message ID from provider
  errorMsg   String?             @db.Text

  createdAt DateTime @default(now())

  // Relations
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
  @@index([status])
  @@index([createdAt])
}

// =============================================================================
// IN-APP NOTIFICATIONS
// =============================================================================

model InAppNotification {
  id        String  @id @default(cuid())
  userId    String
  type      String // FILTERING_COMPLETE, NEW_APPLICATION, ASSIGNED_TO_PROJECT, etc.
  priority  String  @default("normal") // low, normal, high, urgent
  icon      String? // lucide icon name
  title     String
  message   String  @db.Text
  linkUrl   String? // Where to navigate when clicked
  linkLabel String? // CTA text
  metadata  Json?   @db.JsonB // Extra context (projectId, roundId, etc.)
  groupKey  String? // For batching similar notifications

  isRead    Boolean   @default(false)
  readAt    DateTime?
  expiresAt DateTime? // Auto-dismiss after date

  createdAt DateTime @default(now())

  // Relations
  user User @relation("UserNotifications", fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId, isRead])
  @@index([userId, createdAt])
  @@index([groupKey])
}

model NotificationEmailSetting {
  id               String  @id @default(cuid())
  notificationType String  @unique // e.g., "ADVANCED_TO_ROUND", "ASSIGNED_TO_PROJECT"
  category         String // "team", "jury", "mentor", "admin"
  label            String // Human-readable label for admin UI
  description      String? // Help text
  sendEmail        Boolean @default(true)
  emailSubject     String? // Custom subject template (optional)
  emailTemplate    String? @db.Text // Custom body template (optional)

  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  updatedById String?
  updatedBy   User?    @relation("NotificationSettingUpdater", fields: [updatedById], references: [id])

  @@index([category])
}

// =============================================================================
// LEARNING HUB (Phase 2)
// =============================================================================

model LearningResource {
  id           String       @id @default(cuid())
  programId    String? // null = global resource
  title        String
  description  String?      @db.Text
  contentJson  Json?        @db.JsonB // BlockNote document structure
  resourceType ResourceType
  cohortLevel  CohortLevel  @default(ALL)

  // File storage (for uploaded resources)
  fileName  String?
  mimeType  String?
  size      Int?
  bucket    String?
  objectKey String?

  // External link
  externalUrl String?

  sortOrder   Int     @default(0)
  isPublished Boolean @default(false)

  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
  createdById String

  // Relations
  program    Program?         @relation(fields: [programId], references: [id], onDelete: SetNull)
  createdBy  User             @relation("ResourceCreatedBy", fields: [createdById], references: [id])
  accessLogs ResourceAccess[]

  @@index([programId])
  @@index([cohortLevel])
  @@index([isPublished])
  @@index([sortOrder])
}

model ResourceAccess {
  id         String   @id @default(cuid())
  resourceId String
  userId     String
  accessedAt DateTime @default(now())
  ipAddress  String?

  // Relations
  resource LearningResource @relation(fields: [resourceId], references: [id], onDelete: Cascade)
  user     User             @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@index([resourceId])
  @@index([userId])
  @@index([accessedAt])
}

// =============================================================================
// PARTNER MANAGEMENT (Phase 2)
// =============================================================================

model Partner {
  id          String            @id @default(cuid())
  programId   String? // null = global partner
  name        String
  description String?           @db.Text
  website     String?
  partnerType PartnerType       @default(PARTNER)
  visibility  PartnerVisibility @default(ADMIN_ONLY)

  // Logo file
  logoFileName  String?
  logoBucket    String?
  logoObjectKey String?

  sortOrder Int     @default(0)
  isActive  Boolean @default(true)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  program Program? @relation(fields: [programId], references: [id], onDelete: SetNull)

  @@index([programId])
  @@index([partnerType])
  @@index([visibility])
  @@index([isActive])
  @@index([sortOrder])
}

// =============================================================================
// EXPERTISE TAGS (Phase 2B)
// =============================================================================

model ExpertiseTag {
  id          String  @id @default(cuid())
  name        String  @unique
  description String?
  category    String? // "Marine Science", "Technology", "Policy"
  color       String? // Hex for badge
  isActive    Boolean @default(true)
  sortOrder   Int     @default(0)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  projectTags ProjectTag[]

  @@index([category])
  @@index([isActive])
  @@index([sortOrder])
}

// Project-Tag relationship for AI tagging
model ProjectTag {
  id         String   @id @default(cuid())
  projectId  String
  tagId      String
  confidence Float    @default(1.0) // AI confidence score 0-1
  source     String   @default("AI") // "AI" or "MANUAL"
  createdAt  DateTime @default(now())

  // Relations
  project Project      @relation(fields: [projectId], references: [id], onDelete: Cascade)
  tag     ExpertiseTag @relation(fields: [tagId], references: [id], onDelete: Cascade)

  @@unique([projectId, tagId])
  @@index([projectId])
  @@index([tagId])
}

// =============================================================================
// LIVE VOTING (Phase 2B)
// =============================================================================

model LiveVotingSession {
  id                  String    @id @default(cuid())
  roundId             String?   @unique
  status              String    @default("NOT_STARTED") // NOT_STARTED, IN_PROGRESS, PAUSED, COMPLETED
  currentProjectIndex Int       @default(0)
  currentProjectId    String?
  votingStartedAt     DateTime?
  votingEndsAt        DateTime?
  projectOrderJson    Json?     @db.JsonB // Array of project IDs in presentation order

  // Criteria-based voting
  votingMode   String @default("simple") // "simple" (1-10) | "criteria" (per-criterion scores)
  criteriaJson Json?  @db.JsonB // Array of { id, label, description, scale, weight }

  // Audience & presentation settings
  allowAudienceVotes       Boolean @default(false)
  audienceVoteWeight       Float   @default(0) // 0.0 to 1.0
  tieBreakerMethod         String  @default("admin_decides") // 'admin_decides' | 'highest_individual' | 'revote'
  presentationSettingsJson Json?   @db.JsonB

  // Audience voting configuration
  audienceVotingMode     String  @default("disabled") // "disabled" | "per_project" | "per_category" | "favorites"
  audienceMaxFavorites   Int     @default(3) // For "favorites" mode
  audienceRequireId      Boolean @default(false) // Require email/phone for audience
  audienceVotingDuration Int? // Minutes (null = same as jury)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round          Round?          @relation(fields: [roundId], references: [id], onDelete: Cascade)
  votes          LiveVote[]
  audienceVoters AudienceVoter[]

  @@index([status])
}

model LiveVote {
  id             String   @id @default(cuid())
  sessionId      String
  projectId      String
  userId         String? // Nullable for audience voters without accounts
  score          Int // 1-10 (or weighted score for criteria mode)
  isAudienceVote Boolean  @default(false)
  votedAt        DateTime @default(now())

  // Criteria scores (used when votingMode="criteria")
  criterionScoresJson Json? @db.JsonB // { [criterionId]: score } - null for simple mode

  // Audience voter link
  audienceVoterId String?

  // Relations
  session       LiveVotingSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
  user          User?             @relation(fields: [userId], references: [id], onDelete: Cascade)
  audienceVoter AudienceVoter?    @relation(fields: [audienceVoterId], references: [id], onDelete: Cascade)

  @@unique([sessionId, projectId, userId])
  @@unique([sessionId, projectId, audienceVoterId])
  @@index([sessionId])
  @@index([projectId])
  @@index([userId])
  @@index([audienceVoterId])
}

model AudienceVoter {
  id             String   @id @default(cuid())
  sessionId      String
  token          String   @unique // Unique voting token (UUID)
  identifier     String? // Optional: email, phone, or name
  identifierType String? // "email" | "phone" | "name" | "anonymous"
  ipAddress      String?
  userAgent      String?
  createdAt      DateTime @default(now())

  // Relations
  session LiveVotingSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
  votes   LiveVote[]

  @@index([sessionId])
  @@index([token])
}

// =============================================================================
// TEAM MEMBERSHIP
// =============================================================================

model TeamMember {
  id        String         @id @default(cuid())
  projectId String
  userId    String
  role      TeamMemberRole @default(MEMBER)
  title     String? // "CEO", "CTO", etc.
  joinedAt  DateTime       @default(now())

  // Relations
  project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
  user    User    @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([projectId, userId])
  @@index([projectId])
  @@index([userId])
  @@index([role])
}

// =============================================================================
// MENTOR ASSIGNMENT
// =============================================================================

model MentorAssignment {
  id        String @id @default(cuid())
  projectId String @unique // One mentor per project
  mentorId  String // User with MENTOR role or expertise

  // Assignment tracking
  method     MentorAssignmentMethod @default(MANUAL)
  assignedAt DateTime               @default(now())
  assignedBy String? // Admin who assigned

  // AI assignment metadata
  aiConfidenceScore   Float?
  expertiseMatchScore Float?
  aiReasoning         String? @db.Text

  // Tracking
  completionStatus String    @default("in_progress") // 'in_progress' | 'completed' | 'paused'
  lastViewedAt     DateTime?

  // ── Competition/Round architecture — workspace activation ──
  workspaceEnabled Boolean   @default(false)
  workspaceOpenAt  DateTime?
  workspaceCloseAt DateTime?

  // Relations
  project              Project                     @relation(fields: [projectId], references: [id], onDelete: Cascade)
  mentor               User                        @relation("MentorAssignments", fields: [mentorId], references: [id])
  notes                MentorNote[]
  milestoneCompletions MentorMilestoneCompletion[]
  messages             MentorMessage[]
  files                MentorFile[]

  @@index([mentorId])
  @@index([method])
}

// =============================================================================
// FILTERING ROUND SYSTEM
// =============================================================================

enum FilteringOutcome {
  PASSED
  FILTERED_OUT
  FLAGGED
}

enum FilteringRuleType {
  FIELD_BASED
  DOCUMENT_CHECK
  AI_SCREENING
}

model FilteringRule {
  id         String            @id @default(cuid())
  roundId    String
  name       String
  ruleType   FilteringRuleType
  configJson Json              @db.JsonB // Conditions, logic, action per rule type
  priority   Int               @default(0)
  isActive   Boolean           @default(true)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)

  @@index([roundId])
  @@index([priority])
}

model FilteringResult {
  id              String           @id @default(cuid())
  roundId         String
  projectId       String
  outcome         FilteringOutcome
  ruleResultsJson Json?            @db.JsonB // Per-rule results
  aiScreeningJson Json?            @db.JsonB // AI screening details

  // Admin override
  overriddenBy   String?
  overriddenAt   DateTime?
  overrideReason String?           @db.Text
  finalOutcome   FilteringOutcome?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round            Round   @relation(fields: [roundId], references: [id], onDelete: Cascade)
  project          Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
  overriddenByUser User?   @relation("FilteringOverriddenBy", fields: [overriddenBy], references: [id], onDelete: SetNull)

  @@unique([roundId, projectId])
  @@index([roundId])
  @@index([projectId])
  @@index([outcome])
}

// Tracks progress of long-running filtering jobs
model FilteringJob {
  id             String             @id @default(cuid())
  roundId        String
  status         FilteringJobStatus @default(PENDING)
  totalProjects  Int                @default(0)
  totalBatches   Int                @default(0)
  currentBatch   Int                @default(0)
  processedCount Int                @default(0)
  passedCount    Int                @default(0)
  filteredCount  Int                @default(0)
  flaggedCount   Int                @default(0)
  errorMessage   String?            @db.Text
  startedAt      DateTime?
  completedAt    DateTime?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)

  @@index([roundId])
  @@index([status])
}

enum FilteringJobStatus {
  PENDING
  RUNNING
  COMPLETED
  FAILED
}

// Tracks progress of long-running AI assignment jobs
model AssignmentJob {
  id               String              @id @default(cuid())
  roundId          String
  status           AssignmentJobStatus @default(PENDING)
  totalProjects    Int                 @default(0)
  totalBatches     Int                 @default(0)
  currentBatch     Int                 @default(0)
  processedCount   Int                 @default(0)
  suggestionsCount Int                 @default(0)
  suggestionsJson  Json?               @db.JsonB // Stores the AI-generated suggestions
  errorMessage     String?             @db.Text
  startedAt        DateTime?
  completedAt      DateTime?
  fallbackUsed     Boolean             @default(false)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)

  @@index([roundId])
  @@index([status])
}

enum AssignmentJobStatus {
  PENDING
  RUNNING
  COMPLETED
  FAILED
}

// Tracks progress of long-running AI tagging jobs
model TaggingJob {
  id             String           @id @default(cuid())
  programId      String? // If tagging entire program
  roundId        String? // If tagging single round
  status         TaggingJobStatus @default(PENDING)
  totalProjects  Int              @default(0)
  processedCount Int              @default(0)
  taggedCount    Int              @default(0)
  skippedCount   Int              @default(0)
  failedCount    Int              @default(0)
  errorMessage   String?          @db.Text
  errorsJson     Json?            @db.JsonB // Array of error messages
  startedAt      DateTime?
  completedAt    DateTime?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations (optional - can tag by program)
  program Program? @relation(fields: [programId], references: [id], onDelete: Cascade)

  @@index([programId])
  @@index([status])
}

enum TaggingJobStatus {
  PENDING
  RUNNING
  COMPLETED
  FAILED
}

// =============================================================================
// SPECIAL AWARDS SYSTEM
// =============================================================================

enum AwardScoringMode {
  PICK_WINNER
  RANKED
  SCORED
}

enum AwardStatus {
  DRAFT
  NOMINATIONS_OPEN
  VOTING_OPEN
  CLOSED
  ARCHIVED
}

enum EligibilityMethod {
  AUTO
  MANUAL
}

model SpecialAward {
  id          String      @id @default(cuid())
  programId   String
  name        String
  description String?     @db.Text
  status      AwardStatus @default(DRAFT)

  // Criteria
  criteriaText     String? @db.Text // Plain-language criteria for AI
  autoTagRulesJson Json?   @db.JsonB // Deterministic eligibility rules
  useAiEligibility Boolean @default(true) // Whether AI evaluates eligibility

  // Scoring
  scoringMode    AwardScoringMode @default(PICK_WINNER)
  maxRankedPicks Int? // For RANKED mode

  // Voting window
  votingStartAt DateTime?
  votingEndAt   DateTime?

  // Evaluation form (for SCORED mode)
  evaluationFormId String?

  // Winner
  winnerProjectId    String?
  winnerOverridden   Boolean @default(false)
  winnerOverriddenBy String? // FK to User who overrode the winner

  sortOrder Int @default(0)

  // ── Competition/Round architecture fields ──
  competitionId     String?
  evaluationRoundId String?
  juryGroupId       String?
  eligibilityMode   AwardEligibilityMode @default(STAY_IN_MAIN)
  decisionMode      String?              // "JURY_VOTE" | "AWARD_MASTER_DECISION" | "ADMIN_DECISION"

  // Eligibility job tracking
  eligibilityJobStatus  String? // PENDING, PROCESSING, COMPLETED, FAILED
  eligibilityJobTotal   Int? // total projects to process
  eligibilityJobDone    Int? // completed so far
  eligibilityJobError   String?   @db.Text // error message if failed
  eligibilityJobStarted DateTime? // when job started

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  program          Program            @relation(fields: [programId], references: [id], onDelete: Cascade)
  winnerProject    Project?           @relation("AwardWinner", fields: [winnerProjectId], references: [id], onDelete: SetNull)
  overriddenByUser User?              @relation("AwardOverriddenBy", fields: [winnerOverriddenBy], references: [id], onDelete: SetNull)
  eligibilities    AwardEligibility[]
  jurors           AwardJuror[]
  votes            AwardVote[]

  // Competition/Round architecture relations
  competition      Competition?       @relation(fields: [competitionId], references: [id], onDelete: SetNull)
  evaluationRound  Round?             @relation(fields: [evaluationRoundId], references: [id], onDelete: SetNull)
  awardJuryGroup   JuryGroup?         @relation(fields: [juryGroupId], references: [id], onDelete: SetNull)

  @@index([programId])
  @@index([status])
  @@index([sortOrder])
  @@index([competitionId])
  @@index([evaluationRoundId])
}

model AwardEligibility {
  id              String            @id @default(cuid())
  awardId         String
  projectId       String
  method          EligibilityMethod @default(AUTO)
  eligible        Boolean           @default(false)
  aiReasoningJson Json?             @db.JsonB

  // Admin override
  overriddenBy String?
  overriddenAt DateTime?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  award            SpecialAward @relation(fields: [awardId], references: [id], onDelete: Cascade)
  project          Project      @relation(fields: [projectId], references: [id], onDelete: Cascade)
  overriddenByUser User?        @relation("AwardEligibilityOverriddenBy", fields: [overriddenBy], references: [id], onDelete: SetNull)

  @@unique([awardId, projectId])
  @@index([awardId])
  @@index([projectId])
  @@index([eligible])
  @@index([awardId, eligible])
}

model AwardJuror {
  id      String @id @default(cuid())
  awardId String
  userId  String

  createdAt DateTime @default(now())

  // Relations
  award SpecialAward @relation(fields: [awardId], references: [id], onDelete: Cascade)
  user  User         @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([awardId, userId])
  @@index([awardId])
  @@index([userId])
}

model AwardVote {
  id        String   @id @default(cuid())
  awardId   String
  userId    String
  projectId String
  rank      Int? // For RANKED mode
  votedAt   DateTime @default(now())

  // Relations
  award   SpecialAward @relation(fields: [awardId], references: [id], onDelete: Cascade)
  user    User         @relation(fields: [userId], references: [id], onDelete: Cascade)
  project Project      @relation(fields: [projectId], references: [id], onDelete: Cascade)

  @@unique([awardId, userId, projectId])
  @@index([awardId])
  @@index([userId])
  @@index([projectId])
  @@index([awardId, userId])
}

// =============================================================================
// REMINDER LOG (Evaluation Deadline Reminders)
// =============================================================================

model ReminderLog {
  id      String   @id @default(cuid())
  roundId String
  userId  String
  type    String // "3_DAYS", "24H", "1H"
  sentAt  DateTime @default(now())

  // Relations
  round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)
  user  User  @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([roundId, userId, type])
  @@index([roundId])
}

// =============================================================================
// CONFLICT OF INTEREST
// =============================================================================

model ConflictOfInterest {
  id           String   @id @default(cuid())
  assignmentId String   @unique
  userId       String
  projectId    String
  roundId      String? // Legacy — kept for historical data
  hasConflict  Boolean  @default(false)
  conflictType String? // "financial", "personal", "organizational", "other"
  description  String?  @db.Text
  declaredAt   DateTime @default(now())

  // Admin review
  reviewedById String?
  reviewedAt   DateTime?
  reviewAction String? // "cleared", "reassigned", "noted"

  // Relations
  assignment Assignment @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
  user       User       @relation(fields: [userId], references: [id])
  reviewedBy User?      @relation("COIReviewedBy", fields: [reviewedById], references: [id])

  @@index([userId])
  @@index([hasConflict])
}

// =============================================================================
// AI EVALUATION SUMMARY
// =============================================================================

model EvaluationSummary {
  id            String   @id @default(cuid())
  projectId     String
  roundId       String
  summaryJson   Json     @db.JsonB
  generatedAt   DateTime @default(now())
  generatedById String
  model         String
  tokensUsed    Int

  // Relations
  project     Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
  round       Round   @relation(fields: [roundId], references: [id], onDelete: Cascade)
  generatedBy User    @relation("EvaluationSummaryGeneratedBy", fields: [generatedById], references: [id])

  @@unique([projectId, roundId])
  @@index([roundId])
}

// =============================================================================
// PROJECT STATUS HISTORY
// =============================================================================

model ProjectStatusHistory {
  id        String        @id @default(cuid())
  projectId String
  status    ProjectStatus
  changedAt DateTime      @default(now())
  changedBy String?

  // Relations
  project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)

  @@index([projectId, changedAt])
}

// =============================================================================
// MENTOR MESSAGES
// =============================================================================

model MentorMessage {
  id        String   @id @default(cuid())
  projectId String
  senderId  String
  message   String   @db.Text
  isRead    Boolean  @default(false)
  createdAt DateTime @default(now())

  // ── Competition/Round architecture fields ──
  workspaceId String?           // FK to MentorAssignment (used as workspace)
  senderRole  MentorMessageRole?

  // Relations
  project   Project           @relation(fields: [projectId], references: [id], onDelete: Cascade)
  sender    User              @relation("MentorMessageSender", fields: [senderId], references: [id])
  workspace MentorAssignment? @relation(fields: [workspaceId], references: [id], onDelete: Cascade)

  @@index([projectId, createdAt])
  @@index([workspaceId])
}

// RoundTemplate model RETIRED in Phase 6 — replaced by WizardTemplate for pipelines.

// =============================================================================
// MENTOR NOTES & MILESTONES
// =============================================================================

model MentorNote {
  id                 String  @id @default(cuid())
  mentorAssignmentId String
  authorId           String
  content            String  @db.Text
  isVisibleToAdmin   Boolean @default(true)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  mentorAssignment MentorAssignment @relation(fields: [mentorAssignmentId], references: [id], onDelete: Cascade)
  author           User             @relation("MentorNoteAuthor", fields: [authorId], references: [id])

  @@index([mentorAssignmentId])
  @@index([authorId])
}

model MentorMilestone {
  id                 String  @id @default(cuid())
  programId          String
  name               String
  description        String? @db.Text
  isRequired         Boolean @default(false)
  deadlineOffsetDays Int?
  sortOrder          Int     @default(0)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  program     Program                     @relation(fields: [programId], references: [id], onDelete: Cascade)
  completions MentorMilestoneCompletion[]

  @@index([programId])
  @@index([sortOrder])
}

model MentorMilestoneCompletion {
  id                 String   @id @default(cuid())
  milestoneId        String
  mentorAssignmentId String
  completedById      String
  completedAt        DateTime @default(now())

  // Relations
  milestone        MentorMilestone  @relation(fields: [milestoneId], references: [id], onDelete: Cascade)
  mentorAssignment MentorAssignment @relation(fields: [mentorAssignmentId], references: [id], onDelete: Cascade)
  completedBy      User             @relation("MilestoneCompletedBy", fields: [completedById], references: [id])

  @@unique([milestoneId, mentorAssignmentId])
  @@index([mentorAssignmentId])
  @@index([completedById])
}

// =============================================================================
// EVALUATION DISCUSSIONS
// =============================================================================

model EvaluationDiscussion {
  id         String    @id @default(cuid())
  projectId  String
  roundId    String
  status     String    @default("open") // 'open' | 'closed'
  createdAt  DateTime  @default(now())
  closedAt   DateTime?
  closedById String?

  // Relations
  project  Project             @relation(fields: [projectId], references: [id], onDelete: Cascade)
  round    Round               @relation(fields: [roundId], references: [id], onDelete: Cascade)
  closedBy User?               @relation("DiscussionClosedBy", fields: [closedById], references: [id], onDelete: SetNull)
  comments DiscussionComment[]

  @@unique([projectId, roundId])
  @@index([roundId])
  @@index([closedById])
}

model DiscussionComment {
  id           String @id @default(cuid())
  discussionId String
  userId       String
  content      String @db.Text

  createdAt DateTime @default(now())

  // Relations
  discussion EvaluationDiscussion @relation(fields: [discussionId], references: [id], onDelete: Cascade)
  user       User                 @relation("DiscussionCommentAuthor", fields: [userId], references: [id])

  @@index([discussionId])
  @@index([userId])
}

// =============================================================================
// MESSAGING SYSTEM
// =============================================================================

model Message {
  id               String   @id @default(cuid())
  senderId         String
  recipientType    String // 'USER', 'ROLE', 'ROUND_JURY', 'PROGRAM_TEAM', 'ALL'
  recipientFilter  Json?    @db.JsonB
  roundId          String?
  templateId       String?
  subject          String
  body             String   @db.Text
  deliveryChannels String[]

  scheduledAt DateTime?
  sentAt      DateTime?
  metadata    Json?     @db.JsonB

  createdAt DateTime @default(now())

  // Relations
  sender     User               @relation("MessageSender", fields: [senderId], references: [id])
  round      Round?             @relation(fields: [roundId], references: [id], onDelete: SetNull)
  template   MessageTemplate?   @relation(fields: [templateId], references: [id], onDelete: SetNull)
  recipients MessageRecipient[]

  @@index([senderId])
  @@index([roundId])
  @@index([sentAt])
}

model MessageRecipient {
  id          String    @id @default(cuid())
  messageId   String
  userId      String
  channel     String // 'EMAIL', 'IN_APP', etc.
  isRead      Boolean   @default(false)
  readAt      DateTime?
  deliveredAt DateTime?

  // Relations
  message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
  user    User    @relation("MessageRecipient", fields: [userId], references: [id], onDelete: Cascade)

  @@unique([messageId, userId, channel])
  @@index([userId])
}

model MessageTemplate {
  id        String  @id @default(cuid())
  name      String
  category  String // 'SYSTEM', 'EVALUATION', 'ASSIGNMENT'
  subject   String
  body      String  @db.Text
  variables Json?   @db.JsonB
  isActive  Boolean @default(true)
  createdBy String

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  creator  User      @relation("MessageTemplateCreator", fields: [createdBy], references: [id])
  messages Message[]

  @@index([category])
  @@index([isActive])
}

// =============================================================================
// WEBHOOKS
// =============================================================================

model Webhook {
  id          String   @id @default(cuid())
  name        String
  url         String
  secret      String
  events      String[]
  headers     Json?    @db.JsonB
  maxRetries  Int      @default(3)
  isActive    Boolean  @default(true)
  createdById String

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  createdBy  User              @relation("WebhookCreator", fields: [createdById], references: [id])
  deliveries WebhookDelivery[]

  @@index([isActive])
  @@index([createdById])
}

model WebhookDelivery {
  id             String    @id @default(cuid())
  webhookId      String
  event          String
  payload        Json      @db.JsonB
  status         String    @default("PENDING") // 'PENDING', 'DELIVERED', 'FAILED'
  responseStatus Int?
  responseBody   String?   @db.Text
  attempts       Int       @default(0)
  lastAttemptAt  DateTime?

  createdAt DateTime @default(now())

  // Relations
  webhook Webhook @relation(fields: [webhookId], references: [id], onDelete: Cascade)

  @@index([webhookId])
  @@index([status])
  @@index([event])
}

// =============================================================================
// DIGEST LOGS
// =============================================================================

model DigestLog {
  id          String   @id @default(cuid())
  userId      String
  digestType  String // 'daily' | 'weekly'
  contentJson Json     @db.JsonB
  sentAt      DateTime @default(now())

  // Relations
  user User @relation("DigestLog", fields: [userId], references: [id], onDelete: Cascade)

  @@index([userId])
  @@index([sentAt])
}

// =============================================================================
// COHORT & LIVE PROGRESS (formerly part of Stage engine, now refit to Round)
// =============================================================================

model Cohort {
  id            String    @id @default(cuid())
  roundId       String
  name          String
  votingMode    String    @default("simple") // simple, criteria, ranked
  isOpen        Boolean   @default(false)
  windowOpenAt  DateTime?
  windowCloseAt DateTime?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round    Round           @relation(fields: [roundId], references: [id], onDelete: Cascade)
  projects CohortProject[]

  @@index([roundId])
  @@index([isOpen])
}

model CohortProject {
  id        String @id @default(cuid())
  cohortId  String
  projectId String
  sortOrder Int    @default(0)

  createdAt DateTime @default(now())

  // Relations
  cohort  Cohort  @relation(fields: [cohortId], references: [id], onDelete: Cascade)
  project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)

  @@unique([cohortId, projectId])
  @@index([cohortId])
  @@index([projectId])
  @@index([sortOrder])
}

model LiveProgressCursor {
  id               String  @id @default(cuid())
  roundId          String  @unique
  sessionId        String  @unique @default(cuid())
  activeProjectId  String?
  activeOrderIndex Int     @default(0)
  isPaused         Boolean @default(false)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)

  @@index([sessionId])
}

model OverrideAction {
  id            String             @id @default(cuid())
  entityType    String // ProjectRoundState, FilteringResult, AwardEligibility, etc.
  entityId      String
  previousValue Json?              @db.JsonB
  newValueJson  Json               @db.JsonB
  reasonCode    OverrideReasonCode
  reasonText    String?            @db.Text
  actorId       String

  createdAt DateTime @default(now())

  @@index([entityType, entityId])
  @@index([actorId])
  @@index([reasonCode])
  @@index([createdAt])
}

model DecisionAuditLog {
  id           String  @id @default(cuid())
  eventType    String // stage.transitioned, routing.executed, filtering.completed, etc.
  entityType   String
  entityId     String
  actorId      String?
  detailsJson  Json?   @db.JsonB
  snapshotJson Json?   @db.JsonB // State at time of decision

  createdAt DateTime @default(now())

  @@index([eventType])
  @@index([entityType, entityId])
  @@index([actorId])
  @@index([createdAt])
}

model NotificationPolicy {
  id         String  @id @default(cuid())
  eventType  String  @unique // stage.transitioned, filtering.completed, etc.
  channel    String  @default("EMAIL") // EMAIL, IN_APP, BOTH, NONE
  templateId String? // Optional reference to MessageTemplate
  isActive   Boolean @default(true)
  configJson Json?   @db.JsonB // Additional config (delay, batch, etc.)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@index([eventType])
  @@index([isActive])
}

// =============================================================================
// COMPETITION / ROUND ENGINE MODELS (NEW — coexists with Pipeline/Track/Stage)
// =============================================================================

model Competition {
  id        String            @id @default(cuid())
  programId String
  name      String
  slug      String            @unique
  status    CompetitionStatus @default(DRAFT)

  // Competition-wide settings
  categoryMode             String @default("SHARED")
  startupFinalistCount     Int    @default(3)
  conceptFinalistCount     Int    @default(3)

  // Notification preferences
  notifyOnRoundAdvance     Boolean @default(true)
  notifyOnDeadlineApproach Boolean @default(true)
  deadlineReminderDays     Int[]   @default([7, 3, 1])

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  program              Program                @relation(fields: [programId], references: [id], onDelete: Cascade)
  rounds               Round[]
  juryGroups           JuryGroup[]
  submissionWindows    SubmissionWindow[]
  specialAwards        SpecialAward[]
  deliberationSessions DeliberationSession[]
  resultLocks          ResultLock[]

  @@index([programId])
  @@index([status])
}

model Round {
  id            String      @id @default(cuid())
  competitionId String
  name          String
  slug          String
  roundType     RoundType
  status        RoundStatus @default(ROUND_DRAFT)
  sortOrder     Int         @default(0)

  // Time windows
  windowOpenAt  DateTime?
  windowCloseAt DateTime?

  // Round-type-specific configuration (validated by Zod per RoundType)
  configJson Json? @db.JsonB

  // Semantic analytics tag
  purposeKey String?

  // Links to other entities
  juryGroupId        String?
  submissionWindowId String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  competition              Competition                @relation(fields: [competitionId], references: [id], onDelete: Cascade)
  juryGroup                JuryGroup?                 @relation(fields: [juryGroupId], references: [id], onDelete: SetNull)
  submissionWindow         SubmissionWindow?          @relation(fields: [submissionWindowId], references: [id], onDelete: SetNull)
  projectRoundStates       ProjectRoundState[]
  advancementRules         AdvancementRule[]
  visibleSubmissionWindows RoundSubmissionVisibility[]
  assignmentIntents        AssignmentIntent[]
  deliberationSessions     DeliberationSession[]
  resultLocks              ResultLock[]
  submissionPromotions     SubmissionPromotionEvent[]
  specialAwards            SpecialAward[]

  // Reverse relations from refitted models
  evaluationForms       EvaluationForm[]
  fileRequirements      FileRequirement[]
  assignments           Assignment[]
  gracePeriods          GracePeriod[]
  liveVotingSession     LiveVotingSession?
  filteringRules        FilteringRule[]
  filteringResults      FilteringResult[]
  filteringJobs         FilteringJob[]
  assignmentJobs        AssignmentJob[]
  reminderLogs          ReminderLog[]
  evaluationSummaries   EvaluationSummary[]
  evaluationDiscussions EvaluationDiscussion[]
  messages              Message[]
  cohorts               Cohort[]
  liveCursor            LiveProgressCursor?

  @@unique([competitionId, slug])
  @@unique([competitionId, sortOrder])
  @@index([competitionId])
  @@index([roundType])
  @@index([status])
}

model ProjectRoundState {
  id           String                 @id @default(cuid())
  projectId    String
  roundId      String
  state        ProjectRoundStateValue @default(PENDING)
  enteredAt    DateTime               @default(now())
  exitedAt     DateTime?
  metadataJson Json?                  @db.JsonB

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
  round   Round   @relation(fields: [roundId], references: [id], onDelete: Cascade)

  @@unique([projectId, roundId])
  @@index([projectId])
  @@index([roundId])
  @@index([state])
}

model AdvancementRule {
  id            String              @id @default(cuid())
  roundId       String
  targetRoundId String?
  ruleType      AdvancementRuleType
  configJson    Json                @db.JsonB
  isDefault     Boolean             @default(true)
  sortOrder     Int                 @default(0)

  createdAt DateTime @default(now())

  // Relations
  round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)

  @@unique([roundId, sortOrder])
  @@index([roundId])
}

// =============================================================================
// JURY GROUP MODELS (NEW)
// =============================================================================

model JuryGroup {
  id            String @id @default(cuid())
  competitionId String
  name          String
  slug          String
  description   String? @db.Text
  sortOrder     Int     @default(0)

  // Default assignment configuration
  defaultMaxAssignments Int     @default(20)
  defaultCapMode        CapMode @default(SOFT)
  softCapBuffer         Int     @default(2)

  // Default category quotas
  categoryQuotasEnabled Boolean @default(false)
  defaultCategoryQuotas Json?   @db.JsonB

  // Onboarding self-service
  allowJurorCapAdjustment   Boolean @default(false)
  allowJurorRatioAdjustment Boolean @default(false)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  competition Competition       @relation(fields: [competitionId], references: [id], onDelete: Cascade)
  members     JuryGroupMember[]
  rounds      Round[]
  assignments Assignment[]
  awards      SpecialAward[]

  @@unique([competitionId, slug])
  @@index([competitionId])
}

model JuryGroupMember {
  id          String              @id @default(cuid())
  juryGroupId String
  userId      String
  role        JuryGroupMemberRole @default(MEMBER)
  joinedAt    DateTime            @default(now())

  // Per-juror overrides (null = use group defaults)
  maxAssignmentsOverride Int?
  capModeOverride        CapMode?
  categoryQuotasOverride Json?    @db.JsonB

  // Juror preferences (admin-set)
  preferredStartupRatio Float?
  availabilityNotes     String? @db.Text

  // Self-service overrides (set by juror during onboarding, Layer 4b)
  selfServiceCap   Int?
  selfServiceRatio Float?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  juryGroup                  JuryGroup                @relation(fields: [juryGroupId], references: [id], onDelete: Cascade)
  user                       User                     @relation(fields: [userId], references: [id], onDelete: Cascade)
  assignmentIntents          AssignmentIntent[]
  deliberationVotes          DeliberationVote[]
  deliberationParticipations DeliberationParticipant[]

  @@unique([juryGroupId, userId])
  @@index([juryGroupId])
  @@index([userId])
}

// =============================================================================
// SUBMISSION WINDOW MODELS (NEW)
// =============================================================================

model SubmissionWindow {
  id            String @id @default(cuid())
  competitionId String
  name          String
  slug          String
  roundNumber   Int
  sortOrder     Int    @default(0)

  // Window timing
  windowOpenAt  DateTime?
  windowCloseAt DateTime?

  // Deadline behavior
  deadlinePolicy DeadlinePolicy @default(FLAG)
  graceHours     Int?

  // Locking behavior
  lockOnClose Boolean @default(true)
  isLocked    Boolean @default(false)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  competition      Competition                @relation(fields: [competitionId], references: [id], onDelete: Cascade)
  fileRequirements SubmissionFileRequirement[]
  projectFiles     ProjectFile[]
  rounds           Round[]
  visibility       RoundSubmissionVisibility[]

  @@unique([competitionId, slug])
  @@unique([competitionId, roundNumber])
  @@index([competitionId])
}

model SubmissionFileRequirement {
  id                 String   @id @default(cuid())
  submissionWindowId String
  label              String
  slug               String
  description        String?  @db.Text
  mimeTypes          String[]
  maxSizeMb          Int?
  required           Boolean  @default(true)
  sortOrder          Int      @default(0)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  submissionWindow SubmissionWindow @relation(fields: [submissionWindowId], references: [id], onDelete: Cascade)
  files            ProjectFile[]

  @@unique([submissionWindowId, slug])
  @@index([submissionWindowId])
}

model RoundSubmissionVisibility {
  id                 String @id @default(cuid())
  roundId            String
  submissionWindowId String
  canView            Boolean @default(true)
  displayLabel       String?

  // Relations
  round            Round            @relation(fields: [roundId], references: [id], onDelete: Cascade)
  submissionWindow SubmissionWindow @relation(fields: [submissionWindowId], references: [id], onDelete: Cascade)

  @@unique([roundId, submissionWindowId])
  @@index([roundId])
}

// =============================================================================
// ASSIGNMENT GOVERNANCE MODELS (NEW)
// =============================================================================

model AssignmentIntent {
  id                String                 @id @default(cuid())
  juryGroupMemberId String
  roundId           String
  projectId         String
  source            AssignmentIntentSource
  status            AssignmentIntentStatus @default(INTENT_PENDING)

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  juryGroupMember JuryGroupMember @relation(fields: [juryGroupMemberId], references: [id], onDelete: Cascade)
  round           Round           @relation(fields: [roundId], references: [id], onDelete: Cascade)
  project         Project         @relation(fields: [projectId], references: [id], onDelete: Cascade)

  @@unique([juryGroupMemberId, roundId, projectId])
  @@index([roundId])
  @@index([projectId])
  @@index([status])
}

model AssignmentException {
  id           String   @id @default(cuid())
  assignmentId String
  reason       String   @db.Text
  overCapBy    Int
  approvedById String
  createdAt    DateTime @default(now())

  // Relations
  assignment Assignment @relation(fields: [assignmentId], references: [id], onDelete: Cascade)
  approvedBy User       @relation("AssignmentExceptionApprover", fields: [approvedById], references: [id])

  @@index([assignmentId])
  @@index([approvedById])
}

// =============================================================================
// MENTORING WORKSPACE MODELS (NEW)
// =============================================================================

model MentorFile {
  id                 String @id @default(cuid())
  mentorAssignmentId String
  uploadedByUserId   String

  fileName    String
  mimeType    String
  size        Int
  bucket      String
  objectKey   String
  description String? @db.Text

  // Promotion to official submission
  isPromoted       Boolean   @default(false)
  promotedToFileId String?   @unique
  promotedAt       DateTime?
  promotedByUserId String?

  createdAt DateTime @default(now())

  // Relations
  mentorAssignment MentorAssignment          @relation(fields: [mentorAssignmentId], references: [id], onDelete: Cascade)
  uploadedBy       User                      @relation("MentorFileUploader", fields: [uploadedByUserId], references: [id])
  promotedBy       User?                     @relation("MentorFilePromoter", fields: [promotedByUserId], references: [id])
  promotedFile     ProjectFile?              @relation("PromotedFromMentorFile", fields: [promotedToFileId], references: [id], onDelete: SetNull)
  comments         MentorFileComment[]
  promotionEvents  SubmissionPromotionEvent[]

  @@index([mentorAssignmentId])
  @@index([uploadedByUserId])
}

model MentorFileComment {
  id           String @id @default(cuid())
  mentorFileId String
  authorId     String
  content      String @db.Text

  // Threading support
  parentCommentId String?

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  mentorFile    MentorFile         @relation(fields: [mentorFileId], references: [id], onDelete: Cascade)
  author        User               @relation("MentorFileCommentAuthor", fields: [authorId], references: [id])
  parentComment MentorFileComment? @relation("CommentThread", fields: [parentCommentId], references: [id], onDelete: Cascade)
  replies       MentorFileComment[] @relation("CommentThread")

  @@index([mentorFileId])
  @@index([authorId])
  @@index([parentCommentId])
}

model SubmissionPromotionEvent {
  id           String                   @id @default(cuid())
  projectId    String
  roundId      String
  slotKey      String
  sourceType   SubmissionPromotionSource
  sourceFileId String?
  promotedById String
  createdAt    DateTime                 @default(now())

  // Relations
  project    Project     @relation(fields: [projectId], references: [id], onDelete: Cascade)
  round      Round       @relation(fields: [roundId], references: [id], onDelete: Cascade)
  sourceFile MentorFile? @relation(fields: [sourceFileId], references: [id], onDelete: SetNull)
  promotedBy User        @relation("SubmissionPromoter", fields: [promotedById], references: [id])

  @@index([projectId])
  @@index([roundId])
  @@index([sourceFileId])
}

// =============================================================================
// DELIBERATION MODELS (NEW)
// =============================================================================

model DeliberationSession {
  id                     String              @id @default(cuid())
  competitionId          String
  roundId                String
  category               CompetitionCategory
  mode                   DeliberationMode
  showCollectiveRankings Boolean             @default(false)
  showPriorJuryData      Boolean             @default(false)
  status                 DeliberationStatus
  tieBreakMethod         TieBreakMethod
  adminOverrideResult    Json?               @db.JsonB

  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  // Relations
  competition  Competition               @relation(fields: [competitionId], references: [id], onDelete: Cascade)
  round        Round                     @relation(fields: [roundId], references: [id], onDelete: Cascade)
  votes        DeliberationVote[]
  results      DeliberationResult[]
  participants DeliberationParticipant[]

  @@index([competitionId])
  @@index([roundId])
  @@index([status])
}

model DeliberationVote {
  id           String   @id @default(cuid())
  sessionId    String
  juryMemberId String
  projectId    String
  rank         Int?
  isWinnerPick Boolean  @default(false)
  runoffRound  Int      @default(0)
  createdAt    DateTime @default(now())

  // Relations
  session    DeliberationSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
  juryMember JuryGroupMember     @relation(fields: [juryMemberId], references: [id], onDelete: Cascade)
  project    Project             @relation(fields: [projectId], references: [id], onDelete: Cascade)

  @@unique([sessionId, juryMemberId, projectId, runoffRound])
  @@index([sessionId])
  @@index([juryMemberId])
  @@index([projectId])
}

model DeliberationResult {
  id                String  @id @default(cuid())
  sessionId         String
  projectId         String
  finalRank         Int
  voteCount         Int     @default(0)
  isAdminOverridden Boolean @default(false)
  overrideReason    String? @db.Text

  // Relations
  session DeliberationSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
  project Project             @relation(fields: [projectId], references: [id], onDelete: Cascade)

  @@unique([sessionId, projectId])
  @@index([sessionId])
  @@index([projectId])
}

model DeliberationParticipant {
  id           String                        @id @default(cuid())
  sessionId    String
  userId       String
  status       DeliberationParticipantStatus
  replacedById String?

  // Relations
  session    DeliberationSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
  user       JuryGroupMember     @relation(fields: [userId], references: [id], onDelete: Cascade)
  replacedBy User?               @relation("DeliberationReplacement", fields: [replacedById], references: [id])

  @@unique([sessionId, userId])
  @@index([sessionId])
  @@index([userId])
}

// =============================================================================
// RESULT LOCKING MODELS (NEW)
// =============================================================================

model ResultLock {
  id             String              @id @default(cuid())
  competitionId  String
  roundId        String
  category       CompetitionCategory
  lockedById     String
  resultSnapshot Json                @db.JsonB
  lockedAt       DateTime            @default(now())

  // Relations
  competition  Competition         @relation(fields: [competitionId], references: [id], onDelete: Cascade)
  round        Round               @relation(fields: [roundId], references: [id], onDelete: Cascade)
  lockedBy     User                @relation("ResultLockCreator", fields: [lockedById], references: [id])
  unlockEvents ResultUnlockEvent[]

  @@index([competitionId])
  @@index([roundId])
  @@index([category])
}

model ResultUnlockEvent {
  id           String   @id @default(cuid())
  resultLockId String
  unlockedById String
  reason       String   @db.Text
  unlockedAt   DateTime @default(now())

  // Relations
  resultLock ResultLock @relation(fields: [resultLockId], references: [id], onDelete: Cascade)
  unlockedBy User       @relation("ResultUnlocker", fields: [unlockedById], references: [id])

  @@index([resultLockId])
  @@index([unlockedById])
}
