import { createMockFacade } from './facade/mockFacade'
import { createMeshDocument } from './facade/mesh'

type MeshReport = { schemaVersion: 1; status: 'pass' | 'failed'; browserId: 'chrome'; crossOriginIsolated: boolean; geometry?: { sourceShape: string; vertices: number; triangles: number; boundaryEdges: number; nonManifoldEdges: number; degenerateTriangles: number; selfIntersections: number; surfaceArea: number }; operations?: { welded: number; transformedMin: [number, number, number]; objBytes: number; objHash: string; plyBytes: number; stlBytes: number; lodTriangles: number; workerLodTriangles: number }; repairs?: { selfIntersectionFixture: number; nonManifoldFixture: number; holeFilledTriangles: number }; persistence?: { mode: string; byteLength: number; hash: string; roundTrip: boolean; released: boolean }; opfs?: { markerSuite: string; triangles: 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 lodInWorker = async (positions: number[], indices: number[], targetTriangles: number) => {
  const worker = new Worker(new URL('./meshLodWorker.ts', import.meta.url), { type: 'module', name: 'mesh-lod' })
  try { return await new Promise<{ level: number; vertices: number; triangles: number; indices: number[] }>((resolve, reject) => { const timer = setTimeout(() => reject(new Error('Mesh LOD Worker timed out.')), 10_000); worker.addEventListener('message', (event: MessageEvent<{ lod?: { level: number; vertices: number; triangles: number; indices: number[] }; error?: string }>) => { clearTimeout(timer); event.data.lod ? resolve(event.data.lod) : reject(new Error(event.data.error ?? 'Mesh LOD Worker failed.')) }, { once: true }); worker.addEventListener('error', (event) => { clearTimeout(timer); reject(new Error(event.message)) }, { once: true }); worker.postMessage({ positions, indices, targetTriangles }) }) } finally { worker.terminate() }
}

const run = async (): Promise<MeshReport> => {
  const facade = createMockFacade()
  const report: MeshReport = { schemaVersion: 1, status: 'failed', browserId: 'chrome', crossOriginIsolated: globalThis.crossOriginIsolated === true }
  try {
    if (!report.crossOriginIsolated || !navigator.storage?.getDirectory) throw new Error('Chrome Mesh evidence requires isolated OPFS.')
    await facade.project.list()
    const shape = await facade.geometry.createBox({ documentId: 'mesh', documentVersion: 1, width: 10, length: 8, height: 4 })
    const source = await facade.geometry.mesh(shape, 0.05)
    const mesh = createMeshDocument('mesh', 'Box mesh', source)
    const before = mesh.analyze()
    mesh.weldVertices(1e-5)
    mesh.removeDegenerate()
    const quality = mesh.analyze()
    mesh.transform({ translation: [1, 2, 3], scale: 1.5 })
    const obj = new TextEncoder().encode(mesh.exportObj())
    const ply = new TextEncoder().encode(mesh.exportPly())
    const stl = new TextEncoder().encode(mesh.exportStl())
    const lod = mesh.lod(Math.max(1, Math.floor(quality.triangles / 2)))
    const workerLod = await lodInWorker(Array.from(source.positions), Array.from(source.indices), Math.max(1, Math.floor(quality.triangles / 2)))
    const cross = createMeshDocument('cross', 'Cross', { positions: [-1, -1, 0, 1, 1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0], indices: [0, 1, 2, 3, 4, 5] })
    const nonManifold = createMeshDocument('non-manifold', 'Non manifold', { positions: [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1], indices: [0, 1, 2, 0, 1, 3, 0, 1, 4] })
    const hole = createMeshDocument('hole', 'Hole', { positions: [0, 0, 0, 1, 0, 0, 0, 1, 0], indices: [] }); hole.fillBoundaryTriangle([0, 1], 2)
    const objHash = await sha256(obj)
    const snapshot = mesh.snapshot()
    const payload = new TextEncoder().encode(JSON.stringify({ ...snapshot, vertices: snapshot.vertices, triangles: snapshot.triangles }))
    const stored = await facade.project.resource.put(payload, 'text/plain')
    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 = 'mesh-chrome-marker.json'
    const marker = await directory.getFileHandle(markerName, { create: true })
    const writable = await marker.createWritable(); await writable.write(JSON.stringify({ suite: 'MESH-CORE', triangles: quality.triangles })); await writable.close()
    const markerPayload = JSON.parse(await (await marker.getFile()).text()) as { suite: string; triangles: number }
    await directory.removeEntry(markerName)
    let markerRemoved = false
    try { await directory.getFileHandle(markerName) } catch (error) { markerRemoved = error instanceof DOMException && error.name === 'NotFoundError' }
    report.geometry = { sourceShape: shape.id, vertices: quality.vertices, triangles: quality.triangles, boundaryEdges: quality.boundaryEdges, nonManifoldEdges: quality.nonManifoldEdges, degenerateTriangles: quality.degenerateTriangles, selfIntersections: quality.selfIntersections, surfaceArea: quality.surfaceArea }
    report.operations = { welded: before.vertices - quality.vertices, transformedMin: mesh.analyze().bounds.min, objBytes: obj.byteLength, objHash, plyBytes: ply.byteLength, stlBytes: stl.byteLength, lodTriangles: lod.triangles, workerLodTriangles: workerLod.triangles }
    report.repairs = { selfIntersectionFixture: cross.detectSelfIntersections(), nonManifoldFixture: nonManifold.analyze().nonManifoldEdges, holeFilledTriangles: hole.analyze().triangles }
    report.persistence = { mode: facade.project.capabilities().mode, byteLength: stored.byteLength, hash: stored.hash, roundTrip, released }
    report.opfs = { markerSuite: markerPayload.suite, triangles: markerPayload.triangles, markerRemoved }
    report.status = report.geometry.vertices > 0 && report.geometry.triangles > 0 && report.geometry.degenerateTriangles === 0 && report.geometry.boundaryEdges === 0 && report.geometry.nonManifoldEdges === 0 && report.geometry.selfIntersections === 0 && report.operations.welded > 0 && JSON.stringify(report.operations.transformedMin) === JSON.stringify([-6.5, -1, -3]) && report.operations.objBytes > 0 && report.operations.plyBytes > 0 && report.operations.stlBytes > 0 && report.operations.lodTriangles === report.operations.workerLodTriangles && report.repairs.selfIntersectionFixture === 1 && report.repairs.nonManifoldFixture === 1 && report.repairs.holeFilledTriangles === 1 && report.persistence.mode === 'sqlite-opfs' && report.persistence.roundTrip && report.persistence.released && report.opfs.markerSuite === 'MESH-CORE' && report.opfs.triangles === report.geometry.triangles && 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 & { __bitbybitMeshReport?: MeshReport }).__bitbybitMeshReport = 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 {}
