/**
 * Client-side image encoder Web Worker.
 *
 * Receives {buffer, mime, variants} from main thread, encodes variants
 * via jsquash WASM, posts progress + final bundle back. Keeps UI thread responsive.
 * Moved from src/features/admin-deals/ to shared lib.
 */

import { bytesToHex } from '../encoding.js';
import { variantFieldName, type VariantFile } from '../imageVariants';

type WorkerMessageIn = { buffer: ArrayBuffer; mime: string; variants: VariantFile[] };

interface WorkerMessageOut {
  type: 'progress' | 'done' | 'error';
  phase?: 'hashing' | 'decoding' | 'encoding' | 'done';
  variantIndex?: number;
  variantTotal?: number;
  sha256?: string;
  original?: Blob;
  variants?: Array<{ name: string; blob: Blob; contentType: string }>;
  message?: string;
}

function post(msg: WorkerMessageOut, transfer?: Transferable[]): void {
  (self as unknown as { postMessage: (m: unknown, t?: Transferable[]) => void }).postMessage(
    msg,
    transfer ?? [],
  );
}

async function sha256Hex(buf: ArrayBuffer): Promise<string> {
  const digest = await crypto.subtle.digest('SHA-256', buf);
  return bytesToHex(digest);
}

function resizeWithBitmap(
  bitmap: ImageBitmap,
  targetWidth: number,
  targetHeight: number,
): ImageData {
  const canvas = new OffscreenCanvas(targetWidth, targetHeight);
  const ctx = canvas.getContext('2d')!;
  ctx.imageSmoothingEnabled = true;
  ctx.imageSmoothingQuality = 'high';
  ctx.drawImage(bitmap, 0, 0, targetWidth, targetHeight);
  const imageData = ctx.getImageData(0, 0, targetWidth, targetHeight);
  return imageData;
}

self.onmessage = async (ev: MessageEvent<WorkerMessageIn>) => {
  try {
    const { buffer, mime, variants } = ev.data;

    const SUPPORTED = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif'];
    if (!SUPPORTED.includes(mime)) {
      post({ type: 'error', message: `Unsupported image type: ${mime}` });
      return;
    }

    post({ type: 'progress', phase: 'hashing' });
    const sha256 = await sha256Hex(buffer);

    post({ type: 'progress', phase: 'decoding' });
    const blob = new Blob([buffer], { type: mime });
    const bitmap = await createImageBitmap(blob);

    const [avifMod, avifWasmUrl, webpMod, webpWasmUrl] = await Promise.all([
      import('@jsquash/avif/encode'),

      import('@jsquash/avif/codec/enc/avif_enc.wasm?url') as Promise<{ default: string }>,

      import('@jsquash/webp/encode'),

      import('@jsquash/webp/codec/enc/webp_enc_simd.wasm?url') as Promise<{ default: string }>,
    ]);
    const [avifModule, webpModule] = await Promise.all([
      WebAssembly.compileStreaming(fetch(avifWasmUrl.default)),
      WebAssembly.compileStreaming(fetch(webpWasmUrl.default)),
    ]);
    await Promise.all([avifMod.init(avifModule), webpMod.init(webpModule)]);

    const encodedVariants: Array<{ name: string; blob: Blob; contentType: string }> = [];

    for (let i = 0; i < variants.length; i++) {
      const f = variants[i]!;
      post({
        type: 'progress',
        phase: 'encoding',
        variantIndex: i,
        variantTotal: variants.length,
      });

      const targetH = f.height ?? Math.round((bitmap.height / bitmap.width) * f.width);
      const resized = resizeWithBitmap(bitmap, f.width, targetH);
      let encoded: ArrayBuffer;
      let contentType: string;
      if (f.format === 'avif') {
        encoded = await avifMod.default(resized, { quality: 75 });
        contentType = 'image/avif';
      } else {
        encoded = await webpMod.default(resized, { quality: 80 });
        contentType = 'image/webp';
      }
      encodedVariants.push({
        name: variantFieldName(f as VariantFile),
        blob: new Blob([encoded], { type: contentType }),
        contentType,
      });
    }

    bitmap.close();
    const original = new Blob([buffer], { type: mime });
    post({ type: 'done', phase: 'done', sha256, original, variants: encodedVariants });
  } catch (err) {
    post({ type: 'error', message: err instanceof Error ? err.message : String(err) });
  }
};
