import { inspectFcstdArchive, serializeFcstdMetadataArchive } from './facade/fcstd'
import { createSketch } from './facade/sketcher'
import type { DocumentSnapshot, TopoRefValue } from './facade/types'

type SemanticReport = { schemaVersion: 1; status: 'pass' | 'failed'; browserId: 'chrome'; objectNames: string[]; expression?: string; link?: string; linkSub?: { objectId: string; subElement: string }; linkSubList?: Array<{ objectId: string; subElement: string }>; sketchGeometryIds: string[]; dependencyRelations: string[]; archiveBytes: number; guiViews: string[]; shapeResource?: { path: string; byteLength: number; elementMap?: string; elementMapEntries: number }; opfs?: { markerSuite: string; archiveBytes: number; markerRemoved: boolean }; error?: string }
const status = (message: string) => { document.querySelector('#status')!.textContent = message }
const sourceRef: TopoRefValue = { schemaVersion: 1, objectId: 'Source', kind: 'face', persistentId: 'Face1', topologyVersion: 4, generation: 2, status: 'stable', signature: 'source-face' }
const sourceRef2: TopoRefValue = { schemaVersion: 1, objectId: 'Source', kind: 'face', persistentId: 'Face2', topologyVersion: 4, generation: 2, status: 'stable', signature: 'source-face-2' }
const documentFixture = (): DocumentSnapshot => ({
  id: 'chrome-fcstd-semantic', label: 'Chrome FCStd semantic', version: 1, dirty: false, readOnly: false, units: 'mm',
  tree: [{ id: 'Source', label: 'Source', type: 'feature', state: 'valid' }, { id: 'Spreadsheet', label: 'Spreadsheet', type: 'feature', state: 'valid' }, { id: 'Cut', label: 'Cut', type: 'feature', state: 'valid' }, { id: 'Sketch', label: 'Sketch', type: 'sketch', state: 'valid' }],
  objects: [
    { id: 'Source', typeId: 'Part::Feature', properties: [{ name: 'Length', label: 'Length', group: 'Box', scope: 'data', type: 'App::PropertyLength', value: 10 }, { name: 'Shape', label: 'Shape', group: 'Base', scope: 'data', type: 'Part::PropertyPartShape', value: { path: 'Part/Source.Shape.brp', format: 'brep', elementMap: '1', elementMapEntries: [{ key: 'Face1', value: 'Face1' }] } }] },
    { id: 'Spreadsheet', typeId: 'Spreadsheet::Sheet', properties: [{ name: 'Width', label: 'Width', group: 'Spreadsheet', scope: 'data', type: 'App::PropertyLength', value: 6 }, { name: 'Length', label: 'Length', group: 'Spreadsheet', scope: 'data', type: 'App::PropertyLength', value: 12, expression: 'Spreadsheet.Width * 2' }] },
    { id: 'Cut', typeId: 'Part::Cut', properties: [{ name: 'Base', label: 'Base', group: 'Boolean', scope: 'data', type: 'App::PropertyLink', value: 'Source' }, { name: 'References', label: 'References', group: 'Boolean', scope: 'data', type: 'App::PropertyLinkSubList', value: { schemaVersion: 1, entries: [{ objectId: 'Source', subElement: sourceRef }, { objectId: 'Source', subElement: sourceRef2 }] } }] },
    { id: 'Sketch', typeId: 'Sketcher::SketchObject', properties: [{ name: 'Support', label: 'Support', group: 'Attachment', scope: 'data', type: 'App::PropertyLink', value: { objectId: 'Source', subElement: sourceRef } }], sketch: createSketch('Sketch', [{ id: 'profile', type: 'line', start: { x: 0, y: 0 }, end: { x: 4, y: 0 } }]) },
  ],
  dependencies: [{ sourceId: 'Spreadsheet', targetId: 'Spreadsheet', relation: 'expression', propertyName: 'Length' }, { sourceId: 'Cut', targetId: 'Source', relation: 'link', propertyName: 'Base' }, { sourceId: 'Cut', targetId: 'Source', relation: 'topo-ref', propertyName: 'References', reference: 'Face1' }, { sourceId: 'Cut', targetId: 'Source', relation: 'topo-ref', propertyName: 'References', reference: 'Face2' }, { sourceId: 'Sketch', targetId: 'Source', relation: 'topo-ref', propertyName: 'Support', reference: 'Face1' }],
  recompute: { generation: 0, status: 'idle', objectStates: { Source: 'up-to-date', Spreadsheet: 'up-to-date', Cut: 'up-to-date', Sketch: 'up-to-date' }, dirtyObjects: [], order: ['Source', 'Spreadsheet', 'Cut', 'Sketch'], errors: [] },
})

const run = async (): Promise<SemanticReport> => {
  try {
    const archive = serializeFcstdMetadataArchive(documentFixture(), { guiViews: [{ name: 'Front', type: 'orthographic', visibility: 'true' }, { name: 'Top', type: 'orthographic', visibility: 'true' }], opaqueEntries: { 'Part/Source.Shape.brp': new TextEncoder().encode('DBRep_DrawableShape\n') } })
    const inspection = inspectFcstdArchive(archive)
    const spreadsheet = inspection.objects.find((object) => object.name === 'Spreadsheet')
    const cut = inspection.objects.find((object) => object.name === 'Cut')
    const sketch = inspection.objects.find((object) => object.name === 'Sketch')
    const source = inspection.objects.find((object) => object.name === 'Source')
    const expression = spreadsheet?.properties.find((property) => property.name === 'Length')?.expression
    const link = cut?.properties.find((property) => property.name === 'Base')?.value
    const references = inspection.objects.find((object) => object.name === 'Cut')?.properties.find((property) => property.name === 'References')
    const linkSubList = references?.linkSubs?.map((entry) => ({ objectId: entry.objectId, subElement: entry.subElement }))
    const support = inspection.proxyDocument.objects.find((object) => object.id === 'Sketch')?.properties.find((property) => property.name === 'Support')?.value as { objectId?: string; subElement?: string | TopoRefValue } | undefined
    const linkSub = support?.objectId && support.subElement ? { objectId: support.objectId, subElement: typeof support.subElement === 'string' ? support.subElement : support.subElement.persistentId } : undefined
    const shapeSummary = source?.properties.find((property) => property.name === 'Shape')?.shapeResource
    const shapeResource = inspection.shapeResources.find((resource) => resource.path === 'Part/Source.Shape.brp')
    const markerDirectory = await navigator.storage.getDirectory()
    const markerName = 'fcstd-semantic-chrome-marker.bin'
    const marker = await markerDirectory.getFileHandle(markerName, { create: true })
    const writable = await marker.createWritable()
    await writable.write(archive.buffer as ArrayBuffer)
    await writable.close()
    const archiveBytes = (await marker.getFile()).size
    await markerDirectory.removeEntry(markerName)
    let markerRemoved = false
    try { await markerDirectory.getFileHandle(markerName) } catch (error) { markerRemoved = error instanceof DOMException && error.name === 'NotFoundError' }
    const dependencyRelations = [
      ...(expression ? ['expression'] : []),
      ...(link === 'Source' ? ['link'] : []),
      ...(linkSub?.objectId === 'Source' && linkSub.subElement === 'Face1' ? ['topo-ref'] : []),
      ...(JSON.stringify(linkSubList) === JSON.stringify([{ objectId: 'Source', subElement: 'Face1' }, { objectId: 'Source', subElement: 'Face2' }]) ? ['topo-ref-list'] : []),
    ]
    const report: SemanticReport = { schemaVersion: 1, status: 'failed', browserId: 'chrome', objectNames: inspection.objects.map((object) => object.name), expression, link: typeof link === 'string' ? link : undefined, linkSub, linkSubList, sketchGeometryIds: sketch?.sketch?.geometry.map((geometry) => geometry.id) ?? [], dependencyRelations, archiveBytes, guiViews: inspection.guiDocument.views.map((view) => view.name), shapeResource: shapeSummary && shapeResource ? { path: shapeResource.path, byteLength: shapeResource.byteLength, elementMap: shapeSummary.elementMap, elementMapEntries: shapeSummary.elementMapEntries?.length ?? 0 } : undefined, opfs: { markerSuite: 'FC-06', archiveBytes, markerRemoved } }
    const shapeEvidence = report.shapeResource
    report.status = report.objectNames.length === 4 && report.expression === 'Spreadsheet.Width * 2' && report.link === 'Source' && report.linkSub?.objectId === 'Source' && report.linkSub.subElement === 'Face1' && JSON.stringify(report.linkSubList) === JSON.stringify([{ objectId: 'Source', subElement: 'Face1' }, { objectId: 'Source', subElement: 'Face2' }]) && JSON.stringify(report.sketchGeometryIds) === JSON.stringify(['profile']) && report.dependencyRelations.includes('expression') && report.dependencyRelations.includes('link') && report.dependencyRelations.includes('topo-ref') && report.dependencyRelations.includes('topo-ref-list') && JSON.stringify(report.guiViews) === JSON.stringify(['Front', 'Top']) && shapeEvidence !== undefined && shapeEvidence.byteLength > 0 && shapeEvidence.elementMap === '1' && shapeEvidence.elementMapEntries === 1 && report.archiveBytes > 0 && report.opfs?.markerRemoved === true ? 'pass' : 'failed'
    return report
  } catch (error) { return { schemaVersion: 1, status: 'failed', browserId: 'chrome', objectNames: [], sketchGeometryIds: [], dependencyRelations: [], archiveBytes: 0, guiViews: [], error: error instanceof Error ? error.message : String(error) } }
}
run().then((report) => { ;(window as Window & { __bitbybitFcstdSemanticReport?: SemanticReport }).__bitbybitFcstdSemanticReport = report; document.documentElement.dataset.status = report.status; status(JSON.stringify(report)) }).catch((error) => { document.documentElement.dataset.status = 'failed'; status(error instanceof Error ? error.message : String(error)) })
export {}
