import assert from 'node:assert/strict';
import test from 'node:test';
import { resolveLineDraft, type DrawShapeLike } from '../../src/features/layout/draw/lineDraft.ts';

const canvasWidth = 500;
const canvasHeight = 300;

function line(shape: Omit<Extract<DrawShapeLike, { type: 'line' }>, 'type'>): DrawShapeLike {
  return { type: 'line', ...shape };
}

function rect(): DrawShapeLike {
  return { type: 'rect', x: 40, y: 40, width: 80, height: 60 };
}

test('creates a vertical divider from a horizontal gesture when no horizontal crossing line exists ahead', () => {
  assert.deepEqual(resolveLineDraft(120, 80, 300, 90, [], canvasWidth, canvasHeight), {
    startX: 120,
    startY: 0,
    endX: 120,
    endY: canvasHeight,
    orientation: 'vertical',
  });

  assert.deepEqual(resolveLineDraft(120, 80, 10, 70, [], canvasWidth, canvasHeight), {
    startX: 120,
    startY: 0,
    endX: 120,
    endY: canvasHeight,
    orientation: 'vertical',
  });
});

test('creates a horizontal divider from a vertical gesture when no vertical crossing line exists ahead', () => {
  assert.deepEqual(resolveLineDraft(120, 80, 130, 220, [], canvasWidth, canvasHeight), {
    startX: 0,
    startY: 80,
    endX: canvasWidth,
    endY: 80,
    orientation: 'horizontal',
  });

  assert.deepEqual(resolveLineDraft(120, 80, 110, 10, [], canvasWidth, canvasHeight), {
    startX: 0,
    startY: 80,
    endX: canvasWidth,
    endY: 80,
    orientation: 'horizontal',
  });
});

test('stops at the nearest horizontal crossing line for a horizontal gesture divider', () => {
  const shapes: DrawShapeLike[] = [
    line({ x1: 20, y1: 40, x2: 220, y2: 40, orientation: 'horizontal' }),
    line({ x1: 40, y1: 180, x2: 260, y2: 180, orientation: 'horizontal' }),
  ];

  assert.deepEqual(resolveLineDraft(120, 80, 480, 90, shapes, canvasWidth, canvasHeight), {
    startX: 120,
    startY: 40,
    endX: 120,
    endY: 180,
    orientation: 'vertical',
  });
});

test('stops at the nearest vertical crossing line for a vertical gesture divider', () => {
  const shapes: DrawShapeLike[] = [
    line({ x1: 60, y1: 20, x2: 60, y2: 180, orientation: 'vertical' }),
    line({ x1: 260, y1: 20, x2: 260, y2: 160, orientation: 'vertical' }),
  ];

  assert.deepEqual(resolveLineDraft(120, 80, 130, 260, shapes, canvasWidth, canvasHeight), {
    startX: 60,
    startY: 80,
    endX: 260,
    endY: 80,
    orientation: 'horizontal',
  });
});

test('ignores crossing lines that are behind the start point or outside the crossing range', () => {
  const shapes: DrawShapeLike[] = [
    line({ x1: 60, y1: 20, x2: 60, y2: 180, orientation: 'vertical' }),
    line({ x1: 320, y1: 120, x2: 320, y2: 220, orientation: 'vertical' }),
    line({ x1: 20, y1: 20, x2: 260, y2: 20, orientation: 'horizontal' }),
    rect(),
  ];

  assert.deepEqual(resolveLineDraft(120, 80, 480, 90, shapes, canvasWidth, canvasHeight), {
    startX: 120,
    startY: 20,
    endX: 120,
    endY: canvasHeight,
    orientation: 'vertical',
  });

  assert.deepEqual(resolveLineDraft(120, 80, 130, 260, shapes, canvasWidth, canvasHeight), {
    startX: 60,
    startY: 80,
    endX: canvasWidth,
    endY: 80,
    orientation: 'horizontal',
  });
});

test('does not snap to a crossing line that starts exactly at the same point', () => {
  const shapes: DrawShapeLike[] = [
    line({ x1: 120, y1: 40, x2: 120, y2: 160, orientation: 'vertical' }),
    line({ x1: 120, y1: 180, x2: 240, y2: 180, orientation: 'horizontal' }),
  ];

  assert.deepEqual(resolveLineDraft(120, 80, 300, 90, shapes, canvasWidth, canvasHeight), {
    startX: 120,
    startY: 0,
    endX: 120,
    endY: 180,
    orientation: 'vertical',
  });

  assert.deepEqual(resolveLineDraft(120, 180, 130, 260, shapes, canvasWidth, canvasHeight), {
    startX: 0,
    startY: 180,
    endX: canvasWidth,
    endY: 180,
    orientation: 'horizontal',
  });
});

test('extends to the nearest crossing line on both forward and reverse directions', () => {
  const shapes: DrawShapeLike[] = [
    line({ x1: 60, y1: 20, x2: 60, y2: 180, orientation: 'vertical' }),
    line({ x1: 260, y1: 20, x2: 260, y2: 180, orientation: 'vertical' }),
    line({ x1: 20, y1: 40, x2: 220, y2: 40, orientation: 'horizontal' }),
    line({ x1: 20, y1: 180, x2: 220, y2: 180, orientation: 'horizontal' }),
  ];

  assert.deepEqual(resolveLineDraft(120, 80, 320, 90, shapes, canvasWidth, canvasHeight), {
    startX: 120,
    startY: 40,
    endX: 120,
    endY: 180,
    orientation: 'vertical',
  });

  assert.deepEqual(resolveLineDraft(120, 100, 130, 260, shapes, canvasWidth, canvasHeight), {
    startX: 60,
    startY: 100,
    endX: 260,
    endY: 100,
    orientation: 'horizontal',
  });
});

test('respects preferred orientation when the pointer drift would otherwise flip the axis', () => {
  assert.deepEqual(resolveLineDraft(120, 80, 140, 160, [], canvasWidth, canvasHeight, 'horizontal'), {
    startX: 0,
    startY: 80,
    endX: canvasWidth,
    endY: 80,
    orientation: 'horizontal',
  });

  assert.deepEqual(resolveLineDraft(120, 80, 260, 100, [], canvasWidth, canvasHeight, 'vertical'), {
    startX: 120,
    startY: 0,
    endX: 120,
    endY: canvasHeight,
    orientation: 'vertical',
  });
});
