/**
 * Browser-side Tesseract OCR for Layer-1 card name strip recognition.
 * Uses tesseract.js workers (off main thread). Language data loads from CDN on first use.
 */

let workerPromise = null;

const NAME_STRIP_PSM = '7';
const NUMBER_STRIP_PSM = '7';
const NAME_STRIP_HEIGHT_RATIO = 0.35;
const NUMBER_STRIP_HEIGHT_RATIO = 0.18;
const OCR_JPEG_QUALITY = 0.92;

async function getWorker() {
  if (!workerPromise) {
    workerPromise = (async () => {
      const { createWorker } = await import('tesseract.js');
      const worker = await createWorker('eng', 1, {
        logger: () => {},
      });
      return worker;
    })();
  }
  return workerPromise;
}

function cropCardStrip(imageDataUrl, { fromTop = true, heightRatio }) {
  return new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => {
      const stripHeight = Math.max(1, Math.floor(img.height * heightRatio));
      const canvas = document.createElement('canvas');
      canvas.width = img.width;
      canvas.height = stripHeight;
      const ctx = canvas.getContext('2d');
      const sourceY = fromTop ? 0 : Math.max(0, img.height - stripHeight);
      ctx.drawImage(
        img,
        0,
        sourceY,
        img.width,
        stripHeight,
        0,
        0,
        canvas.width,
        canvas.height
      );
      resolve(canvas.toDataURL('image/jpeg', OCR_JPEG_QUALITY));
    };
    img.onerror = () => reject(new Error('Failed to load image for OCR'));
    img.src = imageDataUrl;
  });
}

/**
 * Pick the most likely collector number token from OCR of the bottom strip.
 * @param {string} rawText
 * @returns {string}
 */
export function extractCollectorNumberCandidate(rawText) {
  if (!rawText || typeof rawText !== 'string') return '';

  const lines = rawText
    .split(/\r?\n/)
    .map((line) => line.replace(/\s+/g, ' ').trim())
    .filter(Boolean);

  const joined = lines.join(' ');
  const slashMatch = joined.match(/\b(\d{1,4}\s*\/\s*\d{1,4}[A-Za-z]?)\b/);
  if (slashMatch) {
    return slashMatch[1].replace(/\s+/g, '');
  }

  for (let i = lines.length - 1; i >= 0; i -= 1) {
    const line = lines[i];
    if (/\d/.test(line) && line.length <= 12) {
      return line;
    }
  }

  return '';
}

/**
 * OCR the top name strip of a card crop.
 * @param {string} imageDataUrl - full card crop data URL
 * @returns {Promise<{ text: string, confidence: number }>}
 */
export async function recognizeCardNameStrip(imageDataUrl) {
  const fields = await recognizeCardFields(imageDataUrl);
  return {
    text: fields.nameText,
    confidence: fields.nameConfidence,
  };
}

/**
 * OCR name strip + bottom collector-number strip.
 * @param {string} imageDataUrl
 * @returns {Promise<{ nameText: string, nameConfidence: number, cardNumber: string, numberConfidence: number }>}
 */
export async function recognizeCardFields(imageDataUrl) {
  if (typeof window === 'undefined') {
    return { nameText: '', nameConfidence: 0, cardNumber: '', numberConfidence: 0 };
  }

  try {
    const [nameStripUrl, numberStripUrl] = await Promise.all([
      cropCardStrip(imageDataUrl, { fromTop: true, heightRatio: NAME_STRIP_HEIGHT_RATIO }),
      cropCardStrip(imageDataUrl, { fromTop: false, heightRatio: NUMBER_STRIP_HEIGHT_RATIO }),
    ]);

    const worker = await getWorker();
    await worker.setParameters({ tessedit_pageseg_mode: NAME_STRIP_PSM });
    const nameResult = await worker.recognize(nameStripUrl);

    await worker.setParameters({ tessedit_pageseg_mode: NUMBER_STRIP_PSM });
    const numberResult = await worker.recognize(numberStripUrl);

    const cardNumber = extractCollectorNumberCandidate(numberResult.data.text || '');

    return {
      nameText: (nameResult.data.text || '').trim(),
      nameConfidence: Math.round(nameResult.data.confidence || 0),
      cardNumber,
      numberConfidence: Math.round(numberResult.data.confidence || 0),
    };
  } catch (error) {
    console.warn('[ocr-worker] recognizeCardFields failed:', error);
    return { nameText: '', nameConfidence: 0, cardNumber: '', numberConfidence: 0 };
  }
}

export async function terminateOcrWorker() {
  if (!workerPromise) return;
  try {
    const worker = await workerPromise;
    await worker.terminate();
  } catch {
    // ignore teardown errors
  }
  workerPromise = null;
}
