# Special Awards System

## Overview

Special Awards are standalone award tracks that run parallel to the main competition flow. They enable the MOPC platform to recognize excellence in specific areas (e.g., "Innovation Award", "Impact Award", "Youth Leadership Award") with dedicated juries and evaluation processes while referencing the same pool of projects.

### Purpose

Special Awards serve three key purposes:

1. **Parallel Recognition** — Recognize excellence in specific domains beyond the main competition prizes
2. **Specialized Evaluation** — Enable dedicated jury groups with domain expertise to evaluate specific criteria
3. **Flexible Integration** — Awards can piggyback on main rounds or run independently with their own timelines

### Design Philosophy

- **Standalone Entities** — Awards are not tracks; they're first-class entities linked to competitions
- **Two Modes** — STAY_IN_MAIN (piggyback evaluation) or SEPARATE_POOL (independent flow)
- **Dedicated Juries** — Each award can have its own jury group with unique members or shared members
- **Flexible Eligibility** — AI-suggested, manual, round-based, or all-eligible modes
- **Integration with Results** — Award results feed into the confirmation round alongside main competition winners

---

## Current System Analysis

### Current Architecture (Pipeline-Based)

**Current State:**
```
Program
 └── Pipeline
      ├── Track: "Main Competition" (MAIN)
      └── Track: "Innovation Award" (AWARD)
           ├── Stage: "Evaluation" (EVALUATION)
           └── Stage: "Results" (RESULTS)

SpecialAward {
  id, programId, name, description
  trackId → Track (AWARD track)
  criteriaText (for AI)
  scoringMode: PICK_WINNER | RANKED | SCORED
  votingStartAt, votingEndAt
  winnerProjectId
  useAiEligibility: boolean
}

AwardEligibility { awardId, projectId, eligible, method, aiReasoningJson }
AwardJuror { awardId, userId }
AwardVote { awardId, userId, projectId, rank? }
```

**Current Flow:**
1. Admin creates AWARD track within pipeline
2. Admin configures SpecialAward linked to track
3. Projects routed to award track via ProjectStageState
4. AI or manual eligibility determination
5. Award jurors evaluate/vote
6. Winner selected (admin/award master decision)

**Current Limitations:**
- Awards tied to track concept (being eliminated)
- No distinction between "piggyback" awards and independent awards
- No round-based eligibility
- No jury group integration
- No evaluation form linkage
- No audience voting support
- No integration with confirmation round

---

## Redesigned System: Two Award Modes

### Mode 1: STAY_IN_MAIN

**Concept:** Projects remain in the main competition flow. A dedicated award jury evaluates them using the same submissions, during the same evaluation windows.

**Use Case:** "Innovation Award" — Members of Jury 2 who also serve on the Innovation Award jury score projects specifically for innovation criteria during the Jury 2 evaluation round.

**Characteristics:**
- Projects never leave main track
- Award jury evaluates during specific main evaluation rounds
- Award jury sees the same docs/submissions as main jury
- Award uses its own evaluation form with award-specific criteria
- No separate stages/timeline needed
- Results announced alongside main results

**Data Flow:**
```
Competition → Round 5 (Jury 2 Evaluation)
                ├─ Main Jury (Jury 2) evaluates with standard criteria
                └─ Innovation Award Jury evaluates same projects with innovation criteria

SpecialAward {
  evaluationMode: "STAY_IN_MAIN"
  evaluationRoundId: "round-5" ← Which main round this award evaluates during
  juryGroupId: "innovation-jury" ← Dedicated jury
  evaluationFormId: "innovation-form" ← Award-specific criteria
}
```

### Mode 2: SEPARATE_POOL

**Concept:** Dedicated evaluation with separate criteria, submission requirements, and timeline. Projects may be pulled out for award-specific evaluation.

**Use Case:** "Community Impact Award" — Separate jury evaluates finalists specifically for community impact using a unique rubric and potentially additional documentation.

**Characteristics:**
- Own jury group with unique members
- Own evaluation criteria/form
- Can have own submission requirements
- Runs on its own timeline
- Can pull projects from specific rounds
- Independent results timeline

**Data Flow:**
```
Competition
 └── SpecialAward {
      evaluationMode: "SEPARATE_POOL"
      eligibilityMode: "ROUND_BASED" ← Projects from Round 5 (finalists)
      juryGroupId: "impact-jury"
      evaluationFormId: "impact-form"
      votingStartAt: [own window]
      votingEndAt: [own window]
     }
```

---

## Enhanced SpecialAward Model

### Complete Schema

```prisma
model SpecialAward {
  id              String @id @default(cuid())
  competitionId   String                    // CHANGED: Links to Competition, not Track
  name            String
  description     String? @db.Text

  // Eligibility configuration
  eligibilityMode      AwardEligibilityMode @default(AI_SUGGESTED)
  eligibilityCriteria  Json? @db.JsonB      // Mode-specific config

  // Evaluation configuration
  evaluationMode       AwardEvaluationMode @default(STAY_IN_MAIN)
  evaluationRoundId    String?              // Which main round (for STAY_IN_MAIN)
  evaluationFormId     String?              // Custom criteria
  juryGroupId          String?              // Dedicated or shared jury

  // Voting configuration
  votingMode           AwardVotingMode @default(JURY_ONLY)
  scoringMode          AwardScoringMode @default(PICK_WINNER)
  maxRankedPicks       Int?                 // For RANKED mode
  maxWinners           Int @default(1)      // Number of winners
  audienceVotingWeight Float?               // 0.0-1.0 for COMBINED mode

  // Timing
  votingStartAt   DateTime?
  votingEndAt     DateTime?

  // Results
  status          AwardStatus @default(DRAFT)
  winnerProjectId String?                   // Single winner (for backward compat)

  // AI eligibility
  useAiEligibility Boolean @default(false)
  criteriaText     String? @db.Text         // Plain-language for AI

  // Job tracking (for AI eligibility)
  eligibilityJobStatus  String?
  eligibilityJobTotal   Int?
  eligibilityJobDone    Int?
  eligibilityJobError   String? @db.Text
  eligibilityJobStarted DateTime?

  sortOrder       Int @default(0)
  createdAt       DateTime @default(now())
  updatedAt       DateTime @updatedAt

  // Relations
  competition      Competition        @relation(fields: [competitionId], references: [id], onDelete: Cascade)
  evaluationRound  Round?             @relation("AwardEvaluationRound", fields: [evaluationRoundId], references: [id], onDelete: SetNull)
  evaluationForm   EvaluationForm?    @relation(fields: [evaluationFormId], references: [id], onDelete: SetNull)
  juryGroup        JuryGroup?         @relation("AwardJuryGroup", fields: [juryGroupId], references: [id], onDelete: SetNull)
  winnerProject    Project?           @relation("AwardWinner", fields: [winnerProjectId], references: [id], onDelete: SetNull)

  eligibilities    AwardEligibility[]
  votes            AwardVote[]
  winners          AwardWinner[]      // NEW: Multi-winner support

  @@index([competitionId])
  @@index([status])
  @@index([evaluationRoundId])
  @@index([juryGroupId])
}

enum AwardEligibilityMode {
  AI_SUGGESTED     // AI analyzes and suggests eligible projects
  MANUAL           // Admin manually selects eligible projects
  ALL_ELIGIBLE     // All projects in competition are eligible
  ROUND_BASED      // All projects that reach a specific round
}

enum AwardEvaluationMode {
  STAY_IN_MAIN     // Evaluate during main competition round
  SEPARATE_POOL    // Independent evaluation flow
}

enum AwardVotingMode {
  JURY_ONLY        // Only jury votes
  AUDIENCE_ONLY    // Only audience votes
  COMBINED         // Jury + audience with weighted scoring
}

enum AwardScoringMode {
  PICK_WINNER      // Simple winner selection (1 or N winners)
  RANKED           // Ranked-choice voting
  SCORED           // Criteria-based scoring
}

enum AwardStatus {
  DRAFT
  NOMINATIONS_OPEN
  EVALUATION       // NEW: Award jury evaluation in progress
  DECIDED          // NEW: Winner(s) selected, pending announcement
  ANNOUNCED        // NEW: Winner(s) publicly announced
  ARCHIVED
}
```

### New Model: AwardWinner (Multi-Winner Support)

```prisma
model AwardWinner {
  id              String @id @default(cuid())
  awardId         String
  projectId       String
  rank            Int                // 1st place, 2nd place, etc.

  // Selection metadata
  selectedAt      DateTime @default(now())
  selectedById    String
  selectionMethod String             // "JURY_VOTE" | "AUDIENCE_VOTE" | "COMBINED" | "ADMIN_DECISION"

  // Score breakdown (for transparency)
  juryScore       Float?
  audienceScore   Float?
  finalScore      Float?

  createdAt DateTime @default(now())

  // Relations
  award       SpecialAward @relation(fields: [awardId], references: [id], onDelete: Cascade)
  project     Project      @relation("AwardWinners", fields: [projectId], references: [id], onDelete: Cascade)
  selectedBy  User         @relation("AwardWinnerSelector", fields: [selectedById], references: [id])

  @@unique([awardId, projectId])
  @@unique([awardId, rank])
  @@index([awardId])
  @@index([projectId])
}
```

### Enhanced AwardVote Model

```prisma
model AwardVote {
  id        String   @id @default(cuid())
  awardId   String
  userId    String?              // Nullable for audience votes
  projectId String

  // Voting type
  isAudienceVote Boolean @default(false)

  // Scoring (mode-dependent)
  rank      Int?                 // For RANKED mode (1 = first choice)
  score     Float?               // For SCORED mode

  // Criteria scores (for SCORED mode)
  criterionScoresJson Json? @db.JsonB

  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, isAudienceVote])
}
```

---

## Eligibility System Deep Dive

### Eligibility Modes

#### 1. AI_SUGGESTED

AI analyzes all projects and suggests eligible ones based on plain-language criteria.

**Config JSON:**
```typescript
type AISuggestedConfig = {
  criteriaText: string                    // "Projects using innovative ocean tech"
  confidenceThreshold: number             // 0.0-1.0 (default: 0.7)
  autoAcceptAbove: number                 // Auto-accept above this (default: 0.9)
  requireManualReview: boolean            // All need admin review (default: false)
  sourceRoundId?: string                  // Only projects from this round
}
```

**Flow:**
1. Admin triggers AI eligibility analysis
2. AI processes projects in batches (anonymized)
3. AI returns: `{ projectId, eligible, confidence, reasoning }`
4. High-confidence results auto-applied
5. Medium-confidence results flagged for review
6. Low-confidence results rejected (or flagged if `requireManualReview: true`)

**UI:**
```
┌─────────────────────────────────────────────────────────────┐
│ Innovation Award — AI Eligibility Analysis                  │
├─────────────────────────────────────────────────────────────┤
│ Status: Running... (47/120 projects analyzed)              │
│ [████████████████░░░░░░░░] 68%                             │
│                                                             │
│ Results So Far:                                             │
│ ✓ Auto-Accepted (confidence > 0.9): 12 projects            │
│ ⚠ Flagged for Review (0.6-0.9): 23 projects                │
│ ✗ Rejected (< 0.6): 12 projects                            │
│                                                             │
│ [View Flagged Projects]  [Stop Analysis]                   │
└─────────────────────────────────────────────────────────────┘
```

#### 2. MANUAL

Admin manually selects eligible projects.

**Config JSON:**
```typescript
type ManualConfig = {
  sourceRoundId?: string      // Limit to projects from specific round
  categoryFilter?: "STARTUP" | "BUSINESS_CONCEPT"
  tagFilters?: string[]       // Only projects with these tags
}
```

#### 3. ALL_ELIGIBLE

All projects in the competition are automatically eligible.

**Config JSON:**
```typescript
type AllEligibleConfig = {
  minimumStatus?: ProjectStatus           // e.g., "SEMIFINALIST" or above
  excludeWithdrawn: boolean               // Exclude WITHDRAWN (default: true)
}
```

#### 4. ROUND_BASED

All projects that reach a specific round are automatically eligible.

**Config JSON:**
```typescript
type RoundBasedConfig = {
  sourceRoundId: string                   // Required: which round
  requiredState: ProjectRoundStateValue   // PASSED, COMPLETED, etc.
  autoUpdate: boolean                     // Auto-update when projects advance (default: true)
}
```

**Example:**
```json
{
  "sourceRoundId": "round-5-jury-2",
  "requiredState": "PASSED",
  "autoUpdate": true
}
```

### Admin Override System

**All eligibility modes support admin override:**

```prisma
model AwardEligibility {
  id              String            @id @default(cuid())
  awardId         String
  projectId       String

  // Original determination
  method          EligibilityMethod @default(AUTO)  // AUTO, AI, MANUAL
  eligible        Boolean           @default(false)
  aiReasoningJson Json?             @db.JsonB

  // Override
  overriddenBy    String?
  overriddenAt    DateTime?
  overrideReason  String? @db.Text

  // Final decision
  finalEligible   Boolean           // Computed: overridden ? override : original

  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])

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

---

## Award Jury Groups

### Integration with JuryGroup Model

Awards can have:
1. **Dedicated Jury** — Own `JuryGroup` with unique members
2. **Shared Jury** — Reuse existing competition jury group (e.g., Jury 2)
3. **Mixed Jury** — Some overlap with main jury, some unique members

**Example:**
```typescript
// Dedicated jury for Innovation Award
const innovationJury = await prisma.juryGroup.create({
  data: {
    competitionId: "comp-2026",
    name: "Innovation Award Jury",
    slug: "innovation-jury",
    description: "Technology and innovation experts",
    defaultMaxAssignments: 15,
    defaultCapMode: "SOFT",
    categoryQuotasEnabled: false,
  }
})

// Add members (can overlap with main jury)
await prisma.juryGroupMember.createMany({
  data: [
    { juryGroupId: innovationJury.id, userId: "user-tech-1", isLead: true },
    { juryGroupId: innovationJury.id, userId: "user-tech-2" },
    { juryGroupId: innovationJury.id, userId: "jury-2-member-overlap" }, // Also on Jury 2
  ]
})

// Link to award
await prisma.specialAward.update({
  where: { id: awardId },
  data: { juryGroupId: innovationJury.id }
})
```

### Award Jury Assignment

#### For STAY_IN_MAIN Mode

Award jury members evaluate the same projects as the main jury, but with award-specific criteria.

**Assignment Creation:**
```typescript
// Main jury assignments (created by round)
Assignment { userId: "jury-2-member-1", projectId: "proj-A", roundId: "round-5", juryGroupId: "jury-2" }

// Award jury assignments (created separately, same round)
Assignment { userId: "innovation-jury-1", projectId: "proj-A", roundId: "round-5", juryGroupId: "innovation-jury" }
```

**Evaluation:**
- Award jury uses `evaluationFormId` linked to award
- Evaluations stored separately (different `assignmentId`)
- Both juries can evaluate same project in same round

#### For SEPARATE_POOL Mode

Award has its own assignment workflow, potentially for a subset of projects.

---

## Award Evaluation Flow

### STAY_IN_MAIN Evaluation

**Timeline:**
```
Round 5: Jury 2 Evaluation (Main)
├─ Opens: 2026-03-01
├─ Main Jury evaluates with standard form
├─ Innovation Award Jury evaluates with innovation form
└─ Closes: 2026-03-15

Award results calculated separately but announced together
```

**Step-by-Step:**

1. **Setup Phase**
   - Admin creates `SpecialAward { evaluationMode: "STAY_IN_MAIN", evaluationRoundId: "round-5" }`
   - Admin creates award-specific `EvaluationForm` with innovation criteria
   - Admin creates `JuryGroup` for Innovation Award
   - Admin adds members to jury group

2. **Eligibility Phase**
   - Eligibility determined (AI/manual/round-based)
   - Only eligible projects evaluated by award jury

3. **Assignment Phase**
   - When Round 5 opens, assignments created for award jury
   - Each award juror assigned eligible projects
   - Award assignments reference same `roundId` as main evaluation

4. **Evaluation Phase**
   - Award jurors see projects in their dashboard
   - Form shows award-specific criteria
   - Evaluations stored with `formId` = innovation form

5. **Results Phase**
   - Scores aggregated separately from main jury
   - Winner selection (jury vote, admin decision, etc.)
   - Results feed into confirmation round

### SEPARATE_POOL Evaluation

**Timeline:**
```
Round 5: Jury 2 Evaluation (Main) — March 1-15
  ↓
Round 6: Finalist Selection
  ↓
Impact Award Evaluation (Separate) — March 20 - April 5
├─ Own voting window
├─ Own evaluation form
├─ Impact Award Jury evaluates finalists
└─ Results: April 10
```

---

## Audience Voting for Awards

### Voting Modes

#### JURY_ONLY

Only jury members vote. Standard model.

#### AUDIENCE_ONLY

Only audience (public) votes. No jury involvement.

**Config:**
```typescript
type AudienceOnlyConfig = {
  requireIdentification: boolean          // Require email/phone (default: false)
  votesPerPerson: number                  // Max votes per person (default: 1)
  allowRanking: boolean                   // Ranked-choice (default: false)
  maxChoices?: number                     // For ranked mode
}
```

#### COMBINED

Jury + audience votes combined with weighted scoring.

**Config:**
```typescript
type CombinedConfig = {
  audienceWeight: number                  // 0.0-1.0 (e.g., 0.3 = 30% audience, 70% jury)
  juryWeight: number                      // 0.0-1.0 (should sum to 1.0)
  requireMinimumAudienceVotes: number     // Min votes for validity (default: 50)
  showAudienceResultsToJury: boolean      // Jury sees audience results (default: false)
}
```

**Scoring Calculation:**
```typescript
function calculateCombinedScore(
  juryScores: number[],
  audienceVoteCount: number,
  totalAudienceVotes: number,
  config: CombinedConfig
): number {
  const juryAvg = juryScores.reduce((a, b) => a + b, 0) / juryScores.length
  const audiencePercent = audienceVoteCount / totalAudienceVotes

  // Normalize jury score to 0-1 (assuming 1-10 scale)
  const normalizedJuryScore = juryAvg / 10

  const finalScore =
    (normalizedJuryScore * config.juryWeight) +
    (audiencePercent * config.audienceWeight)

  return finalScore
}
```

---

## Admin Experience

### Award Management Dashboard

```
┌─────────────────────────────────────────────────────────────────────────────┐
│ MOPC 2026 — Special Awards                                    [+ New Award] │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│ ┌───────────────────────────────────────────────────────────────────────┐   │
│ │ Innovation Award                                             [Edit ▼] │   │
│ │ Mode: Stay in Main (Jury 2 Evaluation) • Status: EVALUATION          │   │
│ ├───────────────────────────────────────────────────────────────────────┤   │
│ │ Eligible Projects: 18 / 20 finalists                                  │   │
│ │ Jury: Innovation Jury (5 members)                                     │   │
│ │ Evaluations: 72 / 90 (80% complete)                                   │   │
│ │ Voting Closes: March 15, 2026                                         │   │
│ │                                                                        │   │
│ │ [View Eligibility] [View Evaluations] [Select Winner]                │   │
│ └───────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
│ ┌───────────────────────────────────────────────────────────────────────┐   │
│ │ Community Impact Award                                       [Edit ▼] │   │
│ │ Mode: Separate Pool • Status: DRAFT                                   │   │
│ ├───────────────────────────────────────────────────────────────────────┤   │
│ │ Eligible Projects: Not yet determined (AI pending)                    │   │
│ │ Jury: Not assigned                                                    │   │
│ │ Voting Window: Not set                                                │   │
│ │                                                                        │   │
│ │ [Configure Eligibility] [Set Up Jury] [Set Timeline]                 │   │
│ └───────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

---

## Integration with Main Flow

### Awards Reference Main Competition Projects

Awards don't create their own project pool — they reference existing competition projects.

**Data Relationship:**
```
Competition
 ├── Projects (shared pool)
 │    ├── Project A
 │    ├── Project B
 │    └── Project C
 │
 ├── Main Rounds (linear flow)
 │    ├── Round 1: Intake
 │    ├── Round 5: Jury 2 Evaluation
 │    └── Round 7: Live Finals
 │
 └── Special Awards (parallel evaluation)
      ├── Innovation Award
      │    └── AwardEligibility { projectId: "A", eligible: true }
      │    └── AwardEligibility { projectId: "B", eligible: false }
      └── Impact Award
           └── AwardEligibility { projectId: "A", eligible: true }
           └── AwardEligibility { projectId: "C", eligible: true }
```

### Award Results Feed into Confirmation Round

**Confirmation Round Integration:**

The confirmation round (Round 8) includes:
1. Main competition winners (1st, 2nd, 3rd per category)
2. Special award winners

**WinnerProposal Extension:**
```prisma
model WinnerProposal {
  id              String @id @default(cuid())
  competitionId   String
  category        CompetitionCategory?   // Null for award winners

  // Main competition or award
  proposalType    WinnerProposalType @default(MAIN_COMPETITION)
  awardId         String?                // If proposalType = SPECIAL_AWARD

  status          WinnerProposalStatus @default(PENDING)
  rankedProjectIds String[]

  // ... rest of fields ...
}

enum WinnerProposalType {
  MAIN_COMPETITION    // Main 1st/2nd/3rd place
  SPECIAL_AWARD       // Award winner
}
```

---

## API Changes

### New tRPC Procedures

```typescript
// src/server/routers/award-redesign.ts

export const awardRedesignRouter = router({
  /**
   * Create a new special award
   */
  create: adminProcedure
    .input(z.object({
      competitionId: z.string(),
      name: z.string().min(1).max(255),
      description: z.string().optional(),
      eligibilityMode: z.enum(['AI_SUGGESTED', 'MANUAL', 'ALL_ELIGIBLE', 'ROUND_BASED']),
      evaluationMode: z.enum(['STAY_IN_MAIN', 'SEPARATE_POOL']),
      votingMode: z.enum(['JURY_ONLY', 'AUDIENCE_ONLY', 'COMBINED']),
      scoringMode: z.enum(['PICK_WINNER', 'RANKED', 'SCORED']),
      maxWinners: z.number().int().min(1).default(1),
    }))
    .mutation(async ({ ctx, input }) => { /* ... */ }),

  /**
   * Run eligibility determination
   */
  runEligibility: adminProcedure
    .input(z.object({ awardId: z.string() }))
    .mutation(async ({ ctx, input }) => { /* ... */ }),

  /**
   * Cast vote (jury or audience)
   */
  vote: protectedProcedure
    .input(z.object({
      awardId: z.string(),
      projectId: z.string(),
      rank: z.number().int().min(1).optional(),
      score: z.number().min(0).max(10).optional(),
    }))
    .mutation(async ({ ctx, input }) => { /* ... */ }),

  /**
   * Select winner(s)
   */
  selectWinners: adminProcedure
    .input(z.object({
      awardId: z.string(),
      winnerProjectIds: z.array(z.string()).min(1),
      selectionMethod: z.enum(['JURY_VOTE', 'AUDIENCE_VOTE', 'COMBINED', 'ADMIN_DECISION']),
    }))
    .mutation(async ({ ctx, input }) => { /* ... */ }),
})
```

---

## Service Functions

### Award Service Enhancements

```typescript
// src/server/services/award-service.ts

/**
 * Run round-based eligibility
 */
export async function runRoundBasedEligibility(
  award: SpecialAward,
  prisma = getPrisma()
) {
  const config = award.eligibilityCriteria as RoundBasedConfig

  if (!config.sourceRoundId) {
    throw new Error('Round-based eligibility requires sourceRoundId')
  }

  // Get all projects in the specified round with the required state
  const projectRoundStates = await prisma.projectRoundState.findMany({
    where: {
      roundId: config.sourceRoundId,
      state: config.requiredState ?? 'PASSED',
    },
    select: { projectId: true }
  })

  // Create/update eligibility records
  let created = 0
  let updated = 0

  for (const prs of projectRoundStates) {
    const existing = await prisma.awardEligibility.findUnique({
      where: {
        awardId_projectId: {
          awardId: award.id,
          projectId: prs.projectId
        }
      }
    })

    if (existing) {
      await prisma.awardEligibility.update({
        where: { id: existing.id },
        data: { eligible: true, method: 'AUTO' }
      })
      updated++
    } else {
      await prisma.awardEligibility.create({
        data: {
          awardId: award.id,
          projectId: prs.projectId,
          eligible: true,
          method: 'AUTO',
        }
      })
      created++
    }
  }

  return { created, updated, total: projectRoundStates.length }
}

/**
 * Calculate combined jury + audience score
 */
export function calculateCombinedScore(
  juryScores: number[],
  audienceVoteCount: number,
  totalAudienceVotes: number,
  juryWeight: number,
  audienceWeight: number
): number {
  if (juryScores.length === 0) {
    throw new Error('Cannot calculate combined score without jury votes')
  }

  const juryAvg = juryScores.reduce((a, b) => a + b, 0) / juryScores.length
  const normalizedJuryScore = juryAvg / 10  // Assume 1-10 scale

  const audiencePercent = totalAudienceVotes > 0
    ? audienceVoteCount / totalAudienceVotes
    : 0

  const finalScore =
    (normalizedJuryScore * juryWeight) +
    (audiencePercent * audienceWeight)

  return finalScore
}

/**
 * Create award jury assignments
 */
export async function createAwardAssignments(
  awardId: string,
  prisma = getPrisma()
) {
  const award = await prisma.specialAward.findUniqueOrThrow({
    where: { id: awardId },
    include: {
      juryGroup: {
        include: { members: true }
      }
    }
  })

  if (!award.juryGroupId || !award.juryGroup) {
    throw new Error('Award must have a jury group to create assignments')
  }

  const eligibleProjects = await getEligibleProjects(awardId, prisma)

  const assignments = []

  for (const project of eligibleProjects) {
    for (const member of award.juryGroup.members) {
      assignments.push({
        userId: member.userId,
        projectId: project.id,
        roundId: award.evaluationRoundId ?? null,
        juryGroupId: award.juryGroupId,
        method: 'MANUAL' as const,
      })
    }
  }

  await prisma.assignment.createMany({
    data: assignments,
    skipDuplicates: true,
  })

  return { created: assignments.length }
}
```

---

## Edge Cases

| Scenario | Handling |
|----------|----------|
| **Project eligible for multiple awards** | Allowed — project can win multiple awards |
| **Jury member on both main and award juries** | Allowed — separate assignments, separate evaluations |
| **Award voting ends before main results** | Award winner held until main results finalized, announced together |
| **Award eligibility changes mid-voting** | Admin override can remove eligibility; active votes invalidated |
| **Audience vote spam/fraud** | IP rate limiting, device fingerprinting, email verification, admin review |
| **Tie in award voting** | Admin decision or re-vote (configurable) |
| **Award jury not complete evaluations** | Admin can close voting with partial data or extend deadline |
| **Project withdrawn after eligible** | Eligibility auto-removed; votes invalidated |
| **Award criteria change after eligibility** | Re-run eligibility or grandfather existing eligible projects |
| **No eligible projects for award** | Award status set to DRAFT/ARCHIVED; no voting |

---

## Integration Points

### With Evaluation System
- Awards use `EvaluationForm` for criteria
- Award evaluations stored in `Evaluation` table with `formId` linkage
- Assignment system handles both main and award assignments

### With Jury Groups
- Awards can link to existing `JuryGroup` or have dedicated groups
- Jury members can overlap between main and award juries
- Caps and quotas honored for award assignments

### With Confirmation Round
- Award winners included in `WinnerProposal` system
- Confirmation flow handles both main and award winners
- Approval workflow requires sign-off on all winners

### With Notification System
- Eligibility notifications sent to eligible teams
- Voting reminders sent to award jurors
- Winner announcements coordinated with main results

---

## Summary

The redesigned Special Awards system provides:

1. **Flexibility**: Two modes (STAY_IN_MAIN, SEPARATE_POOL) cover all use cases
2. **Integration**: Deep integration with competition rounds, juries, and results
3. **Autonomy**: Awards can run independently or piggyback on main flow
4. **Transparency**: AI eligibility with admin override, full audit trail
5. **Engagement**: Audience voting support with anti-fraud measures
6. **Scalability**: Support for multiple awards, multiple winners, complex scoring

This architecture eliminates the Track dependency, integrates awards as standalone entities, and provides a robust, flexible system for recognizing excellence across multiple dimensions while maintaining the integrity of the main competition flow.
