import { createMockFacade } from './facade/mockFacade'
import { createDraftDocument } from './facade/draft'
import { createPlot } from './facade/plot'
import { createProductionDocument } from './facade/productionDocument'
import { createSpreadsheet } from './facade/spreadsheet'
import { createTechDrawPage } from './facade/techDraw'
import type { TopoRefValue } from './facade/types'

type ProductionReport = {
  schemaVersion: 1
  status: 'pass' | 'failed'
  browserId: 'chrome'
  crossOriginIsolated: boolean
  closure?: { complete: boolean; artifacts: number; missingKinds: number; missingDependencies: number; cycles: number; documentVersion: number; reopenedComplete: boolean; artifactVersions: number[] }
  workflow?: {
    created: { spreadsheetCells: number; draftObjects: number; techdrawViews: number; plotSeries: number }
    edited: { spreadsheetValue: number; draftStartX: number; techdrawDimension: number; plotValue: number }
    exports: { spreadsheetCsvBytes: number; draftJsonBytes: number; techdrawSvgBytes: number; techdrawPdfBytes: number; techdrawPdfValid: boolean; plotSvgBytes: number; plotCsvBytes: number }
  }
  persistence?: { mode: string; byteLength: number; hash: string; roundTrip: boolean; released: boolean }
  opfs?: { markerSuite: string; artifacts: 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 ref = (id: string, version: number): TopoRefValue => ({ schemaVersion: 1, objectId: 'draft-line', kind: 'vertex', persistentId: id, topologyVersion: version, generation: version, status: 'stable', signature: id })

const run = async (): Promise<ProductionReport> => {
  const facade = createMockFacade()
  const report: ProductionReport = { schemaVersion: 1, status: 'failed', browserId: 'chrome', crossOriginIsolated: globalThis.crossOriginIsolated === true }
  try {
    if (!report.crossOriginIsolated || !navigator.storage?.getDirectory) throw new Error('Chrome production document evidence requires isolated OPFS.')
    await facade.project.list()
    const document = createProductionDocument('doc', 'Production workflow')

    const sheet = createSpreadsheet('sheet', 'Parameters')
    sheet.setCell('A1', 2, { alias: 'Input' })
    sheet.setCell('A2', '=Input * 2', { alias: 'Output' })
    const initialSheet = sheet.evaluate()
    document.add({ id: 'sheet', kind: 'Spreadsheet', dependencies: [], payload: { snapshot: sheet.snapshot(), evaluation: initialSheet } })

    const draft = createDraftDocument('draft', 'Profile')
    draft.createLine('profile', { x: 0, y: 0 }, { x: 4, y: 0 })
    document.add({ id: 'draft', kind: 'Draft', dependencies: [], payload: { snapshot: draft.snapshot() } })

    const page = createTechDrawPage('page', 'Profile drawing')
    page.addSource({ id: 'draft-line', revision: 1, points: [{ id: 'start', ref: ref('Vertex1', 1), point: { x: 0, y: 0, z: 0 } }, { id: 'end', ref: ref('Vertex2', 1), point: { x: 4, y: 0, z: 0 } }], edges: [{ id: 'edge', start: 'start', end: 'end' }] })
    page.addView({ id: 'front', sourceId: 'draft-line', direction: { x: 0, y: 0, z: 1 }, position: { x: 80, y: 80 }, scale: 8 })
    page.addDimension({ id: 'length', sourceId: 'draft-line', first: ref('Vertex1', 1), second: ref('Vertex2', 1), label: 'Profile length' })
    page.addAnnotation({ id: 'note', text: 'Production profile', position: { x: 20, y: 20 } })
    page.addGeometricTolerance({ id: 'straightness', sourceId: 'draft-line', reference: ref('Vertex1', 1), characteristic: 'flatness', value: 0.05 })
    document.add({ id: 'page', kind: 'TechDraw', dependencies: ['draft'], payload: { snapshot: page.snapshot() } })

    const plot = createPlot('plot', 'Parameter history')
    plot.setAxes({ x: { label: 'Revision', scale: 'linear', minimum: 2, maximum: 3 }, y: { label: 'Output', scale: 'linear', minimum: 0, maximum: 8 } })
    plot.setSeries({ id: 'output', label: 'Output', points: [{ x: 2, y: 4 }, { x: 3, y: 4 }], style: { color: '#007f86', lineWidth: 2 } })
    plot.bindSeries('output', { sourceId: 'sheet', xRange: 'A1:A2', yRange: 'A1:A2' })
    document.add({ id: 'plot', kind: 'Plot', dependencies: ['sheet'], payload: { snapshot: plot.snapshot() } })

    sheet.setCell('A1', 3, { alias: 'Input' })
    const editedSheet = sheet.evaluate()
    const spreadsheetValue = editedSheet.cells.A2.value?.value ?? Number.NaN
    const movedLine = draft.move('profile', { x: 1, y: 2 })
    page.updateSource({ id: 'draft-line', revision: 2, points: [{ id: 'start', ref: ref('Vertex1', 2), point: { x: 1, y: 2, z: 0 } }, { id: 'end', ref: ref('Vertex2', 2), point: { x: spreadsheetValue, y: 2, z: 0 } }], edges: [{ id: 'edge', start: 'start', end: 'end' }] })
    plot.updateBoundSeries('output', [{ x: 2, y: 4 }, { x: 3, y: spreadsheetValue }])

    const spreadsheetCsv = sheet.exportCsv()
    const draftJson = JSON.stringify(draft.snapshot())
    const techdrawSvg = page.exportSvg()
    const techdrawPdf = page.exportPdf()
    const plotSvg = plot.exportSvg()
    const plotCsv = plot.exportCsv()
    const techdrawPdfText = new TextDecoder().decode(techdrawPdf)

    document.update('sheet', { payload: { snapshot: sheet.snapshot(), evaluation: editedSheet, exportBytes: new TextEncoder().encode(spreadsheetCsv).byteLength } })
    document.update('draft', { payload: { snapshot: draft.snapshot(), exportBytes: new TextEncoder().encode(draftJson).byteLength } })
    document.update('page', { payload: { snapshot: page.snapshot(), svgBytes: new TextEncoder().encode(techdrawSvg).byteLength, pdfBytes: techdrawPdf.byteLength } })
    document.update('plot', { payload: { snapshot: plot.snapshot(), svgBytes: new TextEncoder().encode(plotSvg).byteLength, csvBytes: new TextEncoder().encode(plotCsv).byteLength } })

    const closure = document.closure()
    const serialized = document.save()
    const reopened = createProductionDocument('reopened', 'Reopened production workflow')
    const reopenedSnapshot = reopened.load(serialized)
    const reopenedClosure = reopened.closure()
    const payload = new TextEncoder().encode(serialized)
    const stored = await facade.project.resource.put(payload, 'application/vnd.bitbybit.production-document+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 = 'production-document-chrome-marker.json'
    const marker = await directory.getFileHandle(markerName, { create: true })
    const writable = await marker.createWritable()
    await writable.write(JSON.stringify({ suite: 'DOC-CLOSURE', artifacts: closure.artifacts }))
    await writable.close()
    const markerPayload = JSON.parse(await (await marker.getFile()).text()) as { suite: string; artifacts: number }
    await directory.removeEntry(markerName)
    let markerRemoved = false
    try { await directory.getFileHandle(markerName) } catch (error) { markerRemoved = error instanceof DOMException && error.name === 'NotFoundError' }

    const pageSnapshot = page.snapshot()
    const plotSnapshot = plot.snapshot()
    report.closure = { complete: closure.complete, artifacts: closure.artifacts, missingKinds: closure.missingKinds.length, missingDependencies: closure.missingDependencies.length, cycles: closure.cycles.length, documentVersion: document.snapshot().version, reopenedComplete: reopenedClosure.complete && reopenedSnapshot.artifacts.length === 4, artifactVersions: document.snapshot().artifacts.map((artifact) => artifact.version).sort() }
    report.workflow = {
      created: { spreadsheetCells: 2, draftObjects: 1, techdrawViews: 1, plotSeries: 1 },
      edited: { spreadsheetValue, draftStartX: movedLine.kind === 'line' ? movedLine.start.x : Number.NaN, techdrawDimension: pageSnapshot.dimensions[0]?.value ?? Number.NaN, plotValue: plotSnapshot.series[0]?.points.at(-1)?.y ?? Number.NaN },
      exports: { spreadsheetCsvBytes: new TextEncoder().encode(spreadsheetCsv).byteLength, draftJsonBytes: new TextEncoder().encode(draftJson).byteLength, techdrawSvgBytes: new TextEncoder().encode(techdrawSvg).byteLength, techdrawPdfBytes: techdrawPdf.byteLength, techdrawPdfValid: techdrawPdfText.startsWith('%PDF-1.4') && techdrawPdfText.endsWith('%%EOF\n'), plotSvgBytes: new TextEncoder().encode(plotSvg).byteLength, plotCsvBytes: new TextEncoder().encode(plotCsv).byteLength },
    }
    report.persistence = { mode: facade.project.capabilities().mode, byteLength: stored.byteLength, hash: stored.hash, roundTrip, released }
    report.opfs = { markerSuite: markerPayload.suite, artifacts: markerPayload.artifacts, markerRemoved }
    report.status = report.closure.complete && report.closure.artifacts === 4 && report.closure.missingKinds === 0 && report.closure.missingDependencies === 0 && report.closure.cycles === 0 && report.closure.documentVersion === 8 && report.closure.reopenedComplete && report.closure.artifactVersions.every((version) => version === 2)
      && report.workflow.created.spreadsheetCells === 2 && report.workflow.created.draftObjects === 1 && report.workflow.created.techdrawViews === 1 && report.workflow.created.plotSeries === 1
      && report.workflow.edited.spreadsheetValue === 6 && report.workflow.edited.draftStartX === 1 && report.workflow.edited.techdrawDimension === 5 && report.workflow.edited.plotValue === 6
      && Object.entries(report.workflow.exports).every(([name, value]) => name === 'techdrawPdfValid' ? value === true : typeof value === 'number' && value > 0)
      && report.persistence.mode === 'sqlite-opfs' && report.persistence.roundTrip && report.persistence.released && report.opfs.markerSuite === 'DOC-CLOSURE' && report.opfs.artifacts === 4 && 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 & { __bitbybitProductionReport?: ProductionReport }).__bitbybitProductionReport = 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 {}
