// This program has been developed by students from the bachelor Computer Science at Utrecht
// University within the Software Project course.
// © Copyright Utrecht University (Department of Information and Computing Sciences)
import '@testing-library/jest-dom';
import { cleanup } from '@testing-library/react';
import {
  type CompositeWarningKey,
  type SeverityIndex,
} from "../src/pages/VisProgPage/visualProgrammingUI/components/EditorWarnings.tsx";
import useFlowStore from '../src/pages/VisProgPage/visualProgrammingUI/VisProgStores.tsx';

if (!globalThis.structuredClone) {
  globalThis.structuredClone = (obj: any) => {
    return JSON.parse(JSON.stringify(obj));
  };
}

// To make sure that the tests are working, it's important that you are using
// this implementation of ResizeObserver and DOMMatrixReadOnly
class ResizeObserver {
  callback: globalThis.ResizeObserverCallback;

  constructor(callback: globalThis.ResizeObserverCallback) {
    this.callback = callback;
  }

  observe(target: Element) {
    this.callback([{ target } as globalThis.ResizeObserverEntry], this);
  }

  unobserve() {}

  disconnect() {}
}

class DOMMatrixReadOnly {
  m22: number;
  constructor(transform: string) {
    const scale = transform?.match(/scale\(([1-9.])\)/)?.[1];
    this.m22 = scale !== undefined ? +scale : 1;
  }
}

// Only run the shim once when requested
let init = false;

export const mockReactFlow = () => {
  if (init) return;
  init = true;

  globalThis.ResizeObserver = ResizeObserver;

  // @ts-expect-error included in advised setup code provided in ReactFlow documentation
  global.DOMMatrixReadOnly = DOMMatrixReadOnly;

  Object.defineProperties(globalThis.HTMLElement.prototype, {
    offsetHeight: {
      get() {
        return parseFloat(this.style.height) || 1;
      },
    },
    offsetWidth: {
      get() {
        return parseFloat(this.style.width) || 1;
      },
    },
  });

  // @ts-expect-error included in advised setup code provided in ReactFlow documentation
  (globalThis.SVGElement as never).prototype.getBBox = () => ({
    x: 0,
    y: 0,
    width: 200,
    height: 200,
  });

};

beforeAll(() => {
  useFlowStore.setState({
    nodes: [],
    edges: [],
    past: [],
    future: [],
    isBatchAction: false,
    edgeReconnectSuccessful: true,
    ruleRegistry: new Map(),
    editorWarningRegistry: new Map(),
    severityIndex: new Map([
      ['INFO', new Set<CompositeWarningKey>()],
      ['WARNING', new Set<CompositeWarningKey>()],
      ['ERROR', new Set<CompositeWarningKey>()],
    ]) as SeverityIndex,
  });
});

afterEach(() => {
  cleanup();
  useFlowStore.setState({
    nodes: [],
    edges: [],
    past: [],
    future: [],
    isBatchAction: false,
    edgeReconnectSuccessful: true,
    ruleRegistry: new Map(),
    editorWarningRegistry: new Map(),
    severityIndex: new Map([
      ['INFO', new Set<CompositeWarningKey>()],
      ['WARNING', new Set<CompositeWarningKey>()],
      ['ERROR', new Set<CompositeWarningKey>()],
    ]) as SeverityIndex,
  });
});

if (typeof HTMLDialogElement !== 'undefined') {
  if (!HTMLDialogElement.prototype.showModal) {
    HTMLDialogElement.prototype.showModal = function () {
      // basic behavior: mark as open
      this.setAttribute('open', '');
    };
  }
  if (!HTMLDialogElement.prototype.close) {
    HTMLDialogElement.prototype.close = function () {
      this.removeAttribute('open');
    };
  }
}