import { createMockFacade } from './facade/mockFacade'
import type { BitBybitWebCadFacade, DocumentObjectSnapshot, PlacementValue } from './facade/types'

type FeatureEvidence = {
  command: string
  objectId: string
  typeId: string
  recompute: string
  base: unknown
  profile: unknown
  sections: unknown
  spine: unknown
  transition: unknown
  tip: unknown
  shapeId: string | null
  volume: number
}

type LoftReport = {
  schemaVersion: 1
  status: 'pass' | 'failed'
  browserId: 'chrome'
  crossOriginIsolated: boolean
  operations: FeatureEvidence[]
  failureRecovery?: {
    loft: { status: string; code: string; retainedShapeId: string | null; previousShapeId: string | null; restoredStatus: string; restoredShapeId: string | null }
    pipe: { status: string; code: string; retainedShapeId: string | null; previousShapeId: string | null; restoredStatus: string; restoredShapeId: string | null }
  }
  persistence?: { mode: string; savedVersion: number; reopenedVersion: number; objectCount: number; reopenedTip: unknown }
  opfs?: { markerSuite: string; markerOperations: number; markerRemoved: boolean }
  beforeRelease?: { shapeCount: number; kernelReferenceCount: number }
  afterRelease?: { shapeCount: number; kernelReferenceCount: number }
  error?: string
}

const rectangleAt = (prefix: string, x0: number, y0: number, x1: number, y1: number) => [
  { id: `${prefix}-1`, type: 'line' as const, start: { x: x0, y: y0 }, end: { x: x1, y: y0 } },
  { id: `${prefix}-2`, type: 'line' as const, start: { x: x1, y: y0 }, end: { x: x1, y: y1 } },
  { id: `${prefix}-3`, type: 'line' as const, start: { x: x1, y: y1 }, end: { x: x0, y: y1 } },
  { id: `${prefix}-4`, type: 'line' as const, start: { x: x0, y: y1 }, end: { x: x0, y: y0 } },
]
const rectangle = (prefix: string) => rectangleAt(prefix, 0, 0, 4, 3)

const property = (facade: BitBybitWebCadFacade, objectId: string, name: string): unknown => facade.app.document.getObject(objectId)?.properties.find((entry) => entry.name === name)?.value ?? null
const ids = (facade: BitBybitWebCadFacade) => new Set(facade.app.document.getActive().objects.map((object) => object.id))
const newest = (facade: BitBybitWebCadFacade, previous: Set<string>): DocumentObjectSnapshot => {
  const object = facade.app.document.getActive().objects.find((candidate) => !previous.has(candidate.id))
  if (!object) throw new Error('PartDesign loft task did not append an object.')
  return object
}

const createSketch = (facade: BitBybitWebCadFacade, geometry: ReturnType<typeof rectangle>, offsetZ = 0): string => {
  const previous = ids(facade)
  facade.task.begin('create-sketch')
  facade.task.apply()
  const sketch = newest(facade, previous)
  for (const item of geometry) facade.app.sketcher.addGeometry(sketch.id, item)
  if (offsetZ !== 0) {
    const offset: PlacementValue = { position: { x: 0, y: 0, z: offsetZ }, rotation: { axis: { x: 0, y: 0, z: 1 }, angle: 0 } }
    facade.app.document.setProperty({ objectId: sketch.id, propertyName: 'AttachmentOffset', value: offset })
  }
  return sketch.id
}

const applyFeature = async (facade: BitBybitWebCadFacade, command: string, draft: Record<string, unknown>): Promise<FeatureEvidence> => {
  const previous = ids(facade)
  if (facade.gui.command.getState(command).status !== 'enabled') throw new Error(`${command} is not enabled for ${facade.selection.getObjectId()}.`)
  facade.gui.command.execute({ commandId: command })
  const task = facade.task.getActive()
  if (!task || task.commandId !== command || task.status !== 'preview') throw new Error(`${command} did not open a preview task.`)
  facade.task.update(draft)
  facade.task.apply()
  const object = newest(facade, previous)
  const recompute = await facade.app.document.recomputeAsync({ dirtyObjectIds: [object.id] })
  if (recompute.status !== 'completed') throw new Error(`${command} recompute failed: ${JSON.stringify(recompute.errors)}`)
  const shape = facade.geometry.getObjectShape(object.id)
  const tip = property(facade, 'body', 'Tip')
  if (!shape || tip !== object.id) throw new Error(`${command} did not retain a ShapeHandle or redirect Body.Tip.`)
  const mass = await facade.geometry.massProperties(shape)
  if (!(mass.volume > 0)) throw new Error(`${command} produced a non-solid or empty result.`)
  return { command, objectId: object.id, typeId: object.typeId, recompute: recompute.status, base: property(facade, object.id, 'Base'), profile: property(facade, object.id, 'Profile'), sections: property(facade, object.id, 'Sections'), spine: property(facade, object.id, 'Spine'), transition: property(facade, object.id, 'Transition'), tip, shapeId: shape.id, volume: mass.volume }
}

const run = async (): Promise<LoftReport> => {
  const facade = createMockFacade()
  const report: LoftReport = { schemaVersion: 1, status: 'failed', browserId: 'chrome', crossOriginIsolated: globalThis.crossOriginIsolated === true, operations: [] }
  try {
    if (!report.crossOriginIsolated || !navigator.storage?.getDirectory) throw new Error('Chrome PartDesign loft evidence requires isolated OPFS.')
    const initialized = await facade.geometry.initialize()
    if (initialized.status !== 'ready') throw new Error(initialized.reason || 'Bitbybit geometry runtime is unavailable.')
    for (const item of rectangle('base')) facade.app.sketcher.addGeometry('sketch', item)
    facade.app.document.setProperty({ objectId: 'pocket', propertyName: 'Suppressed', value: true })
    facade.app.document.setProperty({ objectId: 'fillet', propertyName: 'Suppressed', value: true })
    const initial = await facade.app.document.recomputeAsync()
    if (initial.status !== 'completed') throw new Error(`Initial Pad recompute failed: ${JSON.stringify(initial.errors)}`)
    const section = createSketch(facade, rectangle('section'), 6)
    const spine = createSketch(facade, [{ id: 'spine-1', type: 'line' as const, start: { x: 0, y: 0 }, end: { x: 6, y: 0 } }])
    const pipeProfile = createSketch(facade, [
      { id: 'pipe-profile-1', type: 'line' as const, start: { x: -1, y: -1 }, end: { x: 1, y: -1 } },
      { id: 'pipe-profile-2', type: 'line' as const, start: { x: 1, y: -1 }, end: { x: 1, y: 1 } },
      { id: 'pipe-profile-3', type: 'line' as const, start: { x: 1, y: 1 }, end: { x: -1, y: 1 } },
      { id: 'pipe-profile-4', type: 'line' as const, start: { x: -1, y: 1 }, end: { x: -1, y: -1 } },
    ])
    facade.app.document.setProperty({ objectId: pipeProfile, propertyName: 'AttachmentOffset', value: { position: { x: 0, y: 0, z: 0 }, rotation: { axis: { x: 1, y: 1, z: 1 }, angle: 120 } } })
    facade.selection.select('sketch')
    const loft = await applyFeature(facade, 'additive-loft', { base: 'pad', sections: ['sketch', section], ruled: false, closed: false })
    report.operations.push(loft)
    const previousShapeId = loft.shapeId

    facade.app.document.setProperty({ objectId: loft.objectId, propertyName: 'Sections', value: [section, section] })
    const invalid = await facade.app.document.recomputeAsync({ dirtyObjectIds: [loft.objectId] })
    const diagnostic = facade.diagnostics.list().find((entry) => entry.objectId === loft.objectId && entry.code === 'LOFT_SECTIONS_MISSING') ?? facade.diagnostics.list().find((entry) => entry.objectId === loft.objectId)
    const retainedShape = facade.geometry.getObjectShape(loft.objectId)
    if (invalid.status !== 'failed' || !diagnostic || !retainedShape || retainedShape.id !== previousShapeId) throw new Error(`Loft failure did not retain the last valid Shape: ${JSON.stringify({ invalid, diagnostic, previousShapeId, retainedShapeId: retainedShape?.id })}`)
    facade.app.document.setProperty({ objectId: loft.objectId, propertyName: 'Sections', value: [section] })
    facade.app.document.setProperty({ objectId: loft.objectId, propertyName: 'Profile', value: 'sketch' })
    const restored = await facade.app.document.recomputeAsync({ dirtyObjectIds: [loft.objectId] })
    const restoredShape = facade.geometry.getObjectShape(loft.objectId)
    if (restored.status !== 'completed' || !restoredShape) throw new Error(`Loft did not recover after restoring sections: ${JSON.stringify(restored.errors)}`)
    const loftRecovery = { status: invalid.status, code: diagnostic.code, retainedShapeId: retainedShape.id, previousShapeId, restoredStatus: restored.status, restoredShapeId: restoredShape.id }

    facade.gui.workbench.setActive('Part Design')
    facade.selection.select(pipeProfile)
    const additivePipe = await applyFeature(facade, 'additive-pipe', { base: loft.objectId, profile: pipeProfile, spine, transition: 'Transformed' })
    report.operations.push(additivePipe)

    const cutSection0 = createSketch(facade, rectangleAt('cut-section-0', 1, 1, 3, 2))
    const cutSection1 = createSketch(facade, rectangleAt('cut-section-1', 1, 1, 3, 2), 6)
    facade.selection.select(cutSection0)
    const subtractiveLoft = await applyFeature(facade, 'subtractive-loft', { base: additivePipe.objectId, sections: [cutSection0, cutSection1], ruled: false, closed: false })
    report.operations.push(subtractiveLoft)

    const subtractivePipeProfile = createSketch(facade, rectangleAt('cut-pipe-profile', -0.25, -0.25, 0.25, 0.25))
    facade.app.document.setProperty({ objectId: subtractivePipeProfile, propertyName: 'AttachmentOffset', value: { position: { x: 0, y: 0.5, z: 3 }, rotation: { axis: { x: 1, y: 1, z: 1 }, angle: 120 } } })
    const subtractivePipeSpine = createSketch(facade, [{ id: 'cut-pipe-spine-1', type: 'line' as const, start: { x: 0, y: 0 }, end: { x: 4, y: 0 } }])
    facade.app.document.setProperty({ objectId: subtractivePipeSpine, propertyName: 'AttachmentOffset', value: { position: { x: 0, y: 0.5, z: 3 }, rotation: { axis: { x: 0, y: 0, z: 1 }, angle: 0 } } })
    facade.selection.select(subtractivePipeProfile)
    const subtractivePipe = await applyFeature(facade, 'subtractive-pipe', { base: subtractiveLoft.objectId, profile: subtractivePipeProfile, spine: subtractivePipeSpine, transition: 'Transformed' })
    report.operations.push(subtractivePipe)

    const pipeShapeBeforeFailure = facade.geometry.getObjectShape(subtractivePipe.objectId)
    const branchSpine = createSketch(facade, [
      { id: 'branch-a', type: 'line' as const, start: { x: 0, y: 0 }, end: { x: 1, y: 0 } },
      { id: 'branch-b', type: 'line' as const, start: { x: 1, y: 0 }, end: { x: 2, y: 0 } },
      { id: 'branch-c', type: 'line' as const, start: { x: 1, y: 0 }, end: { x: 1, y: 1 } },
    ])
    facade.app.document.setProperty({ objectId: subtractivePipe.objectId, propertyName: 'Spine', value: { schemaVersion: 1, objectId: branchSpine, subElements: [] } })
    const invalidPipe = await facade.app.document.recomputeAsync({ dirtyObjectIds: [subtractivePipe.objectId] })
    const pipeDiagnostic = facade.diagnostics.list().find((entry) => entry.objectId === subtractivePipe.objectId && entry.code === 'PIPE_PATH_BRANCH')
    const retainedPipeShape = facade.geometry.getObjectShape(subtractivePipe.objectId)
    if (invalidPipe.status !== 'failed' || !pipeDiagnostic || !pipeShapeBeforeFailure || retainedPipeShape?.id !== pipeShapeBeforeFailure.id) throw new Error('Pipe failure did not retain the last valid Shape.')
    facade.app.document.setProperty({ objectId: subtractivePipe.objectId, propertyName: 'Spine', value: { schemaVersion: 1, objectId: subtractivePipeSpine, subElements: [] } })
    const restoredPipe = await facade.app.document.recomputeAsync({ dirtyObjectIds: [subtractivePipe.objectId] })
    const restoredPipeShape = facade.geometry.getObjectShape(subtractivePipe.objectId)
    if (restoredPipe.status !== 'completed' || !restoredPipeShape) throw new Error('Pipe did not recover after restoring the Spine.')
    report.failureRecovery = {
      loft: loftRecovery,
      pipe: { status: invalidPipe.status, code: pipeDiagnostic.code, retainedShapeId: retainedPipeShape.id, previousShapeId: pipeShapeBeforeFailure.id, restoredStatus: restoredPipe.status, restoredShapeId: restoredPipeShape.id },
    }
    const saved = await facade.project.save()
    const loaded = await facade.project.load(facade.app.document.getActive().id)
    if (!loaded) throw new Error('PartDesign loft document did not load from OPFS.')
    await facade.app.document.load(facade.app.document.getActive().id)
    const reopened = facade.app.document.getActive()
    if (reopened.objects.length !== loaded.objects.length || property(facade, 'body', 'Tip') !== subtractivePipe.objectId) throw new Error('PartDesign loft OPFS round-trip changed object count or Body.Tip.')
    const directory = await navigator.storage.getDirectory()
    const markerName = 'partdesign-loft-chrome-marker.json'
    const marker = await directory.getFileHandle(markerName, { create: true })
    const writable = await marker.createWritable()
    await writable.write(JSON.stringify({ suite: 'PD-LOFT', operations: report.operations.length }))
    await writable.close()
    const markerPayload = JSON.parse(await (await marker.getFile()).text()) as { suite: string; operations: number }
    await directory.removeEntry(markerName)
    let markerRemoved = false
    try { await directory.getFileHandle(markerName) } catch (error) { markerRemoved = error instanceof DOMException && error.name === 'NotFoundError' }
    report.persistence = { mode: facade.project.capabilities().mode, savedVersion: saved.documentVersion, reopenedVersion: reopened.version, objectCount: reopened.objects.length, reopenedTip: property(facade, 'body', 'Tip') }
    report.opfs = { markerSuite: markerPayload.suite, markerOperations: markerPayload.operations, markerRemoved }
    report.beforeRelease = { shapeCount: facade.geometry.capabilities().shapeCount, kernelReferenceCount: facade.geometry.capabilities().kernelReferenceCount }
    report.status = 'pass'
  } catch (error) {
    report.error = error instanceof Error ? error.stack || error.message : String(error)
  } finally {
    facade.geometry.dispose()
    await new Promise((resolve) => setTimeout(resolve, 0))
    report.afterRelease = { shapeCount: facade.geometry.capabilities().shapeCount, kernelReferenceCount: facade.geometry.capabilities().kernelReferenceCount }
    if (report.afterRelease.shapeCount !== 0 || report.afterRelease.kernelReferenceCount !== 0) { report.status = 'failed'; report.error = `${report.error ? `${report.error} ` : ''}Shape ownership gate failed: ${JSON.stringify(report.afterRelease)}` }
  }
  return report
}

run().then((report) => {
  ;(window as Window & { __bitbybitPartdesignLoftReport?: LoftReport }).__bitbybitPartdesignLoftReport = report
  document.documentElement.dataset.status = report.status
  document.querySelector('#result')!.textContent = JSON.stringify(report, null, 2)
}).catch((error) => {
  document.documentElement.dataset.status = 'failed'
  document.querySelector('#result')!.textContent = error instanceof Error ? error.stack || error.message : String(error)
})

export {}
