import type { CommandDefinition, WorkbenchId } from '../freecadManifest'
import type { DependencyEdge, RecomputeSnapshot, RecomputePlan } from './dependencyGraph'
import type { Quantity, QuantityDimension } from './units'
import type { SketchConstraint, SketchExternalGeometry, SketchGeometry, SketchSnapshot, SketchSolveResult } from './sketcher'
import type { RecomputeExecutionOptions, RecomputeExecutionResult } from './recomputeEngine'
import type { FcstdArchiveLimits, FcstdBoolListEdit, FcstdColorEdit, FcstdColorListEdit, FcstdDirectionEdit, FcstdFileEdit, FcstdFontEdit, FcstdIncludedFileEdit, FcstdIncludedFileResourcePayload, FcstdInspection, FcstdInstantiatedShape, FcstdIntegerSetEdit, FcstdMeasureAreaEdit, FcstdNumericPropertyEdit, FcstdPathEdit, FcstdShapeResourcePayload, FcstdStoredIncludedFileResource, FcstdStoredShapeResource, FcstdWriteOptions } from './fcstd'
import type { NativeOcctHistoryCapabilities, NativeOcctHistoryProvider } from './nativeHistoryProtocol'
import type { FacadeRuntimeProfile } from './runtimeProfile'
import type { CamApi } from './cam'
import type { NativeStageNamingEvidence } from './nativeNamingEvidence'

export type ModelTreeItem = {
  id: string
  label: string
  type: 'document' | 'folder' | 'body' | 'sketch' | 'feature'
  state?: 'active' | 'valid' | 'dirty' | 'warning' | 'readonly'
  detail?: string
  children?: string[]
}

export type TopoRefValue = {
  schemaVersion: 1
  objectId: string
  kind: 'face' | 'edge' | 'vertex'
  persistentId: string
  topologyVersion: number
  generation: number
  status: 'stable' | 'ambiguous' | 'new' | 'deleted'
  signature?: string
  candidates?: string[]
}

/** One FreeCAD PropertyLinkSub can reference several sub-elements on one object. */
export type LinkSubValue = {
  schemaVersion: 1
  objectId: string
  subElements: Array<string | TopoRefValue>
}

export type LinkSubListValue = {
  schemaVersion: 1
  entries: Array<{
    objectId: string
    subElement: string | TopoRefValue | null
  }>
}

export type VectorValue = { x: number; y: number; z: number }

/** Canonical App::PropertyColor value returned by FreeCAD's Python API. */
export type ColorValue = [number, number, number, number]

/** Canonical ordered App::PropertyColorList value. */
export type ColorListValue = ColorValue[]

/** Portable App::PropertyFileIncluded identity; authoritative bytes live in the project resource store. */
export type IncludedFileValue = {
  schemaVersion: 1
  archiveName: string
  resourceHash: string
  byteLength: number
  mediaType: string
}

export type PlacementValue = {
  position: VectorValue
  rotation: { axis: VectorValue; angle: number }
}

export type AttachmentSupportValue = {
  objectId: string
  subElement?: string | TopoRefValue | null
}

export type MultiTransformStep =
  | { id: string; type: 'linear'; occurrences: number; length: number; direction: 'Horizontal' | 'Vertical' | 'Normal' }
  | { id: string; type: 'polar'; occurrences: number; angle: number; axis: 'Horizontal' | 'Vertical' | 'Normal' }
  | { id: string; type: 'mirrored'; plane: 'XY plane' | 'XZ plane' | 'YZ plane' }

export type MultiTransformValue = { steps: MultiTransformStep[] }

/** Structured representation of FreeCAD Path::PropertyPath command payloads. */
export type PathCommandValue = {
  name: string
  parameters: Record<string, number>
}

export type PathPropertyValue = {
  schemaVersion: 1
  commands: PathCommandValue[]
  resourcePath?: string
  version?: number
  center?: VectorValue
}

export type ShapeResourceValue = {
  path: string
  format: 'brep'
  hasherIndex?: number
  elementMap?: string
  elementMapEntries?: Array<{ key: string; value: string }>
  elementMapResource?: string
}

export type PropertyValue = string | number | boolean | string[] | number[] | boolean[] | ColorValue | ColorListValue | IncludedFileValue | VectorValue | PlacementValue | AttachmentSupportValue | MultiTransformValue | PathPropertyValue | ShapeResourceValue | TopoRefValue | LinkSubValue | LinkSubListValue | null

export type NativeEditablePropertyType =
  | 'App::PropertyString' | 'App::PropertyLength' | 'App::PropertyDistance' | 'App::PropertyAngle' | 'App::PropertyAcceleration' | 'App::PropertyArea' | 'App::PropertyForce' | 'App::PropertyHeatFlux'
  | 'App::PropertyQuantityConstraint' | 'App::PropertyBool' | 'App::PropertyBoolList' | 'App::PropertyEnumeration'
  | 'App::PropertyLink' | 'App::PropertyLinkHidden' | 'App::PropertyLinkSub' | 'App::PropertyLinkSubList'
  | 'App::PropertyLinkList' | 'App::PropertyStringList' | 'App::PropertyFloatList'
  | 'App::PropertyIntegerList' | 'App::PropertyIntegerSet' | 'App::PropertyVector' | 'App::PropertyDirection' | 'App::PropertyFile' | 'App::PropertyFileIncluded' | 'App::PropertyFont' | 'App::PropertyPlacement'
  | 'App::PropertyMultiTransform' | 'Path::PropertyPath' | 'Part::PropertyPartShape'
  | 'App::PropertyColor' | 'App::PropertyColorList' | 'App::PropertyPercent' | 'App::PropertyFloat'
  | 'App::PropertyFloatConstraint' | 'App::PropertyPrecision' | 'App::PropertyInteger'
  | 'App::PropertyIntegerConstraint'

export type ObjectPropertySnapshot = {
  name: string
  label: string
  group: string
  scope: 'data' | 'view'
  type: NativeEditablePropertyType
  value: PropertyValue
  unit?: string
  /** Raw App::Property::Status bitmask from the locked FreeCAD runtime. */
  nativeStatus?: number
  readOnly?: boolean
  hidden?: boolean
  recompute?: boolean
  options?: string[]
  expression?: string
  expressionError?: string
}

export type DocumentObjectSnapshot = {
  id: string
  typeId: string
  /** Stable positive App::DocumentObject tag persisted by FreeCAD's Document.xml. */
  nativeObjectTag?: number
  properties: ObjectPropertySnapshot[]
  sketch?: SketchSnapshot
  topology?: ObjectTopologySnapshot
}

export type DocumentSnapshot = {
  id: string
  label: string
  version: number
  dirty: boolean
  readOnly: boolean
  /** Native PartialDoc state. Partial documents can be inspected but cannot be saved or recomputed. */
  partial?: boolean
  units: string
  tree: ModelTreeItem[]
  objects: DocumentObjectSnapshot[]
  dependencies?: DependencyEdge[]
  recompute?: RecomputeSnapshot
}

export type SetExpressionInput = {
  objectId: string
  propertyName: string
  expression: string
}

export type RecomputeResult = RecomputePlan & {
  generation: number
  status: RecomputeSnapshot['status']
  errors: Array<{ objectId: string; code: string; message: string }>
}

export type SetPropertyInput = {
  objectId: string
  propertyName: string
  value: PropertyValue
  /** Reject the mutation before emitting events when the caller observed an older document. */
  expectedDocumentVersion?: number
  /** Property writes are synchronous; a pre-aborted signal cancels before the transaction opens. */
  signal?: AbortSignal
}

export type SetIncludedFileInput = {
  objectId: string
  propertyName: string
  fileName: string
  bytes: Uint8Array
  mediaType?: string
  expectedDocumentVersion?: number
  signal?: AbortSignal
}

export type AddDynamicPropertyInput = {
  objectId: string
  property: ObjectPropertySnapshot
}

export type RemoveDynamicPropertyInput = {
  objectId: string
  propertyName: string
}

export type RenameDynamicPropertyInput = RemoveDynamicPropertyInput & {
  newPropertyName: string
}

export type ReorderBodyFeatureInput = {
  bodyId: string
  objectId: string
  beforeObjectId?: string | null
}

export type RemoveObjectInput = {
  objectId: string
  cascade?: boolean
}

export type ResolveTopologyReferenceInput = {
  ownerObjectId: string
  referenceName: string
  candidatePersistentId: string
  currentPersistentId?: string
}

export type PersistenceCapabilities = {
  mode: 'sqlite-opfs' | 'sqlite-memory' | 'unavailable'
  sqliteWasm: boolean
  opfs: boolean
  schemaVersion: number
  crossTabWriteLock?: 'web-locks' | 'local-queue'
  reason?: string
}

export type ProjectChangeNotice = {
  documentId: string
  documentVersion: number
  senderId: string
  kind: 'document-saved'
}

export type ProjectSaveResult = {
  documentId: string
  documentVersion: number
  persistedAt: number
  mode: PersistenceCapabilities['mode']
}

export type ProjectSummary = {
  documentId: string
  label: string
  documentVersion: number
  objectCount: number
  dirty: boolean
  readOnly: boolean
  updatedAt: number
}

export type ProjectRecoveryReport = {
  documentId: string
  mode: PersistenceCapabilities['mode']
  schemaVersion: number
  integrity: 'ok' | 'failed' | 'unavailable'
  lastSavedVersion: number | null
  dirtyAtLastSave: boolean | null
  checkpoints: Array<{ version: number; createdAt: number }>
  warnings: string[]
}

export type ProjectResource = {
  hash: string
  byteLength: number
  mediaType: string
  refCount: number
}

export type ProjectResourceSweepReport = {
  inspectedRecords: number
  inspectedFiles: number
  deletedRecords: number
  deletedFiles: number
  reclaimedBytes: number
  missingFiles: string[]
  warnings: string[]
}

export type GeometryCapabilities = {
  provider: 'Bitbybit OCCT'
  version: '1.1.1'
  status: 'idle' | 'initializing' | 'ready' | 'failed' | 'unavailable'
  worker: boolean
  wasm: boolean
  shapeCount: number
  kernelReferenceCount: number
  releasedShapeCount: number
  peakShapeCount: number
  peakKernelReferenceCount: number
  reason?: string
}

export type ShapeHandle = {
  readonly id: string
  readonly kernel: 'bitbybit-occt'
  readonly kind: 'solid'
  readonly documentId: string
  readonly documentVersion: number
}

export type SubshapeRef = {
  shapeId: string
  kind: 'face' | 'edge' | 'vertex'
  persistentId: string
  topologyVersion: number
  status?: 'stable' | 'ambiguous' | 'new' | 'deleted'
  signature?: string
  candidates?: string[]
}

export type ElementMapEntry = {
  name: string
  objectId: string
  kind: SubshapeRef['kind']
  persistentId: string
  status: 'stable' | 'ambiguous' | 'new' | 'deleted'
  candidates?: Array<{ objectId: string; persistentId: string; name?: string; sourceStageId?: string }>
}

export type ElementMapSnapshot = {
  schemaVersion: 1
  entries: ElementMapEntry[]
  /** Native naming evidence is retained when a provider supplies it. */
  nativeNamingEvidence?: NativeStageNamingEvidence[]
}

export type SubshapeSelection = {
  objectId: string
  ref: SubshapeRef
}

export type SubshapeSignature = {
  kind: SubshapeRef['kind']
  canonical: string
  hash: string
  centroid: [number, number, number]
  bounds: { min: [number, number, number]; max: [number, number, number] }
  area: number
  normal: [number, number, number]
  analytic?: {
    type: string
    tolerance: number
    parameterRange?: [number, number]
    adjacencyDegree: number
    incidentCount: number
    curvature?: { kind: string; values: number[] }
  }
}

export type TopologySnapshotEntry = { ref: SubshapeRef; signature: SubshapeSignature }

export type TopologyAdjacency = {
  faceNeighbors: Record<string, string[]>
  faceEdges: Record<string, string[]>
  edgeFaces: Record<string, string[]>
  edgeVertices: Record<string, string[]>
  vertexEdges: Record<string, string[]>
}

export type TopologyMigrationMatch = {
  current: SubshapeRef
  previousId?: string
  score: number
  status: 'stable' | 'ambiguous' | 'new' | 'deleted'
}

export type TopologyHistoryRelation = {
  relation: 'preserved' | 'modified' | 'generated' | 'deleted' | 'ambiguous'
  sourceObjectId?: string
  sourcePersistentId?: string
  resultPersistentId?: string
  /** Result candidates retained when OCCT cannot disambiguate isomorphic subshapes. */
  resultCandidates?: string[]
  candidates?: Array<{ sourceObjectId: string; persistentId: string }>
  sourceStageId?: string
  resultStageId?: string
  score: number
}

export type TopologyHistoryResult = {
  operationId: string
  provider: 'occt-native' | 'signature-fallback'
  relations: TopologyHistoryRelation[]
  counts: Record<TopologyHistoryRelation['relation'], number>
  stages?: Array<{
    stageId: string
    operationId: string
    inputObjectIds: string[]
    resultObjectId?: string
    ordinal: number
  }>
  /** Stage-level MappedNameRef/StringHasher evidence, when available. */
  namingEvidence?: NativeStageNamingEvidence[]
}

export type NativeTopologyHistoryRecord = {
  sourceObjectId: string
  sourceKind: SubshapeRef['kind']
  sourceIndex: number
  relation: 'preserved' | 'modified' | 'generated' | 'deleted'
  resultKind?: SubshapeRef['kind']
  resultIndexes?: number[]
  sourceStageId?: string
  resultStageId?: string
}

export type NativeTopologyHistoryStageCaptureResult = {
  stageId: string
  operation: NonNullable<NativeTopologyHistoryInput['operation']>
  inputObjectIds: string[]
  resultObjectId: string
  ordinal: number
  topology: SubshapeTopology
  records: NativeTopologyHistoryRecord[]
  namingEvidence?: NativeStageNamingEvidence
}

export type NativeTopologyHistoryRecords = NativeTopologyHistoryRecord[] & {
  /** Non-enumerable runtime evidence for builders that execute several native stages. */
  stageCaptures?: NativeTopologyHistoryStageCaptureResult[]
}

export type NativeTopologyHistoryStage = {
  stageId: string
  operation?: NativeTopologyHistoryInput['operation']
  inputObjectIds: string[]
  resultObjectId?: string
  ordinal: number
  namingEvidence?: NativeStageNamingEvidence
}

export type NativeMultiTransformHistoryStep =
  | { type: 'linear'; direction: [number, number, number] }
  | { type: 'polar'; axisOrigin: [number, number, number]; direction: [number, number, number]; angle: number }
  | { type: 'mirrored'; axisOrigin: [number, number, number]; direction: [number, number, number] }

export type NativeFeatureHistorySide = {
  direction: [number, number, number]
  /** Required by Revolution/Groove; omitted by Pad/Pocket. */
  angle?: number
}

export type NativeTopologyHistoryInput = GeometryDocumentContext & {
  operationId: string
  operation?: 'fuse' | 'cut' | 'common' | 'rotate' | 'pad' | 'pocket' | 'loft' | 'pipe' | 'revolution' | 'groove' | 'fillet' | 'chamfer' | 'hole' | 'draft' | 'thickness' | 'linear-pattern' | 'polar-pattern' | 'mirrored' | 'multi-transform'
  transformKind?: 'linear' | 'polar' | 'mirrored'
  transforms?: NativeMultiTransformHistoryStep[]
  /** Ordered positive/reverse builders for two-sided and midplane features. */
  featureSides?: NativeFeatureHistorySide[]
  direction?: [number, number, number]
  axisOrigin?: [number, number, number]
  angle?: number
  radius?: number
  distance?: number
  depth?: number
  position?: [number, number, number]
  neutralPlaneDirection?: [number, number, number]
  faceIndexes?: number[]
  reversed?: boolean
  ruled?: boolean
  offset?: number
  joinType?: 'Arc' | 'Intersection'
  inputs: Array<{ objectId: string; shape: ShapeHandle; inputId?: string; role?: string; stageId?: string; objectTag?: number; namingEvidence?: NativeStageNamingEvidence }>
  stages?: NativeTopologyHistoryStage[]
  /** Stable FreeCAD owner tag used when encoding the result ElementMap. */
  resultObjectTag?: number
  result: ShapeHandle
}

export type ObjectTopologySnapshot = {
  shapeId: string
  documentVersion: number
  generation: number
  entries: TopologySnapshotEntry[]
  adjacency?: TopologyAdjacency
  migration: { previousGeneration: number | null; matches: TopologyMigrationMatch[] }
  history: TopologyHistoryResult
  elementMap?: ElementMapSnapshot
}

export type SubshapeTopology = {
  faces: SubshapeRef[]
  edges: SubshapeRef[]
  vertices: SubshapeRef[]
  entries: TopologySnapshotEntry[]
  adjacency?: TopologyAdjacency
}

export type ShapeMassProperties = {
  volume: number
  surfaceArea: number
  centerOfMass: [number, number, number]
}

export type ShapeQualityReport = {
  shapeType: 'vertex' | 'edge' | 'wire' | 'face' | 'shell' | 'solid' | 'compSolid' | 'compound' | 'shape' | 'unknown'
  isNull: boolean
  structuralValid: boolean
  structuralErrors: number
  structuralWarnings: number
  structuralIssues?: Array<{ severity: 'error' | 'warning'; node: { kind: string; index: number }; description: string }>
  nativeKernelValid?: boolean
  solids: number
  faces: number
  edges: number
  vertices: number
  boundingBox: {
    min: [number, number, number]
    max: [number, number, number]
  }
}

export type MeshAsset = {
  shapeId: string
  topologyVersion: number
  positions: Float32Array
  normals: Float32Array
  indices: Uint32Array
  subshapes?: SubshapeRef[]
  /** Triangle ranges retain the face-to-mesh mapping needed for 3D TopoRef picking. */
  subshapeRanges?: Array<{
    startTriangle: number
    triangleCount: number
    ref: SubshapeRef
  }>
  /** OCCT edge polylines and topology vertices, with a tessellation fallback when analytic metadata is unavailable. */
  subshapeEdges?: Array<{
    start: Point3
    end: Point3
    ref: SubshapeRef
  }>
  subshapeVertices?: Array<{
    position: Point3
    ref: SubshapeRef
  }>
  bounds: {
    min: [number, number, number]
    max: [number, number, number]
  }
}

export type ViewportMeshAsset = {
  objectId: string
  mesh: MeshAsset
}

export type GeometryFileExport = {
  format: 'step' | 'stl' | 'iges' | 'brep'
  fileName: string
  mediaType: 'application/step' | 'model/stl' | 'model/iges' | 'application/x-freecad-brep'
  text: string
}

export type GeometryFileImport = GeometryDocumentContext & {
  format: 'step' | 'iges' | 'brep'
  text: string
}

export type GeometryDocumentContext = {
  documentId: string
  documentVersion: number
}

export type CreateBoxInput = GeometryDocumentContext & {
  width: number
  length: number
  height: number
  center?: [number, number, number]
  originOnCenter?: boolean
}

export type CreateCylinderInput = GeometryDocumentContext & {
  radius: number
  height: number
  center?: [number, number, number]
  direction?: [number, number, number]
  angle?: number
  originOnCenter?: boolean
}

export type CreateSphereInput = GeometryDocumentContext & {
  radius: number
  center?: [number, number, number]
}

export type CreateConeInput = GeometryDocumentContext & {
  radius1: number
  radius2: number
  height: number
  center?: [number, number, number]
  direction?: [number, number, number]
  angle?: number
}

export type CreateTorusInput = GeometryDocumentContext & {
  majorRadius: number
  minorRadius: number
  center?: [number, number, number]
  direction?: [number, number, number]
  angle?: number
}

export type CreateHelixInput = GeometryDocumentContext & {
  pitch: number
  height: number
  radius: number
  angle?: number
  leftHanded?: boolean
  center?: [number, number, number]
  direction?: [number, number, number]
  tolerance?: number
}

export type ModeledThreadInput = GeometryDocumentContext & {
  base: ShapeHandle
  minorDiameter: number
  majorDiameter: number
  pitch: number
  depth: number
  center?: Point3
  direction?: Point3
  leftHanded?: boolean
}

export type CreatePrismInput = GeometryDocumentContext & {
  polygon: number
  circumradius: number
  height: number
  firstAngle?: number
  secondAngle?: number
  center?: [number, number, number]
}

export type CreateWedgeInput = GeometryDocumentContext & {
  xmin: number
  ymin: number
  zmin: number
  z2min: number
  x2min: number
  xmax: number
  ymax: number
  zmax: number
  z2max: number
  x2max: number
  center?: [number, number, number]
}

export type CreateEllipsoidInput = GeometryDocumentContext & {
  radius1: number
  radius2: number
  radius3?: number
  angle1?: number
  angle2?: number
  angle3?: number
  center?: [number, number, number]
}

export type Placement = {
  translation: [number, number, number]
  rotationAxis: [number, number, number]
  rotationAngle: number
}

export type ApplyPlacementInput = GeometryDocumentContext & {
  shape: ShapeHandle
  placement: Placement
}

export type MirrorInput = GeometryDocumentContext & {
  shape: ShapeHandle
  origin: Point3
  normal: Point3
}

export type BooleanUnionInput = GeometryDocumentContext & {
  shapes: ShapeHandle[]
  keepEdges?: boolean
}

export type BooleanCutInput = GeometryDocumentContext & {
  base: ShapeHandle
  tools: ShapeHandle[]
  keepEdges?: boolean
}

export type BooleanIntersectionInput = GeometryDocumentContext & {
  shapes: ShapeHandle[]
  keepEdges?: boolean
}

export type FilletInput = GeometryDocumentContext & {
  base: ShapeHandle
  radius: number
  indexes?: number[]
}

export type ChamferInput = GeometryDocumentContext & {
  base: ShapeHandle
  distance: number
  indexes?: number[]
}

export type DraftInput = GeometryDocumentContext & {
  base: ShapeHandle
  angle: number
  direction?: Point3
  neutralPlaneOrigin?: Point3
  neutralPlaneDirection?: Point3
  indexes?: number[]
  reversed?: boolean
}

export type ThicknessInput = GeometryDocumentContext & {
  base: ShapeHandle
  offset: number
  removeFaceIndexes?: number[]
  joinType?: 'Arc' | 'Intersection'
}

export type Point3 = [number, number, number]

export type PlanarProfile = {
  outer: Point3[]
  holes?: Point3[][]
  /** Independent filled regions emitted by profiles that produce multiple solids. */
  additionalRegions?: Array<{ outer: Point3[]; holes?: Point3[][] }>
  /** Profiles are implicitly closed unless an importer explicitly marks them open. */
  closed?: boolean
}

export type ProfileClassification = {
  status: 'closed' | 'open' | 'multi-ring' | 'self-intersecting' | 'invalid-nesting' | 'degenerate' | 'non-planar'
  ringCount: number
  selfIntersections: number
}

export type LinearFeatureParameters = {
  profile: PlanarProfile
  length: number
  direction?: Point3
  reversed?: boolean
  symmetricToPlane?: boolean
  taperAngle?: number
}

export type PadInput = GeometryDocumentContext & LinearFeatureParameters

/** Standalone Part workbench extrusion; intentionally separate from Pad. */
export type ExtrudeInput = GeometryDocumentContext & LinearFeatureParameters

export type PocketInput = GeometryDocumentContext & LinearFeatureParameters & {
  base: ShapeHandle
  throughAll?: boolean
}

export type RevolutionInput = GeometryDocumentContext & {
  profile: PlanarProfile
  axisOrigin?: Point3
  axisDirection?: Point3
  angle?: number
  /** FreeCAD TwoAngles/Midplane rotates the profile before one continuous sweep. */
  profileRotationAngle?: number
}

export type GrooveInput = GeometryDocumentContext & {
  base: ShapeHandle
  profile: PlanarProfile
  axisOrigin?: Point3
  axisDirection?: Point3
  angle?: number
  /** FreeCAD TwoAngles/Midplane rotates the profile before one continuous sweep. */
  profileRotationAngle?: number
}

export type LoftInput = GeometryDocumentContext & {
  sections: PlanarProfile[]
  mode?: 'standalone' | 'additive' | 'subtractive'
  base?: ShapeHandle
  ruled?: boolean
  closed?: boolean
}

export type PipeInput = GeometryDocumentContext & {
  profile: PlanarProfile
  path: Point3[]
  mode?: 'standalone' | 'additive' | 'subtractive'
  base?: ShapeHandle
}

export type CommandState = {
  id: string
  status: 'hidden' | 'disabled' | 'enabled' | 'active'
  reason?: string
}

export type FacadeRequestContext = {
  apiVersion: '0.1'
  requestId: string
  documentId: string
  documentVersion: number
  workbench: WorkbenchId
}

export type Diagnostic = {
  id: string
  source: 'command' | 'recompute' | 'geometry' | 'persistence' | 'system'
  severity: 'info' | 'warning' | 'error'
  code: string
  message: string
  objectId?: string
  requestId?: string
  documentId?: string
  documentVersion?: number
  generation?: number
  rootCauseObjectId?: string
  dependencyPath?: string[]
  repairActions?: DiagnosticRepairAction[]
  topologyRepair?: {
    ownerObjectId: string
    referenceName: string
    candidates: string[]
  }
  resolved?: boolean
}

export type DiagnosticRepairAction = {
  id: 'select-object' | 'recompute-root' | 'suppress-root'
  label: string
  targetObjectId: string
  enabled: boolean
  reason?: string
}

export type DiagnosticTreeNode = {
  diagnostic: Diagnostic
  children: DiagnosticTreeNode[]
}

export type DiagnosticRepairResult = {
  diagnosticId: string
  actionId: DiagnosticRepairAction['id']
  status: 'completed' | 'failed' | 'unavailable'
  message: string
}

export type TaskSnapshot = {
  id: string
  commandId: string
  title: string
  status: 'idle' | 'preview' | 'committing' | 'cancelled' | 'completed' | 'failed'
  draft: Record<string, unknown>
}

export type FacadeState = {
  apiVersion: '0.1'
  activeWorkbench: WorkbenchId
  selectedObjectId: string
  selectedObjectIds?: string[]
  selectedSubshape?: SubshapeSelection | null
  preselectedSubshape?: SubshapeSelection | null
  document: DocumentSnapshot
  persistence: PersistenceCapabilities
  task: TaskSnapshot | null
  lastNotice: string
  diagnostics: Diagnostic[]
}

export type FacadePropertyMutation = {
  transactionId: string
  documentId: string
  documentVersion: number
  objectId: string
  propertyName: string
  previousPropertyName?: string
  partialDocument: boolean
  partialTrigger: boolean
}

export type FacadeEvent =
  | { type: 'state.changed'; state: FacadeState }
  | { type: 'notice'; message: string }
  | { type: 'command.started' | 'command.completed' | 'command.failed'; commandId: string; context: FacadeRequestContext; message?: string }
  | { type: 'diagnostic.focused'; diagnosticId: string; objectId: string }
  | { type: 'diagnostic.added'; diagnostic: Diagnostic; context: FacadeRequestContext }
  | ({ type: 'property.before-change' | 'property.changed' | 'property.added' | 'property.removed' | 'property.renamed' } & FacadePropertyMutation)
  | { type: 'transaction.committed'; transactionId: string; operation: 'property.set' | 'property.add' | 'property.remove' | 'property.rename'; documentId: string; beforeVersion: number; afterVersion: number }

export type FacadeListener = (event: FacadeEvent) => void
export type Unsubscribe = () => void

export type ViewportBackend = 'webgl2' | 'webgpu'

export type ViewportInteractionHandlers = {
  onObjectClick?: (objectId?: string, options?: { additive: boolean }) => void
  onSubshapeClick?: (selection: Pick<SubshapeRef, 'kind' | 'persistentId'> & { objectId?: string }) => void
  onSubshapeHover?: (selection: (Pick<SubshapeRef, 'kind' | 'persistentId'> & { objectId?: string }) | null) => void
  onBoxSelect?: (selection: { objectIds: string[]; additive: boolean; mode: 'window' | 'crossing' }) => void
}

export interface BitBybitViewportAdapter {
  mount(host: HTMLElement): void
  resize(width: number, height: number, devicePixelRatio?: number): void
  setMesh(mesh: MeshAsset | null): void
  setMeshes?(meshes: ViewportMeshAsset[]): void
  setToolpath(paths: Array<Array<[number, number, number]>>): void
  setInteractionHandlers?(handlers: ViewportInteractionHandlers): void
  flashSelection?(): void
  setSelectedObjects?(objectIds: string[]): void
  setSelection(objectId: string): void
  setView(orientation: 'axonometric' | 'front' | 'rear' | 'left' | 'right' | 'top' | 'bottom'): void
  fitAll(): void
  zoomBy(factor: number): void
  dispose(): void
  getBackend(): ViewportBackend
}

export type ExecuteCommandInput = {
  commandId: string
  payload?: Record<string, unknown>
}

export interface BitBybitWebCadFacade {
  readonly runtime: FacadeRuntimeProfile
  readonly app: {
    document: {
      getActive(): DocumentSnapshot
      getObject(objectId: string): DocumentObjectSnapshot | null
      create(label?: string): DocumentSnapshot
      load(documentId: string): Promise<DocumentSnapshot | null>
      markDirty(): void
      setProperty(input: SetPropertyInput): void
      setIncludedFile(input: SetIncludedFileInput): Promise<IncludedFileValue>
      addDynamicProperty(input: AddDynamicPropertyInput): ObjectPropertySnapshot
      removeDynamicProperty(input: RemoveDynamicPropertyInput): boolean
      renameDynamicProperty(input: RenameDynamicPropertyInput): boolean
      reorderBodyFeature(input: ReorderBodyFeatureInput): DocumentSnapshot
      removeObject(input: RemoveObjectInput): string[]
      resolveTopologyReference(input: ResolveTopologyReferenceInput): TopoRefValue
      setExpression(input: SetExpressionInput): void
      recompute(): RecomputeResult
      recomputeAsync(options?: RecomputeExecutionOptions): Promise<RecomputeExecutionResult>
      cancelRecompute(): void
      getDependencies(): DependencyEdge[]
    }
    expression: {
      evaluate(expression: string, variables?: Record<string, Quantity>): { value: Quantity; references: string[] }
      dimensionForUnit(unit?: string): QuantityDimension
    }
    sketcher: {
      get(objectId: string): SketchSnapshot | null
      addGeometry(objectId: string, geometry: SketchGeometry): SketchSnapshot
      projectGeometry(objectId: string, sourceGeometryId: string, projectedId?: string): SketchSnapshot
      carbonCopy(objectId: string, sourceGeometryIds: string[], idPrefix?: string): SketchSnapshot
      addExternalGeometry(objectId: string, geometry: SketchExternalGeometry): SketchSnapshot
      addConstraint(objectId: string, constraint: SketchConstraint): SketchSnapshot
      solve(objectId: string): SketchSolveResult
    }
  }
  readonly history: {
    canUndo(): boolean
    canRedo(): boolean
    undo(): void
    redo(): void
  }
  readonly gui: {
    workbench: {
      list(): WorkbenchId[]
      getActive(): WorkbenchId
      setActive(id: WorkbenchId): void
    }
    command: {
      getState(commandId: string): CommandState
      list(workbench: WorkbenchId): CommandDefinition[]
      execute(input: ExecuteCommandInput): string
    }
  }
  readonly selection: {
    getObjectId(): string
    getObjectIds(): string[]
    getSubshape(): SubshapeSelection | null
    getPreselection(): SubshapeSelection | null
    select(objectId: string): void
    selectObjects(objectIds: string[]): void
    selectSubshape(selection: { objectId: string; kind: SubshapeRef['kind']; persistentId: string }): void
    preselectSubshape(selection: { objectId: string; kind: SubshapeRef['kind']; persistentId: string } | null): void
    clear(): void
  }
  readonly task: {
    getActive(): TaskSnapshot | null
    begin(commandId: string, draft?: Record<string, unknown>): TaskSnapshot
    update(draft: Record<string, unknown>): void
    apply(): void
    cancel(): void
  }
  readonly diagnostics: {
    list(): Diagnostic[]
    tree(): DiagnosticTreeNode[]
    repair(diagnosticId: string, actionId: DiagnosticRepairAction['id']): Promise<DiagnosticRepairResult>
  }
  readonly cam: CamApi
  readonly project: {
    capabilities(): PersistenceCapabilities
    subscribeExternalChanges(listener: (notice: ProjectChangeNotice) => void): Unsubscribe
    list(): Promise<ProjectSummary[]>
    save(document?: DocumentSnapshot): Promise<ProjectSaveResult>
    load(documentId: string): Promise<DocumentSnapshot | null>
    loadCheckpoint(documentId: string, version?: number): Promise<DocumentSnapshot | null>
    recovery(documentId: string): Promise<ProjectRecoveryReport>
    fcstd: {
      inspect(bytes: Uint8Array, limits?: Partial<FcstdArchiveLimits>): FcstdInspection
      extractShapes(bytes: Uint8Array, limits?: Partial<FcstdArchiveLimits>): FcstdShapeResourcePayload[]
      storeShapes(bytes: Uint8Array, limits?: Partial<FcstdArchiveLimits>): Promise<FcstdStoredShapeResource[]>
      extractIncludedFiles(bytes: Uint8Array, limits?: Partial<FcstdArchiveLimits>): FcstdIncludedFileResourcePayload[]
      storeIncludedFiles(bytes: Uint8Array, limits?: Partial<FcstdArchiveLimits>): Promise<FcstdStoredIncludedFileResource[]>
      instantiateShape(bytes: Uint8Array, resourcePath: string, signal?: AbortSignal, limits?: Partial<FcstdArchiveLimits>): Promise<FcstdInstantiatedShape<ShapeHandle>>
      serializeMetadata(document: DocumentSnapshot, options?: FcstdWriteOptions): Uint8Array
      serializeMetadataWithResources(document: DocumentSnapshot, options?: FcstdWriteOptions): Promise<Uint8Array>
      rewriteMetadata(bytes: Uint8Array, document: DocumentSnapshot, options?: FcstdWriteOptions): Uint8Array
      rewriteMetadataWithResources(bytes: Uint8Array, document: DocumentSnapshot, options?: FcstdWriteOptions): Promise<Uint8Array>
      decodePath(bytes: Uint8Array, objectName: string, propertyName?: string): PathPropertyValue
      rewritePath(bytes: Uint8Array, edit: FcstdPathEdit): Uint8Array
      rewriteNumeric(bytes: Uint8Array, edit: FcstdNumericPropertyEdit): Uint8Array
      rewriteMeasureArea(bytes: Uint8Array, edit: FcstdMeasureAreaEdit): Uint8Array
      rewriteBoolList(bytes: Uint8Array, edit: FcstdBoolListEdit): Uint8Array
      rewriteIntegerSet(bytes: Uint8Array, edit: FcstdIntegerSetEdit): Uint8Array
      rewriteColor(bytes: Uint8Array, edit: FcstdColorEdit): Uint8Array
      rewriteColorList(bytes: Uint8Array, edit: FcstdColorListEdit): Uint8Array
      rewriteDirection(bytes: Uint8Array, edit: FcstdDirectionEdit): Uint8Array
      rewriteFile(bytes: Uint8Array, edit: FcstdFileEdit): Uint8Array
      rewriteFont(bytes: Uint8Array, edit: FcstdFontEdit): Uint8Array
      rewriteIncludedFile(bytes: Uint8Array, edit: FcstdIncludedFileEdit): Promise<Uint8Array>
    }
    resource: {
      put(bytes: Uint8Array, mediaType: string): Promise<ProjectResource>
      get(hash: string): Promise<Uint8Array | null>
      release(hash: string): Promise<void>
      sweep(): Promise<ProjectResourceSweepReport>
    }
    dispose(): Promise<void>
  }
  readonly geometry: {
    capabilities(): GeometryCapabilities
    configureNativeHistory?(provider: NativeOcctHistoryProvider | null, timeoutMs?: number): void
    nativeHistoryCapabilities?(): NativeOcctHistoryCapabilities
    initialize(): Promise<GeometryCapabilities>
    createBox(input: CreateBoxInput): Promise<ShapeHandle>
    createCylinder(input: CreateCylinderInput): Promise<ShapeHandle>
    createSphere(input: CreateSphereInput): Promise<ShapeHandle>
    createCone(input: CreateConeInput): Promise<ShapeHandle>
    createTorus(input: CreateTorusInput): Promise<ShapeHandle>
    createHelix(input: CreateHelixInput): Promise<ShapeHandle>
    createPrism(input: CreatePrismInput): Promise<ShapeHandle>
    createWedge(input: CreateWedgeInput): Promise<ShapeHandle>
    createEllipsoid(input: CreateEllipsoidInput): Promise<ShapeHandle>
    applyPlacement(input: ApplyPlacementInput): Promise<ShapeHandle>
    mirror(input: MirrorInput): Promise<ShapeHandle>
    union(input: BooleanUnionInput): Promise<ShapeHandle>
    cut(input: BooleanCutInput): Promise<ShapeHandle>
    intersection(input: BooleanIntersectionInput): Promise<ShapeHandle>
    fillet(input: FilletInput): Promise<ShapeHandle>
    chamfer(input: ChamferInput): Promise<ShapeHandle>
    draft(input: DraftInput): Promise<ShapeHandle>
    thickness(input: ThicknessInput): Promise<ShapeHandle>
    exportStep(shape: ShapeHandle, fileName?: string): Promise<GeometryFileExport>
    exportIges(shape: ShapeHandle, fileName?: string): Promise<GeometryFileExport>
    exportStl(shape: ShapeHandle, fileName?: string, precision?: number): Promise<GeometryFileExport>
    exportBrep(shape: ShapeHandle, fileName?: string): Promise<GeometryFileExport>
    pad(input: PadInput): Promise<ShapeHandle>
    extrude(input: ExtrudeInput): Promise<ShapeHandle>
    pocket(input: PocketInput): Promise<ShapeHandle>
    revolution(input: RevolutionInput): Promise<ShapeHandle>
    groove(input: GrooveInput): Promise<ShapeHandle>
    loft(input: LoftInput): Promise<ShapeHandle>
    pipe(input: PipeInput): Promise<ShapeHandle>
    importShape(input: GeometryFileImport): Promise<ShapeHandle>
    mesh(shape: ShapeHandle, precision?: number): Promise<MeshAsset>
    massProperties(shape: ShapeHandle): Promise<ShapeMassProperties>
    qualityReport(shape: ShapeHandle): Promise<ShapeQualityReport>
    linearLength(shape: ShapeHandle): Promise<number>
    subshapes(shape: ShapeHandle, precision?: number): Promise<SubshapeRef[]>
    topology(shape: ShapeHandle, precision?: number): Promise<SubshapeTopology>
    topologyHistory?(input: NativeTopologyHistoryInput): Promise<NativeTopologyHistoryRecord[]>
    getObjectShape(objectId: string): ShapeHandle | null
    release(shape: ShapeHandle): Promise<void>
    dispose(): void
  }
  readonly viewport: {
    createAdapter(): BitBybitViewportAdapter
  }
  getState(): FacadeState
  subscribe(listener: FacadeListener): Unsubscribe
  notify(message: string): void
}
