/// <reference lib="webworker" />

import type { NativeOcctHistoryStepProvider } from './nativeHistoryProvider'
import { nativeNamingCapabilitiesForModule, type NativeOcctHistoryCapabilities, type NativeOcctHistoryRequest, type NativeOcctHistoryProtocolResponse } from './nativeHistoryProtocol'
import { captureFreeCadPrivateNamingEvidence, createFreeCadPrivateNamingAbiRequest } from './nativeNamingAbi'

type WorkerRequest = { type: 'initialize'; moduleUrl: string } | { type: 'capture'; request: NativeOcctHistoryRequest } | { type: 'cancel'; requestId: string } | { type: 'dispose' }
type WorkerResponse = { type: 'ready'; capabilities: NativeOcctHistoryCapabilities } | { type: 'response'; response: NativeOcctHistoryProtocolResponse } | { type: 'error'; requestId?: string; error: string }

const scope = self as DedicatedWorkerGlobalScope
let provider: NativeOcctHistoryStepProvider | null = null
const cancelled = new Set<string>()

const send = (message: WorkerResponse) => scope.postMessage(message)
const operations: NativeOcctHistoryCapabilities['operations'] = ['fuse', 'cut', 'common', 'rotate', 'pad', 'pocket', 'loft', 'pipe', 'revolution', 'groove', 'fillet', 'chamfer', 'hole', 'draft', 'thickness', 'linear-pattern', 'polar-pattern', 'mirrored', 'multi-transform']
const capabilitiesFor = (module: NativeOcctHistoryStepProvider, occtVersion = module.occtVersion()): NativeOcctHistoryCapabilities => ({
  providerId: 'occt-native.history-step',
  providerVersion: '8.0.0-embind',
  occtVersion,
  availability: 'available',
  operations: [...operations],
  transport: 'step-text',
  naming: nativeNamingCapabilitiesForModule(module),
})

const captureMultiTransform = (module: NativeOcctHistoryStepProvider, request: NativeOcctHistoryRequest) => {
  if (request.transforms && request.transforms.length >= 2) return module.multiTransformHistoryFromStep?.(request.objectStep, request.transforms)
  if (request.transformKind === 'linear') return module.linearPatternHistoryFromStep?.(request.objectStep, ...(request.direction || [0, 0, 0]))
  if (request.transformKind === 'polar') return module.polarPatternHistoryFromStep?.(request.objectStep, ...(request.axisOrigin || [0, 0, 0]), ...(request.direction || [0, 0, 0]), request.angle || 0)
  if (request.transformKind === 'mirrored') return module.mirroredHistoryFromStep?.(request.objectStep, ...(request.axisOrigin || [0, 0, 0]), ...(request.direction || [0, 0, 0]))
}

const initialize = async (moduleUrl: string) => {
  const imported = await import(/* @vite-ignore */ moduleUrl) as { default?: () => Promise<NativeOcctHistoryStepProvider> }
  if (typeof imported.default !== 'function') throw new Error('Native OCCT history module has no default Emscripten factory export.')
  provider = await imported.default()
  send({
    type: 'ready',
    capabilities: capabilitiesFor(provider),
  })
}

scope.onmessage = ({ data }: MessageEvent<WorkerRequest>) => {
  void (async () => {
    try {
      if (data.type === 'initialize') return await initialize(data.moduleUrl)
      if (data.type === 'dispose') { provider = null; scope.close(); return }
      if (data.type === 'cancel') { cancelled.add(data.requestId); return }
      if (!provider) throw new Error('Native OCCT history Worker is not initialized.')
      if (cancelled.delete(data.request.requestId)) return
      const request = data.request
      const history = request.operation === 'rotate'
        ? provider.rotateHistoryFromStep?.(request.objectStep, ...(request.axisOrigin || [0, 0, 0]), ...(request.direction || [0, 0, 0]), request.angle || 0)
        : request.operation === 'pad'
        ? provider.prismHistoryFromStep?.(request.objectStep, ...(request.direction || [0, 0, 0]))
        : request.operation === 'pocket'
          ? provider.pocketHistoryFromStep?.(request.objectStep, request.toolStep || '', ...(request.direction || [0, 0, 0]))
          : request.operation === 'loft'
            ? provider.loftHistoryFromStep?.(request.objectStep, request.toolStep || '', request.ruled === true)
          : request.operation === 'pipe'
            ? provider.pipeHistoryFromStep?.(request.objectStep, request.toolStep || '')
          : request.operation === 'revolution'
            ? provider.revolutionHistoryFromStep?.(request.objectStep, ...(request.axisOrigin || [0, 0, 0]), ...(request.direction || [0, 0, 0]), request.angle || 0)
              : request.operation === 'groove'
                ? provider.grooveHistoryFromStep?.(request.objectStep, request.toolStep || '', ...(request.axisOrigin || [0, 0, 0]), ...(request.direction || [0, 0, 0]), request.angle || 0)
                : request.operation === 'fillet'
                  ? provider.filletHistoryFromStep?.(request.objectStep, request.radius || 0)
                  : request.operation === 'chamfer'
                    ? provider.chamferHistoryFromStep?.(request.objectStep, request.distance || 0)
                  : request.operation === 'hole'
                      ? provider.holeHistoryFromStep?.(request.objectStep, request.radius || 0, request.depth || 0, ...(request.position || [0, 0, 0]), ...(request.direction || [0, 0, 0]))
                      : request.operation === 'draft'
                        ? provider.draftHistoryFromStep?.(request.objectStep, request.faceIndex || 0, request.angle || 0, ...(request.direction || [0, 0, 0]), ...(request.axisOrigin || [0, 0, 0]), ...(request.neutralPlaneDirection || [0, 0, 0]), request.reversed === true)
                        : request.operation === 'thickness'
                          ? provider.thicknessHistoryFromStep?.(request.objectStep, request.faceIndex || 0, request.offset || 0, request.joinType === 'Intersection')
                          : request.operation === 'linear-pattern'
                            ? provider.linearPatternHistoryFromStep?.(request.objectStep, ...(request.direction || [0, 0, 0]))
                            : request.operation === 'polar-pattern'
                              ? provider.polarPatternHistoryFromStep?.(request.objectStep, ...(request.axisOrigin || [0, 0, 0]), ...(request.direction || [0, 0, 0]), request.angle || 0)
                              : request.operation === 'mirrored'
                                ? provider.mirroredHistoryFromStep?.(request.objectStep, ...(request.axisOrigin || [0, 0, 0]), ...(request.direction || [0, 0, 0]))
                                : request.operation === 'multi-transform'
                                  ? captureMultiTransform(provider, request)
                  : provider.booleanHistoryFromStep(request.objectStep, request.toolStep || '', request.operation)
      if (!history) throw new Error('Native OCCT history provider does not expose the requested feature operation.')
      if (cancelled.delete(data.request.requestId)) return
      const orderedStages = [...(request.stages ?? [])].sort((left, right) => left.ordinal - right.ordinal)
      const finalStage = orderedStages.at(-1)
      const stageId = finalStage?.resultStageId ?? finalStage?.stageId ?? `${request.operationId}:stage:0`
      const namingEvidence = captureFreeCadPrivateNamingEvidence(provider, createFreeCadPrivateNamingAbiRequest({
        requestId: request.requestId,
        documentId: request.documentId,
        documentVersion: request.documentVersion,
        operationId: request.operationId,
        operation: request.operation,
        stageId,
        resultObjectId: request.resultObjectId ?? request.operationId,
        ...(request.resultObjectTag === undefined ? {} : { resultObjectTag: request.resultObjectTag }),
        inputs: (request.inputs ?? [{ inputId: 'object', role: 'object', step: request.objectStep }, ...(request.toolStep ? [{ inputId: 'tool', role: 'tool', step: request.toolStep }] : [])]).map(({ inputId, objectId, role, stageId: inputStageId, step, objectTag, namingEvidence }) => ({ inputId, step, ...(objectId ? { objectId } : {}), ...(role ? { role } : {}), ...(inputStageId ? { stageId: inputStageId } : {}), ...(objectTag === undefined ? {} : { objectTag }), ...(namingEvidence ? { namingEvidence } : {}) })),
        stages: orderedStages.map((stage) => ({ ...stage, inputIds: [...stage.inputIds] })),
        ...(history.resultStep ? { resultStep: history.resultStep } : {}),
        ...(history.resultBrep ? { resultBrep: history.resultBrep } : {}),
        ...(request.resultStepByStage ? { resultStepByStage: { ...request.resultStepByStage } } : {}),
        history,
      }))
      send({ type: 'response', response: { protocolVersion: request.protocolVersion, requestId: request.requestId, documentId: request.documentId, documentVersion: request.documentVersion, operationId: request.operationId, provider: capabilitiesFor(provider, history.occtVersion), history: namingEvidence ? { ...history, namingEvidence } : history } })
    } catch (error) {
      send({ type: 'error', requestId: data.type === 'capture' ? data.request.requestId : undefined, error: error instanceof Error ? error.message : String(error) })
    }
  })()
}

export {}
