import { describe, it, expect } from 'vitest';
import { clampZoom, zoomToPoint, fitViewport, worldFromScreen, MIN_ZOOM, MAX_ZOOM } from './viewport';

describe('viewport math', () => {
  it('clamps zoom to bounds', () => {
    expect(clampZoom(0.001)).toBe(MIN_ZOOM);
    expect(clampZoom(999)).toBe(MAX_ZOOM);
    expect(clampZoom(1.5)).toBe(1.5);
  });

  it('keeps the world point under the cursor fixed when zooming', () => {
    const start = { zoom: 1, panX: 0, panY: 0 };
    const cx = 100, cy = 50;
    const before = worldFromScreen(start.zoom, cx, cy, start.panX, start.panY);
    const next = zoomToPoint(start, 2, cx, cy);
    const after = worldFromScreen(next.zoom, cx, cy, next.panX, next.panY);
    expect(next.zoom).toBe(2);
    expect(after.x).toBeCloseTo(before.x);
    expect(after.y).toBeCloseTo(before.y);
  });

  it('fits a large landscape map by shrinking and centering', () => {
    const vp = fitViewport(800, 600, 4000, 3000);
    expect(vp.zoom).toBeCloseTo(0.2);
    expect(vp.panX).toBeCloseTo(0);
    expect(vp.panY).toBeCloseTo(0);
  });

  it('never upscales a small map past 1:1, and centers it', () => {
    const vp = fitViewport(800, 600, 100, 100);
    expect(vp.zoom).toBe(1);
    expect(vp.panX).toBeCloseTo(350);
    expect(vp.panY).toBeCloseTo(250);
  });

  it('guards degenerate sizes', () => {
    expect(fitViewport(800, 600, 0, 0)).toEqual({ zoom: 1, panX: 0, panY: 0 });
  });

  it('converts screen to world by dividing out zoom', () => {
    expect(worldFromScreen(2, 120, 80, 20, 0)).toEqual({ x: 50, y: 40 });
  });
});
