import { describe, it, expect } from 'vitest';
import type { Wall, WallOpening } from '@house-plan-maker/shared';
import {
  wallStartEnd,
  wallLength,
  wallAngle,
  findNearestWall,
  openingWorldPosition,
  wallsFromShape,
  wallOutlinePolygon,
} from '../wallUtils';

function makeWall(overrides: Partial<Wall> = {}): Wall {
  return {
    id: 'w1',
    roomId: 'r1',
    startX: 0,
    startY: 0,
    endX: 4,
    endY: 0,
    thickness: 0.1,
    direction: 'NORTH' as const,
    ...overrides,
  };
}

describe('wallStartEnd', () => {
  it('returns start and end points', () => {
    const wall = makeWall({ startX: 1, startY: 2, endX: 3, endY: 4 });
    const result = wallStartEnd(wall);
    expect(result.start).toEqual({ x: 1, y: 2 });
    expect(result.end).toEqual({ x: 3, y: 4 });
  });
});

describe('wallLength', () => {
  it('computes length of horizontal wall', () => {
    expect(wallLength(makeWall())).toBe(4);
  });

  it('computes length of vertical wall', () => {
    expect(wallLength(makeWall({ startX: 0, startY: 0, endX: 0, endY: 3 }))).toBe(3);
  });

  it('computes length of diagonal wall', () => {
    expect(wallLength(makeWall({ startX: 0, startY: 0, endX: 3, endY: 4 }))).toBe(5);
  });

  it('returns 0 for zero-length wall', () => {
    expect(wallLength(makeWall({ startX: 2, startY: 3, endX: 2, endY: 3 }))).toBe(0);
  });
});

describe('wallAngle', () => {
  it('returns 0 for rightward wall', () => {
    expect(wallAngle(makeWall())).toBeCloseTo(0);
  });

  it('returns PI/2 for downward wall', () => {
    expect(wallAngle(makeWall({ endX: 0, endY: 5 }))).toBeCloseTo(Math.PI / 2);
  });
});

describe('findNearestWall', () => {
  it('returns null for no walls', () => {
    expect(findNearestWall({ x: 0, y: 0 }, [])).toBeNull();
  });

  it('finds nearest wall', () => {
    const walls: readonly Wall[] = [
      makeWall({ id: 'w1', startX: 0, startY: 0, endX: 4, endY: 0 }),
      makeWall({ id: 'w2', startX: 0, startY: 3, endX: 4, endY: 3 }),
    ];
    const result = findNearestWall({ x: 2, y: 0.5 }, walls);
    expect(result).not.toBeNull();
    expect(result!.wall.id).toBe('w1');
    expect(result!.distance).toBeCloseTo(0.5);
  });

  it('returns position along wall', () => {
    const walls: readonly Wall[] = [
      makeWall({ startX: 0, startY: 0, endX: 10, endY: 0 }),
    ];
    const result = findNearestWall({ x: 5, y: 1 }, walls);
    expect(result).not.toBeNull();
    expect(result!.positionAlongWall).toBeCloseTo(5);
  });
});

describe('openingWorldPosition', () => {
  it('computes center for opening on horizontal wall', () => {
    const wall = makeWall({ startX: 0, startY: 0, endX: 10, endY: 0 });
    const opening: WallOpening = {
      id: 'o1',
      roomId: 'r1',
      wallId: 'w1',
      type: 'DOOR',
      positionAlongWall: 5,
      width: 1,
      height: 2.1,
      elevationFromFloor: 0,
      openDirection: 'LEFT',
      positionAnchor: { horizontal: 'middle', vertical: 'bottom' },
      gridCols: 2,
      gridRows: 2,
      slopeDepth: 0,
      frameThickness: 0.03,
    };
    const result = openingWorldPosition(opening, wall);
    expect(result.center.x).toBeCloseTo(5);
    expect(result.center.y).toBeCloseTo(0);
    expect(result.start.x).toBeCloseTo(4.5);
    expect(result.end.x).toBeCloseTo(5.5);
  });

  it('handles zero-length wall', () => {
    const wall = makeWall({ startX: 3, startY: 3, endX: 3, endY: 3 });
    const opening: WallOpening = {
      id: 'o1',
      roomId: 'r1',
      wallId: 'w1',
      type: 'WINDOW',
      positionAlongWall: 0,
      width: 1,
      height: 1.2,
      elevationFromFloor: 0.9,
      openDirection: 'LEFT',
      positionAnchor: { horizontal: 'middle', vertical: 'bottom' },
      gridCols: 2,
      gridRows: 2,
      slopeDepth: 0,
      frameThickness: 0.03,
    };
    const result = openingWorldPosition(opening, wall);
    expect(result.center).toEqual({ x: 3, y: 3 });
    expect(result.angle).toBe(0);
  });
});

describe('wallsFromShape', () => {
  it('returns empty for fewer than 2 points', () => {
    expect(wallsFromShape([])).toEqual([]);
    expect(wallsFromShape([{ x: 0, y: 0 }])).toEqual([]);
  });

  it('creates walls from rectangular shape', () => {
    const shape = [
      { x: 0, y: 0 },
      { x: 4, y: 0 },
      { x: 4, y: 3 },
      { x: 0, y: 3 },
    ];
    const walls = wallsFromShape(shape, 0.1);
    expect(walls).toHaveLength(4);

    // First wall: top
    expect(walls[0]).toEqual({ startX: 0, startY: 0, endX: 4, endY: 0, thickness: 0.1 });
    // Last wall: left, closing the loop
    expect(walls[3]).toEqual({ startX: 0, startY: 3, endX: 0, endY: 0, thickness: 0.1 });
  });

  it('uses default thickness', () => {
    const walls = wallsFromShape([{ x: 0, y: 0 }, { x: 1, y: 0 }]);
    expect(walls[0].thickness).toBe(0.1);
  });
});

describe('wallOutlinePolygon', () => {
  it('returns 4 corner points for horizontal wall', () => {
    const wall = makeWall({ startX: 0, startY: 0, endX: 4, endY: 0, thickness: 0.2 });
    const poly = wallOutlinePolygon(wall);
    expect(poly).toHaveLength(4);

    // For a horizontal wall, perpendicular offset is in Y direction
    expect(poly[0].x).toBeCloseTo(0);
    expect(poly[0].y).toBeCloseTo(0.1); // +halfThick
    expect(poly[1].x).toBeCloseTo(4);
    expect(poly[1].y).toBeCloseTo(0.1);
    expect(poly[2].x).toBeCloseTo(4);
    expect(poly[2].y).toBeCloseTo(-0.1); // -halfThick
    expect(poly[3].x).toBeCloseTo(0);
    expect(poly[3].y).toBeCloseTo(-0.1);
  });
});
