// Dimension47 - TTRPG Campaign Management Platform
// Prisma Schema

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

datasource db {
  provider = "postgresql"
}

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

enum UserRole {
  ADMIN
  GM
  PLAYER
}

enum CharacterType {
  PC
  NPC
}

enum AbilityType {
  STR
  DEX
  CON
  INT
  WIS
  CHA
}

enum Proficiency {
  UNTRAINED
  TRAINED
  EXPERT
  MASTER
  LEGENDARY
}

enum FeatSource {
  CLASS
  ANCESTRY
  GENERAL
  SKILL
  BONUS
  ARCHETYPE
}

enum SpellTradition {
  ARCANE
  DIVINE
  OCCULT
  PRIMAL
}

enum CombatantType {
  PC
  NPC
  MONSTER
}

enum ActionType {
  ACTION
  REACTION
  FREE
}

enum HighlightColor {
  YELLOW
  GREEN
  BLUE
  PINK
}

enum TranslationType {
  FEAT
  EQUIPMENT
  SPELL
  TRAIT
  ANCESTRY
  HERITAGE
  CLASS
  BACKGROUND
  CONDITION
  ACTION
}

enum TranslationQuality {
  HIGH
  MEDIUM
  LOW
}

enum ResearchField {
  BOMBER
  CHIRURGEON
  MUTAGENIST
  TOXICOLOGIST
}

// ==========================================
// USER & AUTH
// ==========================================

model User {
  id           String   @id @default(uuid())
  username     String   @unique
  email        String   @unique
  passwordHash String
  role         UserRole @default(PLAYER)
  avatarUrl    String?
  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt

  // Relations
  gmCampaigns       Campaign[]       @relation("CampaignGM")
  campaignMembers   CampaignMember[]
  characters        Character[]      @relation("CharacterOwner")
  uploadedDocuments Document[]
  documentAccess    DocumentAccess[]
  highlights        Highlight[]
  notes             Note[]
  noteShares        NoteShare[]
}

// ==========================================
// CAMPAIGNS
// ==========================================

model Campaign {
  id          String   @id @default(uuid())
  name        String
  description String?
  gmId        String
  imageUrl    String?
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  // Relations
  gm             User             @relation("CampaignGM", fields: [gmId], references: [id])
  members        CampaignMember[]
  characters     Character[]
  battleMaps     BattleMap[]
  combatants     Combatant[]
  battleSessions BattleSession[]
  documents      Document[]
  notes          Note[]
}

model CampaignMember {
  campaignId String
  userId     String
  joinedAt   DateTime @default(now())

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

  @@id([campaignId, userId])
}

// ==========================================
// CHARACTERS (Normalisiert)
// ==========================================

model Character {
  id         String        @id @default(uuid())
  campaignId String
  ownerId    String?
  name       String
  type       CharacterType @default(PC)
  level      Int           @default(1)
  avatarUrl  String?

  // Core Stats
  hpCurrent Int
  hpMax     Int
  hpTemp    Int @default(0)

  // Ancestry/Class/Background (References)
  ancestryId   String?
  heritageId   String?
  classId      String?
  backgroundId String?

  // Experience
  experiencePoints Int @default(0)

  // Currency (Ironvale uses Credits instead of Gold/Silver/Copper)
  credits Int @default(0)

  // Pathbuilder Import Data (JSON blob for original import)
  pathbuilderData Json?

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

  // Relations
  campaign       Campaign             @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  owner          User?                @relation("CharacterOwner", fields: [ownerId], references: [id])
  abilities      CharacterAbility[]
  feats          CharacterFeat[]
  skills         CharacterSkill[]
  spells         CharacterSpell[]
  items          CharacterItem[]
  conditions     CharacterCondition[]
  resources      CharacterResource[]
  battleTokens   BattleToken[]
  documentAccess DocumentAccess[]

  // Alchemy
  alchemyState  CharacterAlchemyState?
  formulas      CharacterFormula[]
  preparedItems CharacterPreparedItem[]

  // === Phase 1 additions ===
  freeArchetype    Boolean          @default(false) // D-08
  prereqViolations Json? // D-06
  levelUpSessions  LevelUpSession[]
  levelUpHistories LevelUpHistory[]
}

model CharacterAbility {
  id          String      @id @default(uuid())
  characterId String
  ability     AbilityType
  score       Int

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)

  @@unique([characterId, ability])
}

model CharacterFeat {
  id          String     @id @default(uuid())
  characterId String
  featId      String? // Reference to Feat table
  name        String // English name
  nameGerman  String? // German translation
  level       Int // Level obtained
  source      FeatSource

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
  feat      Feat?     @relation(fields: [featId], references: [id])
}

model CharacterSkill {
  id          String      @id @default(uuid())
  characterId String
  skillName   String
  proficiency Proficiency @default(UNTRAINED)

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)

  @@unique([characterId, skillName])
}

model CharacterSpell {
  id          String         @id @default(uuid())
  characterId String
  spellId     String?
  name        String
  nameGerman  String?
  tradition   SpellTradition
  spellLevel  Int
  prepared    Boolean        @default(false)

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
  spell     Spell?    @relation(fields: [spellId], references: [id])
}

model CharacterItem {
  id          String  @id @default(uuid())
  characterId String
  equipmentId String? // Reference to Equipment library
  name        String
  nameGerman  String?
  quantity    Int     @default(1)
  bulk        Decimal @default(0)
  equipped    Boolean @default(false)
  invested    Boolean @default(false)
  containerId String? // For containers
  notes       String?

  // Player-editable: Alias/Nickname for the item
  alias String?

  // GM-editable: Custom overrides for item properties
  customName       String? // Override display name
  customDamage     String? // Override damage dice (e.g. "2d6")
  customDamageType String? // Override damage type (e.g. "fire")
  customTraits     String[] // Override/add traits
  customRange      String? // Override range
  customHands      String? // Override hands requirement

  character Character  @relation(fields: [characterId], references: [id], onDelete: Cascade)
  equipment Equipment? @relation(fields: [equipmentId], references: [id])
}

model CharacterCondition {
  id          String  @id @default(uuid())
  characterId String
  name        String
  nameGerman  String?
  value       Int? // For valued conditions like Frightened 2
  duration    String?
  source      String?

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
}

model CharacterResource {
  id          String @id @default(uuid())
  characterId String
  name        String
  current     Int
  max         Int

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)

  @@unique([characterId, name])
}

// ==========================================
// ALCHEMY SYSTEM
// ==========================================

model CharacterAlchemyState {
  id                    String         @id @default(uuid())
  characterId           String         @unique
  researchField         ResearchField?
  versatileVialsCurrent Int            @default(0)
  versatileVialsMax     Int            @default(0)
  advancedAlchemyBatch  Int            @default(0)
  advancedAlchemyMax    Int            @default(0)
  lastRestAt            DateTime?

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
}

model CharacterFormula {
  id            String  @id @default(uuid())
  characterId   String
  equipmentId   String
  name          String
  nameGerman    String?
  learnedAt     Int     @default(1)
  formulaSource String? // "Pathbuilder Import", "Purchased", "Found"

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
  equipment Equipment @relation(fields: [equipmentId], references: [id])

  @@unique([characterId, equipmentId])
  @@index([characterId])
}

model CharacterPreparedItem {
  id             String   @id @default(uuid())
  characterId    String
  equipmentId    String
  name           String
  nameGerman     String?
  quantity       Int      @default(1)
  isQuickAlchemy Boolean  @default(false)
  isInfused      Boolean  @default(true) // Infused items expire on rest, permanent items don't
  createdAt      DateTime @default(now())

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
  equipment Equipment @relation(fields: [equipmentId], references: [id])

  @@index([characterId])
}

// ==========================================
// BATTLE SYSTEM
// ==========================================

model BattleMap {
  id          String   @id @default(uuid())
  campaignId  String
  name        String
  imageUrl    String
  gridSizeX   Int      @default(20)
  gridSizeY   Int      @default(20)
  gridOffsetX Int      @default(0)
  gridOffsetY Int      @default(0)
  createdAt   DateTime @default(now())

  campaign       Campaign        @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  battleSessions BattleSession[]
}

model Combatant {
  id         String        @id @default(uuid())
  campaignId String
  name       String
  type       CombatantType
  level      Int
  hpMax      Int
  ac         Int

  // Saves
  fortitude  Int
  reflex     Int
  will       Int
  perception Int

  // Speed
  speed Int @default(25)

  avatarUrl   String?
  description String?
  createdAt   DateTime @default(now())

  campaign     Campaign           @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  abilities    CombatantAbility[]
  battleTokens BattleToken[]
}

model CombatantAbility {
  id          String     @id @default(uuid())
  combatantId String
  name        String
  actionCost  Int // 1, 2, 3, or 0 for free/reaction
  actionType  ActionType
  description String
  damage      String? // e.g. "2d6+4 slashing"
  traits      String[]

  combatant Combatant @relation(fields: [combatantId], references: [id], onDelete: Cascade)
}

model BattleSession {
  id          String   @id @default(uuid())
  campaignId  String
  mapId       String?
  name        String?
  isActive    Boolean  @default(false)
  roundNumber Int      @default(0)
  createdAt   DateTime @default(now())

  campaign Campaign      @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  map      BattleMap?    @relation(fields: [mapId], references: [id])
  tokens   BattleToken[]
}

model BattleToken {
  id              String   @id @default(uuid())
  battleSessionId String
  combatantId     String?
  characterId     String?
  name            String
  positionX       Float
  positionY       Float
  hpCurrent       Int
  hpMax           Int
  initiative      Int?
  conditions      String[]
  size            Int      @default(1) // 1 = Medium, 2 = Large, etc.

  battleSession BattleSession @relation(fields: [battleSessionId], references: [id], onDelete: Cascade)
  combatant     Combatant?    @relation(fields: [combatantId], references: [id])
  character     Character?    @relation(fields: [characterId], references: [id])
}

// ==========================================
// DOCUMENTS SYSTEM
// ==========================================

model Document {
  id          String   @id @default(uuid())
  campaignId  String
  title       String
  description String?
  category    String?
  tags        String[]
  filePath    String
  fileType    String // html, pdf
  uploadedBy  String
  createdAt   DateTime @default(now())

  campaign   Campaign         @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  uploader   User             @relation(fields: [uploadedBy], references: [id])
  access     DocumentAccess[]
  highlights Highlight[]
}

model DocumentAccess {
  id          String  @id @default(uuid())
  documentId  String
  userId      String? // NULL = all campaign members
  characterId String? // Access per character

  document  Document   @relation(fields: [documentId], references: [id], onDelete: Cascade)
  user      User?      @relation(fields: [userId], references: [id])
  character Character? @relation(fields: [characterId], references: [id])

  @@unique([documentId, userId, characterId])
}

model Highlight {
  id            String         @id @default(uuid())
  documentId    String
  userId        String
  selectionText String
  startOffset   Int
  endOffset     Int
  color         HighlightColor
  note          String?
  createdAt     DateTime       @default(now())

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

model Note {
  id         String   @id @default(uuid())
  userId     String
  campaignId String
  title      String
  content    String // Markdown
  isShared   Boolean  @default(false)
  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  user     User        @relation(fields: [userId], references: [id])
  campaign Campaign    @relation(fields: [campaignId], references: [id], onDelete: Cascade)
  shares   NoteShare[]
}

model NoteShare {
  noteId String
  userId String

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

  @@id([noteId, userId])
}

// ==========================================
// REFERENCE DATA (Pathfinder 2e)
// ==========================================

model Feat {
  id          String   @id @default(uuid())
  name        String   @unique
  traits      String[]
  summary     String?
  description String? // Full feat description/benefit text
  actions     String? // "1", "2", "3", "free", "reaction", null for passive
  url         String?
  level       Int? // Minimum level requirement
  sourceBook  String?

  // Feat classification
  featType String? // "General", "Skill", "Class", "Ancestry", "Archetype", "Heritage"
  rarity   String? // "Common", "Uncommon", "Rare", "Unique"

  // Prerequisites
  prerequisites String? // Text description of prerequisites

  // For class/archetype feats
  className     String? // "Fighter", "Wizard", etc.
  archetypeName String? // "Sentinel", "Medic", etc.

  // For ancestry feats
  ancestryName String? // "Human", "Elf", etc.

  // For skill feats
  skillName String? // "Acrobatics", "Athletics", etc.

  // Cached German translation
  nameGerman    String?
  summaryGerman String?

  characterFeats CharacterFeat[]

  @@index([featType])
  @@index([className])
  @@index([ancestryName])
  @@index([level])
}

model Equipment {
  id              String   @id @default(uuid())
  name            String   @unique
  traits          String[]
  itemCategory    String // "Weapons", "Armor", "Consumables", "Shields", etc.
  itemSubcategory String?
  bulk            String? // "L" for light, "1", "2", etc.
  url             String?
  summary         String?
  level           Int?
  price           Int? // In CP

  // Weapon-specific fields
  hands          String?
  damage         String? // "1d8 S", "1d6 P", etc.
  damageType     String? // "S", "P", "B" (Slashing, Piercing, Bludgeoning)
  range          String?
  reload         String?
  weaponCategory String? // "Simple", "Martial", "Advanced", "Ammunition"
  weaponGroup    String? // "Sword", "Axe", "Bow", etc.

  // Armor-specific fields
  ac            Int?
  dexCap        Int?
  checkPenalty  Int?
  speedPenalty  Int?
  strength      Int? // Strength requirement
  armorCategory String? // "Unarmored", "Light", "Medium", "Heavy"
  armorGroup    String? // "Leather", "Chain", "Plate", etc.

  // Shield-specific fields
  shieldHp       Int?
  shieldHardness Int?
  shieldBt       Int? // Broken Threshold

  // Consumable/Equipment-specific fields
  activation String? // "Cast A Spell", "[one-action]", etc.
  duration   String?
  usage      String?
  effect     String? // Specific effect text for item variants (Lesser, Moderate, Greater, Major)

  characterItems CharacterItem[]
  formulas       CharacterFormula[]
  preparedItems  CharacterPreparedItem[]
}

model Spell {
  id          String   @id @default(uuid())
  name        String   @unique
  level       Int
  actions     String?
  traditions  String[]
  traits      String[]
  range       String?
  targets     String?
  duration    String?
  description String?
  url         String?

  characterSpells CharacterSpell[]
}

model Trait {
  id          String  @id @default(uuid())
  name        String  @unique
  description String?
  url         String?
}

// ==========================================
// TRANSLATION CACHE
// ==========================================

model Translation {
  id                String             @id @default(uuid())
  type              TranslationType
  englishName       String
  germanName        String
  germanSummary     String?
  germanDescription String?
  germanEffect      String? // Translated effect text for alchemical items
  quality           TranslationQuality @default(MEDIUM)
  translatedBy      String             @default("claude-api")
  createdAt         DateTime           @default(now())
  updatedAt         DateTime           @updatedAt

  @@unique([type, englishName])
  @@index([type])
  @@index([englishName])
}

// ==========================================
// LEVEL-UP SYSTEM (Phase 1)
// ==========================================

model LevelUpSession {
  id          String    @id @default(uuid())
  characterId String
  targetLevel Int
  state       Json      @default("{}")
  committedAt DateTime?
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)
  // Partial unique index "one open DRAFT per character" added in raw migration SQL —
  // Prisma 7 cannot emit partial indexes from schema (per RESEARCH.md §Don't Hand-Roll).

  @@index([characterId])
}

model LevelUpHistory {
  id             String   @id @default(uuid())
  characterId    String
  levelFrom      Int
  levelTo        Int
  snapshotBefore Json
  choices        Json
  committedAt    DateTime @default(now())

  character Character @relation(fields: [characterId], references: [id], onDelete: Cascade)

  @@index([characterId, committedAt(sort: Desc)])
}

model ClassProgression {
  id                  String   @id @default(uuid())
  className           String
  level               Int
  grants              String[]
  proficiencyChanges  Json
  spellSlotIncrement  Json?
  cantripIncrement    Int?
  repertoireIncrement Int?
  choiceType          String?
  choiceOptionsRef    String?

  @@unique([className, level])
  @@index([className])
}

model ClassFeatureOption {
  id                 String   @id @default(uuid())
  optionsRef         String
  optionKey          String
  name               String
  nameGerman         String?
  description        String
  grants             String[]
  proficiencyChanges Json?

  @@unique([optionsRef, optionKey])
  @@index([optionsRef])
}
