import assert from 'node:assert/strict';
import test from 'node:test';
import { parseConfig } from '../src/config.js';
import { WaylandBackend } from '../src/backend/wayland.js';
import type { WaylandHelperClient, WaylandNativeAction } from '../src/backend/wayland-helper.js';
import type { ComputerInfo, Rect } from '../src/types.js';

const info: ComputerInfo = {
  backend: 'wayland',
  sessionType: 'wayland',
  coordinateUnit: 'logical-pixel',
  coordinateSpaces: ['desktop', 'display'],
  virtualBounds: { x: 0, y: 0, width: 3200, height: 1080 },
  displays: [
    { id: 'one', name: 'one', x: 0, y: 0, width: 1920, height: 1080, primary: false, pixelWidth: 3840, pixelHeight: 2160, scale: 2 },
    { id: 'two', name: 'two', x: 1920, y: 180, width: 1280, height: 720, primary: false, pixelWidth: 1280, pixelHeight: 720, scale: 1 },
  ],
  capabilities: { observe: true, pointer: true, keyboard: true, text: true },
};

class FakeWaylandHelper implements WaylandHelperClient {
  readonly actions: WaylandNativeAction[] = [];
  readonly observations: Array<{ region: Rect; includePointer: boolean }> = [];
  async info(): Promise<ComputerInfo> { return info; }
  async observe(region: Rect, includePointer: boolean) {
    this.observations.push({ region, includePointer });
    return { data: Buffer.from([0x89, 0x50, 0x4e, 0x47]), pixelWidth: region.width * 2, pixelHeight: region.height * 2 };
  }
  async act(action: WaylandNativeAction): Promise<void> { this.actions.push(action); }
}

test('Wayland info exposes logical coordinate units and physical stream dimensions', async () => {
  const backend = new WaylandBackend(parseConfig({}), new FakeWaylandHelper());
  const result = await backend.info();
  assert.equal(result.backend, 'wayland');
  assert.equal(result.coordinateUnit, 'logical-pixel');
  assert.equal(result.displays[0]?.pixelWidth, 3840);
  assert.equal(result.displays[0]?.scale, 2);
});

test('Wayland display-relative observation resolves to compositor logical desktop coordinates', async () => {
  const helper = new FakeWaylandHelper();
  const backend = new WaylandBackend(parseConfig({}), helper);
  const result = await backend.observe({ target: { kind: 'region', region: { space: 'display', displayId: 'two', x: 10, y: 20, width: 100, height: 80 } }, includePointer: false });
  assert.deepEqual(helper.observations, [{ region: { x: 1930, y: 200, width: 100, height: 80 }, includePointer: false }]);
  assert.equal(result.pixelWidth, 200);
  assert.equal(result.pixelHeight, 160);
});

test('Wayland display-relative action resolves before native libei helper execution', async () => {
  const helper = new FakeWaylandHelper();
  const backend = new WaylandBackend(parseConfig({}), helper);
  await backend.act({ type: 'click', button: 'left', point: { space: 'display', displayId: 'two', x: 10, y: 20 } });
  assert.deepEqual(helper.actions, [{ type: 'click', button: 'left', point: { x: 1930, y: 200 } }]);
});

test('Wayland drag remains one explicit backend action', async () => {
  const helper = new FakeWaylandHelper();
  const backend = new WaylandBackend(parseConfig({}), helper);
  await backend.act({ type: 'drag', button: 'left', from: { space: 'desktop', x: 10, y: 20 }, to: { space: 'desktop', x: 30, y: 40 } });
  assert.deepEqual(helper.actions, [{ type: 'drag', button: 'left', from: { x: 10, y: 20 }, to: { x: 30, y: 40 } }]);
});

test('Wayland action limits are enforced before helper execution', async () => {
  const helper = new FakeWaylandHelper();
  const backend = new WaylandBackend(parseConfig({ action: { maxScrollSteps: 2, maxTextBytes: 3 } }), helper);
  await assert.rejects(() => backend.act({ type: 'scroll', deltaX: 0, deltaY: 3 }), /maximum steps/);
  await assert.rejects(() => backend.act({ type: 'text', text: 'four', delayMs: 0 }), /byte limit/);
  assert.deepEqual(helper.actions, []);
});
