export type ExhibitType = 'folder' | 'document' | 'note' | 'event' | 'party' | 'claim'
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 type DocumentCaptureKind = 'unclassified' | 'photo' | 'scene' | 'clipping' | 'full_page'

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'
  /** Stable, board-local number used when citing this exhibit. */
  displayNumber?: number
  /** Author-mode reveal requirements. Omitted from play-mode payloads. */
  requiredFlags?: string[]
  publishedAt?: string
  capturedAt?: string
  sourceUri?: string
  sourceCitation?: string
  body: string[]
  regions: DocumentRegion[]
  assetId?: string
  fileName?: string
  mimeType?: string
  fileSize?: number
  fileType: SourceFileType
  /** Evidentiary form on the board; independent of MIME/file type. */
  captureKind: DocumentCaptureKind
  metadata: Record<string, string>
}

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

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

export type NotePresentation = 'luggage' | 'lined_sheet'

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

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 interface ClaimExhibit extends ExhibitBase {
  type: 'claim'
  statement: string
}

export type Exhibit = FolderExhibit | DocumentExhibit | NoteExhibit | EventExhibit | PartyExhibit | ClaimExhibit
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 BriefChecklistItem { id: string; text: string }

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

export interface LevelGoal {
  /** Present in author mode so the goal can be edited; omitted in play mode. */
  id?: string
  key: string
  title: string
  instructions: string
  completionMessage: string
  enabled?: boolean
  /** Author-only success condition. Expected answer flags stay out of play payloads. */
  requiredFlags?: string[]
  status: 'pending' | 'complete'
  completedAt?: string
  /** True only in the response to the mutation that completed this goal. */
  newlyCompleted: boolean
}

export type CaseReportSubmissionStatus = 'evidence_insufficient' | 'evidence_accepted_report_incomplete' | 'accepted'
export type EvidenceVerificationStatus = 'accepted' | 'not_evaluated' | 'ocr_unavailable' | 'text_not_matched' | 'semantic_pending' | 'semantic_failed' | 'semantic_rejected'

export interface EvidenceVerification {
  status: EvidenceVerificationStatus
  detail: string
  score?: number
  matchedMarkers?: number
  requiredMarkers?: number
}

export interface CaseReportEvidence {
  connectionId: string
  documentExhibitId: string
  displayNumber: number
  documentTitle: string
  fileType: SourceFileType
  relationText: string
  publishedAt?: string
  sourceCitation?: string
  sourceUri?: string
  evidenceAccepted: boolean
  verification: EvidenceVerification
}

export interface CaseReportClaim {
  claimExhibitId: string
  statement: string
  evidence: CaseReportEvidence[]
}

export interface CaseReport {
  title: string
  investigatorName: string
  requiredForCompletion: boolean
  status: 'draft' | CaseReportSubmissionStatus
  feedback?: string
  issues: string[]
  claims: CaseReportClaim[]
}

export interface CaseReportSubmissionInput {
  investigatorName: string
}

export interface CaseState {
  id: string
  title: string
  subtitle: string
  exhibits: Exhibit[]
  relations: ExhibitRelation[]
  connections: Connection[]
  views: BoardView[]
  viewport: Viewport
  brief: LevelBrief
  goals: LevelGoal[]
  report?: CaseReport
  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
  sourceLabel?: string
  sourceUri?: string
  flagKey: string
  matcherVersion: 'char_trigram_v1'
  minimumAnchorMatches: number
  enabled: boolean
  anchors: EvidenceMatchAnchorDefinition[]
}

export interface EvidenceSemanticRuleDefinition {
  id: string
  goalId: string
  goalKey: string
  name: string
  targetSubject: string
  relatedSubject?: string
  assertion: string
  successFlagKey: string
  relatedFlagKey?: string
  minimumConfidence: number
  evaluatorVersion: string
  enabled: boolean
}

export interface DocumentSemanticAnalysis {
  status: 'not_needed' | 'unavailable' | 'pending' | 'succeeded' | 'failed'
  subject?: 'target' | 'related' | 'ambiguous' | 'neither'
  supportsClaim?: boolean
  evidenceExcerpt?: string
  confidence?: number
  retryable: boolean
  awardedFlags: string[]
  goals: LevelGoal[]
}

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' }
export function isClaimExhibit(exhibit: Exhibit): exhibit is ClaimExhibit { return exhibit.type === 'claim' }
