/**
 * Main-thread wrapper around encodeImage.worker.ts.
 * Spawns module Web Worker, relays progress, resolves with full bundle.
 * Moved from src/features/admin-deals/ to shared lib.
 */

import { listVariantFiles, type VariantFile } from '@/lib/imageVariants';

export interface EncodedVariant {
  name: string;
  blob: Blob;
  contentType: string;
}

export interface EncodedBundle {
  sha256: string;
  original: Blob;
  variants: EncodedVariant[];
}

export interface EncodeProgress {
  phase: 'hashing' | 'decoding' | 'encoding' | 'done';
  variantIndex?: number;
  variantTotal?: number;
}

export class EncodeError extends Error {
  constructor(
    public readonly code: 'invalid-mime' | 'decode-failed' | 'worker-failed' | 'aborted',
    message: string,
  ) {
    super(message);
    this.name = 'EncodeError';
  }
}

interface WorkerMessage {
  type: 'progress' | 'done' | 'error';
  phase?: EncodeProgress['phase'];
  variantIndex?: number;
  variantTotal?: number;
  sha256?: string;
  original?: Blob;
  variants?: EncodedVariant[];
  message?: string;
}

export async function encodeImage(
  file: File,
  onProgress?: (p: EncodeProgress) => void,
  signal?: AbortSignal,
  variants?: VariantFile[],
): Promise<EncodedBundle> {
  const resolvedVariants = variants ?? listVariantFiles();

  const worker = new Worker(new URL('./encodeImage.worker.ts', import.meta.url), {
    type: 'module',
  });

  return new Promise<EncodedBundle>((resolve, reject) => {
    const cleanup = () => {
      worker.terminate();
      signal?.removeEventListener('abort', onAbort);
    };

    const onAbort = () => {
      cleanup();
      reject(new EncodeError('aborted', 'encode aborted'));
    };
    signal?.addEventListener('abort', onAbort);

    worker.onmessage = (ev: MessageEvent<WorkerMessage>) => {
      const msg = ev.data;
      if (msg.type === 'progress' && msg.phase) {
        onProgress?.({
          phase: msg.phase,
          variantIndex: msg.variantIndex,
          variantTotal: msg.variantTotal,
        });
      } else if (msg.type === 'done') {
        cleanup();
        if (msg.sha256 && msg.original && msg.variants) {
          resolve({ sha256: msg.sha256, original: msg.original, variants: msg.variants });
        } else {
          reject(new EncodeError('worker-failed', 'worker returned incomplete bundle'));
        }
      } else if (msg.type === 'error') {
        cleanup();
        reject(new EncodeError('decode-failed', msg.message ?? 'unknown encode error'));
      }
    };

    worker.onerror = (ev: ErrorEvent) => {
      cleanup();
      reject(new EncodeError('worker-failed', ev.message || 'worker crashed'));
    };

    file
      .arrayBuffer()
      .then((buffer) => {
        worker.postMessage({ buffer, mime: file.type, variants: resolvedVariants }, [buffer]);
      })
      .catch((err: unknown) => {
        cleanup();
        reject(new EncodeError('decode-failed', err instanceof Error ? err.message : String(err)));
      });
  });
}
