import { createMockFacade } from './facade/mockFacade'
import { createBimModel } from './facade/bim'

type BimReport = {
  schemaVersion: 1
  status: 'pass' | 'failed'
  browserId: 'chrome'
  crossOriginIsolated: boolean
  model?: { sites: number; buildings: number; levels: number; spaces: number; materials: number; elements: number; scheduleRows: number; ifc4Bytes: number; ifc2x3Bytes: number; ifc4Schema: string; ifc2x3Schema: string; ifc4Entities: number; ifc4Hierarchy: number; ifc4PropertySets: number; ifc4Classifications: number; ifc2x3RoundTrip: boolean }
  persistence?: { mode: string; byteLength: number; hash: string; roundTrip: boolean; released: boolean }
  opfs?: { markerSuite: string; elements: number; markerRemoved: boolean }
  afterRelease?: { shapeCount: number; kernelReferenceCount: number }
  error?: string
}

const sha256 = async (bytes: Uint8Array) => [...new Uint8Array(await crypto.subtle.digest('SHA-256', Uint8Array.from(bytes).buffer))].map((value) => value.toString(16).padStart(2, '0')).join('')

const run = async (): Promise<BimReport> => {
  const facade = createMockFacade()
  const report: BimReport = { schemaVersion: 1, status: 'failed', browserId: 'chrome', crossOriginIsolated: globalThis.crossOriginIsolated === true }
  try {
    if (!report.crossOriginIsolated || !navigator.storage?.getDirectory) throw new Error('Chrome BIM evidence requires isolated OPFS.')
    await facade.project.list()
    const shape = await facade.geometry.createBox({ documentId: 'bim', documentVersion: 1, width: 5, length: 2, height: 3 })
    const bim = createBimModel('bim', 'Plant BIM')
    bim.addSite({ id: 'site', label: 'Campus', latitude: 40, longitude: -74 })
    bim.addBuilding({ id: 'building', label: 'Plant', siteId: 'site' })
    bim.addLevel({ id: 'L1', label: 'Ground', elevation: 0, buildingId: 'building' })
    bim.addSpace({ id: 'S1', label: 'Plant room', levelId: 'L1', area: 24 })
    bim.addMaterial({ id: 'concrete', name: 'Concrete', category: 'structural' })
    bim.addElement({ id: 'wall-1', label: 'Wall 1', type: 'wall', shapeId: shape.id, levelId: 'L1', spaceId: 'S1', properties: { FireRating: 'A1' }, quantity: { length: 5, area: 6, volume: 0.5 } })
    bim.assignMaterial('wall-1', 'concrete')
    bim.setClassification('wall-1', { system: 'OmniClass', code: '21-02' })
    const schedule = bim.schedule()
    const ifc4Text = bim.exportIfc('IFC4')
    const ifc2x3Text = bim.exportIfc('IFC2X3')
    const ifc4 = new TextEncoder().encode(ifc4Text)
    const ifc2x3 = new TextEncoder().encode(ifc2x3Text)
    const imported4 = bim.importIfc(ifc4Text)
    const imported2x3 = bim.importIfc(ifc2x3Text)
    const snapshot = bim.snapshot()
    const payload = new TextEncoder().encode(JSON.stringify({ snapshot, ifc4: imported4, ifc2x3: imported2x3 }))
    const stored = await facade.project.resource.put(payload, 'application/ifc')
    const loaded = await facade.project.resource.get(stored.hash)
    const roundTrip = loaded !== null && await sha256(loaded) === await sha256(payload)
    await facade.project.resource.release(stored.hash)
    const released = await facade.project.resource.get(stored.hash) === null
    const directory = await navigator.storage.getDirectory()
    const markerName = 'bim-chrome-marker.json'
    const marker = await directory.getFileHandle(markerName, { create: true })
    const writable = await marker.createWritable(); await writable.write(JSON.stringify({ suite: 'BIM-IFC', elements: snapshot.elements.length })); await writable.close()
    const markerPayload = JSON.parse(await (await marker.getFile()).text()) as { suite: string; elements: number }
    await directory.removeEntry(markerName)
    let markerRemoved = false
    try { await directory.getFileHandle(markerName) } catch (error) { markerRemoved = error instanceof DOMException && error.name === 'NotFoundError' }
    report.model = { sites: snapshot.sites.length, buildings: snapshot.buildings.length, levels: snapshot.levels.length, spaces: snapshot.spaces.length, materials: snapshot.materials.length, elements: snapshot.elements.length, scheduleRows: schedule.length, ifc4Bytes: ifc4.byteLength, ifc2x3Bytes: ifc2x3.byteLength, ifc4Schema: imported4.schema, ifc2x3Schema: imported2x3.schema, ifc4Entities: imported4.entities, ifc4Hierarchy: imported4.hierarchy, ifc4PropertySets: imported4.propertySets, ifc4Classifications: imported4.classifications, ifc2x3RoundTrip: imported2x3.entities === imported4.entities }
    report.persistence = { mode: facade.project.capabilities().mode, byteLength: stored.byteLength, hash: stored.hash, roundTrip, released }
    report.opfs = { markerSuite: markerPayload.suite, elements: markerPayload.elements, markerRemoved }
    report.status = report.model.sites === 1 && report.model.buildings === 1 && report.model.levels === 1 && report.model.spaces === 1 && report.model.materials === 1 && report.model.elements === 1 && report.model.scheduleRows === 1 && report.model.ifc4Bytes > 0 && report.model.ifc2x3Bytes > 0 && report.model.ifc4Schema === 'IFC4' && report.model.ifc2x3Schema === 'IFC2X3' && report.model.ifc4Entities >= 7 && report.model.ifc4Hierarchy === 4 && report.model.ifc4PropertySets >= 1 && report.model.ifc4Classifications === 1 && report.model.ifc2x3RoundTrip && report.persistence.mode === 'sqlite-opfs' && report.persistence.roundTrip && report.persistence.released && report.opfs.markerSuite === 'BIM-IFC' && report.opfs.elements === 1 && report.opfs.markerRemoved ? 'pass' : 'failed'
  } catch (error) { report.error = error instanceof Error ? error.stack || error.message : String(error) }
  finally { facade.geometry.dispose(); await new Promise((resolve) => setTimeout(resolve, 0)); const capabilities = facade.geometry.capabilities(); report.afterRelease = { shapeCount: capabilities.shapeCount, kernelReferenceCount: capabilities.kernelReferenceCount }; if (capabilities.shapeCount !== 0 || capabilities.kernelReferenceCount !== 0) { report.status = 'failed'; report.error = `${report.error ? `${report.error} ` : ''}Shape ownership gate failed.` } }
  return report
}

run().then((report) => { ;(window as Window & { __bitbybitBimReport?: BimReport }).__bitbybitBimReport = 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 {}
