export type ExhibitType = 'folder' | 'document' | 'note' | 'event' | 'party'
export type PartyKind = 'person' | 'organization'
export type OrganizationKind = 'business' | 'public_body' | 'association' | 'informal_group' | 'other'
export type SourceFileType = 'image' | 'pdf' | 'web_capture' | 'email' | 'article' | 'filing' | 'price_list' | 'text' | 'file'

export interface CanvasPlacement {
  x: number
  y: number
  width: number
  height: number
  rotation: number
  zIndex: number
  hidden: boolean
}

export interface ExhibitBase extends CanvasPlacement {
  id: string
  type: ExhibitType
  title: string
}

export interface DocumentRegion {
  id: string
  label: string
  excerpt: string
  date?: string
}

export interface FolderExhibit extends ExhibitBase {
  type: 'folder'
  content: string
  isOpen: boolean
}

export interface DocumentExhibit extends ExhibitBase {
  type: 'document'
  /** Author-mode reveal requirements. Omitted from play-mode payloads. */
  requiredFlags?: string[]
  publishedAt?: string
  capturedAt?: string
  sourceUri?: string
  body: string[]
  regions: DocumentRegion[]
  assetId?: string
  fileName?: string
  mimeType?: string
  fileSize?: number
  fileType: SourceFileType
  metadata: Record<string, string>
}

export interface DocumentUploadAnalysis {
  extractionStatus: 'succeeded' | 'unsupported' | 'failed'
  matchedFlags: string[]
  awardedFlags: string[]
}

export interface UploadedCaseDocument extends DocumentExhibit {
  /** Transient upload response data; it is not part of persisted exhibit state. */
  analysis: DocumentUploadAnalysis
}

export interface NoteExhibit extends ExhibitBase {
  type: 'note'
  content: string
}

export interface EventExhibit extends ExhibitBase {
  type: 'event'
  content: string
  eventDate?: string
}

export interface PartyExhibit extends ExhibitBase {
  type: 'party'
  content: string
  partyKind: PartyKind
  organizationKind?: OrganizationKind
  aliases: string[]
}

export type Exhibit = FolderExhibit | DocumentExhibit | NoteExhibit | EventExhibit | PartyExhibit
export type Evidence = Exclude<Exhibit, DocumentExhibit>
export type CaseDocument = DocumentExhibit
export type EvidenceType = Evidence['type']

interface ExhibitRelationBase {
  id: string
  fromExhibitId: string
  toExhibitId: string
  sortOrder: number
  note?: string
}

export interface FolderMembership extends ExhibitRelationBase { type: 'contains' }
export interface EventSupportRelation extends ExhibitRelationBase { type: 'supports' }
export interface PartyAssociationRelation extends ExhibitRelationBase { type: 'concerns' }
export interface ProvenanceRelation extends ExhibitRelationBase { type: 'source'; sourceRegionId?: string }
export type ExhibitRelation = FolderMembership | EventSupportRelation | PartyAssociationRelation | ProvenanceRelation

export interface Connection {
  id: string
  fromExhibitId: string
  toExhibitId: string
  label?: string
  /** Percentage from slack (0) to taut (100). */
  tightness?: number
  tagStyle?: 'luggage' | 'compact'
  /** Percentage along the thread from its source exhibit. */
  tagPosition?: number
  /** Signed perpendicular distance from the thread, constrained by tightness. */
  tagOffset?: number
}

export interface Viewport { x: number; y: number; zoom: number }
export interface TimelineRange { start: string; end: string }

export type BoardViewPlacement =
  | { mode: 'docked'; dockEdge: 'top' | 'right' | 'bottom' | 'left'; size: number }
  | { mode: 'canvas' | 'window'; x: number; y: number; width: number; height: number }

export interface TimelineView {
  id: string
  type: 'timeline'
  placement: BoardViewPlacement
  visible: boolean
  zIndex: number
  rangeMode: 'auto' | 'fixed'
  range?: TimelineRange
}

export type BoardView = TimelineView

export interface TemporalFact {
  id: string
  exhibitId: string
  kind: 'published' | 'captured' | 'occurred' | 'region_date'
  start: string
  end?: string
  label: string
}

export interface BriefConcept {
  id: string
  label: string
  context: string
  expectedPartyKind?: PartyKind
  resolvedPartyExhibitId?: string
}

export interface LevelBrief { body: string; concepts: BriefConcept[] }

export interface CaseState {
  id: string
  title: string
  subtitle: string
  exhibits: Exhibit[]
  relations: ExhibitRelation[]
  connections: Connection[]
  views: BoardView[]
  viewport: Viewport
  brief: LevelBrief
  revision: number
  updatedAt?: string
  levelStatus?: string
  sourceTemplateVersionId?: string
  editingAllowed?: boolean
  /** Visible documents that have not previously played their arrival flourish. */
  newlyVisibleDocumentIds?: string[]
}

export interface LevelFlag {
  key: string
  earnedAt?: string
  gatedDocumentCount: number
}

export interface EvidenceMatchAnchorDefinition {
  id: string
  phrase: string
  minimumSimilarity: number
  sortOrder: number
}

export interface EvidenceMatchRuleDefinition {
  id: string
  name: string
  flagKey: string
  matcherVersion: 'char_trigram_v1'
  minimumAnchorMatches: number
  enabled: boolean
  anchors: EvidenceMatchAnchorDefinition[]
}

export function isDocumentExhibit(exhibit: Exhibit): exhibit is DocumentExhibit { return exhibit.type === 'document' }
export function isEvidenceExhibit(exhibit: Exhibit): exhibit is Evidence { return exhibit.type !== 'document' }
export function isFolderExhibit(exhibit: Exhibit): exhibit is FolderExhibit { return exhibit.type === 'folder' }
export function isEventExhibit(exhibit: Exhibit): exhibit is EventExhibit { return exhibit.type === 'event' }
export function isPartyExhibit(exhibit: Exhibit): exhibit is PartyExhibit { return exhibit.type === 'party' }
