import { createMockFacade } from './facade/mockFacade'
import { createSecondaryFormats, type SecondaryFormat } from './facade/secondaryFormats'

type FormatEvidence = { format: SecondaryFormat; category: string; byteLength: number; imported: boolean; rejectedInvalid: boolean; byteExactRoundTrip: boolean; deterministic: boolean; proxy: boolean }
type SecondaryReport = { schemaVersion: 1; status: 'pass' | 'failed'; browserId: 'chrome'; crossOriginIsolated: boolean; formats?: { descriptors: number; records: number; nativeExports: number; proxyImports: number; bytes: number; categories: string[]; matrix: FormatEvidence[] }; persistence?: { mode: string; byteLength: number; roundTrip: boolean; released: boolean }; opfs?: { markerSuite: string; records: 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 formats: SecondaryFormat[] = ['DXF', 'SVG', 'OBJ', 'PLY', 'STL', 'PDF', 'IFC', 'CSV']

const run = async (): Promise<SecondaryReport> => {
  const facade = createMockFacade()
  const report: SecondaryReport = { schemaVersion: 1, status: 'failed', browserId: 'chrome', crossOriginIsolated: globalThis.crossOriginIsolated === true }
  try {
    if (!report.crossOriginIsolated || !navigator.storage?.getDirectory) throw new Error('Chrome secondary format evidence requires isolated OPFS.')
    await facade.project.list()
    const api = createSecondaryFormats()
    const descriptorByFormat = new Map(api.descriptors().map((entry) => [entry.format, entry]))
    const invalid = new TextEncoder().encode('invalid secondary format payload')
    const matrix: FormatEvidence[] = []
    let bytesTotal = 0
    for (const format of formats) {
      const id = format.toLowerCase()
      const bytes = api.exportBytes(format, { label: 'Chrome format fixture' })
      const second = api.exportBytes(format, { label: 'Chrome format fixture' })
      bytesTotal += bytes.byteLength
      const imported = api.importBytes({ id, format, bytes })
      const exported = api.exportRecord(id)
      let rejectedInvalid = false
      try { api.importBytes({ id: `invalid-${id}`, format, bytes: invalid }) } catch (error) { rejectedInvalid = error instanceof RangeError && /signature/.test(error.message) }
      const descriptor = descriptorByFormat.get(format)
      if (!descriptor) throw new Error(`${format} descriptor is missing.`)
      matrix.push({ format, category: descriptor.category, byteLength: bytes.byteLength, imported: imported.detected, rejectedInvalid, byteExactRoundTrip: await sha256(exported) === await sha256(bytes), deterministic: await sha256(second) === await sha256(bytes), proxy: imported.proxy })
    }
    const snapshot = api.snapshot()
    const payload = new TextEncoder().encode(JSON.stringify(snapshot))
    const stored = await facade.project.resource.put(payload, 'application/vnd.bitbybit.secondary-formats+json')
    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 = 'secondary-formats-chrome-marker.json'
    const marker = await directory.getFileHandle(markerName, { create: true })
    const writable = await marker.createWritable()
    await writable.write(JSON.stringify({ suite: 'FC-09', records: snapshot.records.length }))
    await writable.close()
    const markerPayload = JSON.parse(await (await marker.getFile()).text()) as { suite: string; records: number }
    await directory.removeEntry(markerName)
    let markerRemoved = false
    try { await directory.getFileHandle(markerName) } catch (error) { markerRemoved = error instanceof DOMException && error.name === 'NotFoundError' }
    report.formats = { descriptors: snapshot.descriptors.length, records: snapshot.records.length, nativeExports: snapshot.descriptors.filter((entry) => entry.exportMode === 'native').length, proxyImports: snapshot.records.filter((entry) => entry.proxy).length, bytes: bytesTotal, categories: [...new Set(snapshot.descriptors.map((entry) => entry.category))].sort(), matrix }
    report.persistence = { mode: facade.project.capabilities().mode, byteLength: stored.byteLength, roundTrip, released }
    report.opfs = { markerSuite: markerPayload.suite, records: markerPayload.records, markerRemoved }
    report.status = report.formats.descriptors === formats.length && report.formats.records === formats.length && report.formats.nativeExports === 3 && report.formats.proxyImports === formats.length && JSON.stringify(report.formats.categories) === JSON.stringify(['2d', 'bim', 'data', 'mesh']) && report.formats.matrix.every((entry) => entry.imported && entry.rejectedInvalid && entry.byteExactRoundTrip && entry.deterministic && entry.proxy) && report.persistence.mode === 'sqlite-opfs' && report.persistence.roundTrip && report.persistence.released && report.opfs.markerSuite === 'FC-09' && report.opfs.records === formats.length && 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 & { __bitbybitSecondaryReport?: SecondaryReport }).__bitbybitSecondaryReport = 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 {}
