import test from 'node:test';
import assert from 'node:assert/strict';
import {
  createEnforceOverlayLayerOrderHandler,
  createEnsureOverlayWindowLevelHandler,
  createUpdateVisibleOverlayBoundsHandler,
  hasLiveOverlayWindowBoundsMismatch,
} from './overlay-window-layout';

test('visible bounds handler writes visible layer geometry', () => {
  const calls: Array<{ x: number; y: number; width: number; height: number }> = [];
  const handleVisible = createUpdateVisibleOverlayBoundsHandler({
    setOverlayWindowBounds: (geometry) => calls.push(geometry),
  });
  const geometry = { x: 0, y: 0, width: 100, height: 50 };
  handleVisible(geometry);
  assert.deepEqual(calls, [geometry]);
});

test('visible bounds handler runs follow-up callback after applying geometry', () => {
  const calls: string[] = [];
  const geometry = { x: 0, y: 0, width: 100, height: 50 };
  const handleVisible = createUpdateVisibleOverlayBoundsHandler({
    setOverlayWindowBounds: () => calls.push('set-bounds'),
    afterSetOverlayWindowBounds: (nextGeometry) => {
      assert.deepEqual(nextGeometry, geometry);
      calls.push('after-bounds');
    },
  });

  handleVisible(geometry);

  assert.deepEqual(calls, ['set-bounds', 'after-bounds']);
});

test('visible bounds handler skips unchanged geometry', () => {
  const calls: string[] = [];
  const geometry = { x: 0, y: 0, width: 100, height: 50 };
  const handleVisible = createUpdateVisibleOverlayBoundsHandler({
    getCurrentOverlayWindowBounds: () => ({ ...geometry }),
    setOverlayWindowBounds: () => calls.push('set-bounds'),
    afterSetOverlayWindowBounds: () => calls.push('after-bounds'),
  });

  handleVisible(geometry);

  assert.deepEqual(calls, []);
});

test('visible bounds handler can refresh unchanged geometry for mode reconciliation', () => {
  const calls: string[] = [];
  const geometry = { x: 0, y: 0, width: 100, height: 50 };
  const handleVisible = createUpdateVisibleOverlayBoundsHandler({
    getCurrentOverlayWindowBounds: () => ({ ...geometry }),
    shouldRefreshUnchangedGeometry: (nextGeometry) => {
      assert.deepEqual(nextGeometry, geometry);
      calls.push('refresh-check');
      return true;
    },
    setOverlayWindowBounds: () => calls.push('set-bounds'),
    afterSetOverlayWindowBounds: () => calls.push('after-bounds'),
  });

  handleVisible(geometry);

  assert.deepEqual(calls, ['refresh-check', 'set-bounds', 'after-bounds']);
});

test('live overlay bounds mismatch forces refresh after window manager restore drift', () => {
  const geometry = { x: 100, y: 80, width: 1280, height: 720 };

  assert.equal(
    hasLiveOverlayWindowBoundsMismatch(
      [
        {
          isDestroyed: () => false,
          getBounds: () => ({ x: 96, y: 76, width: 1300, height: 740 }),
        },
      ],
      geometry,
    ),
    true,
  );
  assert.equal(
    hasLiveOverlayWindowBoundsMismatch(
      [
        {
          isDestroyed: () => false,
          getBounds: () => ({ ...geometry }),
        },
        {
          isDestroyed: () => true,
          getBounds: () => ({ x: 0, y: 0, width: 1, height: 1 }),
        },
      ],
      geometry,
    ),
    false,
  );
});

test('live overlay bounds mismatch compares content bounds when compositor adds insets', () => {
  const geometry = { x: 0, y: 0, width: 3440, height: 1440 };

  assert.equal(
    hasLiveOverlayWindowBoundsMismatch(
      [
        {
          isDestroyed: () => false,
          getBounds: () => ({ ...geometry }),
          getContentBounds: () => ({ x: 0, y: 14, width: 3440, height: 1426 }),
        },
      ],
      geometry,
    ),
    true,
  );
  assert.equal(
    hasLiveOverlayWindowBoundsMismatch(
      [
        {
          isDestroyed: () => false,
          getBounds: () => ({ x: 0, y: -14, width: 3440, height: 1454 }),
          getContentBounds: () => ({ ...geometry }),
        },
      ],
      geometry,
    ),
    false,
  );
});

test('ensure overlay window level handler delegates to core', () => {
  const calls: string[] = [];
  const ensureLevel = createEnsureOverlayWindowLevelHandler({
    ensureOverlayWindowLevelCore: () => calls.push('core'),
    afterEnsureOverlayWindowLevel: () => calls.push('after'),
  });
  ensureLevel({});
  assert.deepEqual(calls, ['core', 'after']);
});

test('ensure overlay window level handler skips while top reassertion is suppressed', () => {
  const calls: string[] = [];
  const window = {};
  const ensureLevel = createEnsureOverlayWindowLevelHandler({
    shouldSuppressOverlayWindowLevel: (nextWindow) => {
      assert.equal(nextWindow, window);
      calls.push('suppress-check');
      return true;
    },
    ensureOverlayWindowLevelCore: () => calls.push('core'),
    afterEnsureOverlayWindowLevel: () => calls.push('after'),
  });

  ensureLevel(window);

  assert.deepEqual(calls, ['suppress-check']);
});

test('enforce overlay layer order handler forwards resolved state', () => {
  const calls: string[] = [];
  const enforce = createEnforceOverlayLayerOrderHandler({
    enforceOverlayLayerOrderCore: (params) => {
      calls.push(params.visibleOverlayVisible ? 'visible-on' : 'visible-off');
      params.ensureOverlayWindowLevel({});
    },
    getVisibleOverlayVisible: () => true,
    getMainWindow: () => ({}),
    ensureOverlayWindowLevel: () => calls.push('ensure-level'),
  });
  enforce();
  assert.deepEqual(calls, ['visible-on', 'ensure-level']);
});
