import test from 'node:test';
import assert from 'node:assert/strict';

import { OVERLAY_CAPTURE_SELECTORS, captureViewportBlob } from '../src/workflowCapture.js';

function makeElement(name, { tagName = 'DIV', width = 160, height = 90, src = '' } = {}) {
  return {
    name,
    tagName,
    src,
    currentSrc: src,
    complete: true,
    naturalWidth: 1,
    clientWidth: width,
    clientHeight: height,
    parentNode: null,
    getBoundingClientRect() {
      return { width, height };
    },
  };
}

function makeParent(initialChild) {
  return {
    currentChild: initialChild,
    replaceChild(nextChild, prevChild) {
      assert.equal(this.currentChild, prevChild);
      this.currentChild = nextChild;
      nextChild.parentNode = this;
    },
  };
}

function makeViewport({ overlay, image, canvas }) {
  return {
    querySelectorAll(selector) {
      if (selector === '.lineplot-overlay') {
        return overlay.parentNode?.currentChild === overlay ? [overlay] : [];
      }
      if (selector === '.cs-overlay' || selector === '.crop-overlay') {
        return [];
      }
      if (selector === 'img') {
        return image.parentNode?.currentChild === image ? [image] : [];
      }
      if (selector === 'canvas') {
        return canvas.parentNode?.currentChild === canvas ? [canvas] : [];
      }
      return [];
    },
  };
}

test('captureViewportBlob rasterizes custom overlays before the final workflow snapshot', async () => {
  const overlay = makeElement('overlay', { width: 320, height: 220 });
  const image = makeElement('image', { tagName: 'IMG', width: 180, height: 120, src: 'data:image/png;base64,abc' });
  const canvas = makeElement('canvas', { tagName: 'CANVAS', width: 128, height: 128 });
  canvas.toDataURL = () => 'data:image/png;base64,canvas';

  const overlayParent = makeParent(overlay);
  const imageParent = makeParent(image);
  const canvasParent = makeParent(canvas);
  overlay.parentNode = overlayParent;
  image.parentNode = imageParent;
  canvas.parentNode = canvasParent;

  const selectorsSeen = [];
  const viewport = makeViewport({ overlay, image, canvas });
  const blob = await captureViewportBlob(viewport, { width: 800, height: 600 }, {
    queryAll(root, selector) {
      selectorsSeen.push(selector);
      return root.querySelectorAll(selector);
    },
    waitForImageElement: async () => {},
    renderOverlayToDataUrl: async (el) => `data:image/png;base64,overlay-${el.name}`,
    renderImageToDataUrl: async (el) => `data:image/png;base64,image-${el.name}`,
    renderCanvasToDataUrl: async (el) => `data:image/png;base64,canvas-${el.name}`,
    createPlaceholder: (el, dataUrl) => ({ placeholderFor: el.name, dataUrl, parentNode: null }),
    nextFrame: async () => {},
    toBlobImpl: async (target) => {
      if (target === viewport) {
        assert.notEqual(overlayParent.currentChild, overlay);
        assert.notEqual(imageParent.currentChild, image);
        assert.notEqual(canvasParent.currentChild, canvas);
        return new Blob(['viewport'], { type: 'image/png' });
      }
      throw new Error('Unexpected element capture path');
    },
  });

  assert.equal(await blob.text(), 'viewport');
  assert.equal(overlayParent.currentChild, overlay);
  assert.equal(imageParent.currentChild, image);
  assert.equal(canvasParent.currentChild, canvas);
  assert.deepEqual(
    selectorsSeen.filter((selector) => OVERLAY_CAPTURE_SELECTORS.includes(selector)),
    OVERLAY_CAPTURE_SELECTORS,
  );
});

test('captureViewportBlob restores live elements when final capture fails', async () => {
  const overlay = makeElement('overlay', { width: 320, height: 220 });
  const image = makeElement('image', { tagName: 'IMG', width: 180, height: 120, src: 'data:image/png;base64,abc' });
  const canvas = makeElement('canvas', { tagName: 'CANVAS', width: 128, height: 128 });
  canvas.toDataURL = () => 'data:image/png;base64,canvas';

  const overlayParent = makeParent(overlay);
  const imageParent = makeParent(image);
  const canvasParent = makeParent(canvas);
  overlay.parentNode = overlayParent;
  image.parentNode = imageParent;
  canvas.parentNode = canvasParent;

  const viewport = makeViewport({ overlay, image, canvas });

  await assert.rejects(
    captureViewportBlob(viewport, { width: 800, height: 600 }, {
      queryAll: (root, selector) => root.querySelectorAll(selector),
      waitForImageElement: async () => {},
      renderOverlayToDataUrl: async () => 'data:image/png;base64,overlay',
      renderImageToDataUrl: async () => 'data:image/png;base64,image',
      renderCanvasToDataUrl: async () => 'data:image/png;base64,canvas',
      createPlaceholder: (el) => ({ placeholderFor: el.name, parentNode: null }),
      nextFrame: async () => {},
      toBlobImpl: async (target) => {
        if (target === viewport) throw new Error('capture failed');
        return new Blob(['unexpected'], { type: 'image/png' });
      },
    }),
    /capture failed/,
  );

  assert.equal(overlayParent.currentChild, overlay);
  assert.equal(imageParent.currentChild, image);
  assert.equal(canvasParent.currentChild, canvas);
});
