/**
 * Web Worker that loads the calcpad-wasm module and evaluates sheets
 * off the main thread. Communication is via structured postMessage.
 *
 * The WASM module is expected at `/wasm/calcpad_wasm_bg.wasm` with
 * its JS glue at `/wasm/calcpad_wasm.js` (output of wasm-pack).
 *
 * If the WASM module is not available (e.g., during development without
 * a WASM build), the worker falls back to a lightweight JS evaluator.
 */

import type { WorkerRequest, WorkerResponse, EngineLineResult } from './types.ts'

// ---------- WASM engine interface ----------

interface CalcpadWasm {
  evalSheet(lines: string[]): EngineLineResult[]
}

let engine: CalcpadWasm | null = null

// ---------- Fallback JS evaluator ----------

function fallbackEvalSheet(lines: string[]): EngineLineResult[] {
  return lines.map((line) => {
    const trimmed = line.trim()

    if (trimmed === '') {
      return { type: 'empty', display: '', rawValue: null, error: null }
    }

    if (trimmed.startsWith('//')) {
      return { type: 'comment', display: trimmed.slice(2).trim(), rawValue: null, error: null }
    }

    if (/^#{1,6}\s/.test(trimmed)) {
      return { type: 'text', display: trimmed, rawValue: null, error: null }
    }

    // Try to evaluate as a simple math expression
    try {
      // Strip inline comments
      const exprPart = trimmed.replace(/\/\/.*$/, '').trim()
      if (exprPart === '') {
        return { type: 'empty', display: '', rawValue: null, error: null }
      }

      // Handle variable assignments: "name = expr"
      const assignMatch = exprPart.match(/^([a-zA-Z_]\w*)\s*=\s*(.+)$/)
      const expr = assignMatch ? assignMatch[2] : exprPart

      // Very basic: try Function-based evaluation for simple arithmetic
      // This is a development fallback only; production uses the WASM engine.
      const sanitized = expr
        .replace(/\^/g, '**')
        .replace(/\bpi\b/gi, String(Math.PI))
        .replace(/\be\b/gi, String(Math.E))

      // Only allow safe characters for eval
      if (/^[\d\s+\-*/.()%,eE]+$/.test(sanitized)) {
        // eslint-disable-next-line no-new-func
        const result = new Function(`"use strict"; return (${sanitized})`)() as number
        if (typeof result === 'number' && isFinite(result)) {
          const display = Number.isInteger(result) ? result.toString() : parseFloat(result.toPrecision(10)).toString()
          return { type: 'number', display, rawValue: result, error: null }
        }
      }

      return { type: 'text', display: '', rawValue: null, error: null }
    } catch {
      return { type: 'text', display: '', rawValue: null, error: null }
    }
  })
}

// ---------- WASM loading ----------

async function initWasm(): Promise<boolean> {
  try {
    // Try to load the wasm-pack output
    const wasmPath = '/wasm/calcpad_wasm.js'
    const wasmModule = await import(/* @vite-ignore */ wasmPath) as {
      default: () => Promise<void>
      evalSheet: (lines: string[]) => EngineLineResult[]
    }
    await wasmModule.default()

    engine = {
      evalSheet: (lines: string[]) => wasmModule.evalSheet(lines),
    }

    return true
  } catch {
    // WASM not available; fallback mode
    console.warn('[calcpad-worker] WASM engine not found, using JS fallback')
    return false
  }
}

// ---------- Message handler ----------

function sendResponse(msg: WorkerResponse) {
  self.postMessage(msg)
}

self.onmessage = async (event: MessageEvent<WorkerRequest>) => {
  const msg = event.data

  switch (msg.kind) {
    case 'init': {
      const loaded = await initWasm()
      if (loaded) {
        sendResponse({ kind: 'ready' })
      } else {
        // Still report ready — we have the fallback
        sendResponse({ kind: 'ready' })
      }
      break
    }

    case 'evalSheet': {
      try {
        const results = engine
          ? engine.evalSheet(msg.lines)
          : fallbackEvalSheet(msg.lines)
        sendResponse({ kind: 'evalResult', id: msg.id, results })
      } catch (err) {
        const message = err instanceof Error ? err.message : String(err)
        sendResponse({ kind: 'evalError', id: msg.id, error: message })
      }
      break
    }
  }
}
