import type { Point, Wall, WallOpening } from '@house-plan-maker/shared';
import { distance, projectPointOntoSegment, segmentAngle } from './geometry';

/** Get the start/end points of a wall as Point objects. */
export function wallStartEnd(wall: Wall): { start: Point; end: Point } {
  return {
    start: { x: wall.startX, y: wall.startY },
    end: { x: wall.endX, y: wall.endY },
  };
}

/** Compute the length of a wall segment. */
export function wallLength(wall: Wall): number {
  return distance(
    { x: wall.startX, y: wall.startY },
    { x: wall.endX, y: wall.endY },
  );
}

/** Compute the angle of a wall in radians. */
export function wallAngle(wall: Wall): number {
  return segmentAngle(
    { x: wall.startX, y: wall.startY },
    { x: wall.endX, y: wall.endY },
  );
}

/**
 * Find the nearest wall to a point. Returns the wall, projected point, and distance.
 * Returns null if no walls exist.
 */
export function findNearestWall(
  point: Point,
  walls: readonly Wall[],
): {
  wall: Wall;
  projected: Point;
  positionAlongWall: number;
  distance: number;
} | null {
  if (walls.length === 0) return null;

  let best: {
    wall: Wall;
    projected: Point;
    positionAlongWall: number;
    distance: number;
  } | null = null;

  for (const wall of walls) {
    const { start, end } = wallStartEnd(wall);
    const result = projectPointOntoSegment(point, start, end);
    const len = wallLength(wall);
    const posAlongWall = result.t * len;

    if (best === null || result.distance < best.distance) {
      best = {
        wall,
        projected: result.projected,
        positionAlongWall: posAlongWall,
        distance: result.distance,
      };
    }
  }

  return best;
}

/**
 * Compute the position of an opening on a wall in world coordinates.
 * Returns center point, angle, and the start/end points of the opening along the wall.
 */
export function openingWorldPosition(
  opening: WallOpening,
  wall: Wall,
): {
  center: Point;
  angle: number;
  start: Point;
  end: Point;
} {
  const { start, end } = wallStartEnd(wall);
  const len = wallLength(wall);
  const angle = wallAngle(wall);

  if (len === 0) {
    return { center: start, angle: 0, start, end: start };
  }

  const dx = (end.x - start.x) / len;
  const dy = (end.y - start.y) / len;

  const halfWidth = opening.width / 2;
  const centerPos = opening.positionAlongWall;

  const center: Point = {
    x: start.x + dx * centerPos,
    y: start.y + dy * centerPos,
  };

  const openingStart: Point = {
    x: start.x + dx * (centerPos - halfWidth),
    y: start.y + dy * (centerPos - halfWidth),
  };

  const openingEnd: Point = {
    x: start.x + dx * (centerPos + halfWidth),
    y: start.y + dy * (centerPos + halfWidth),
  };

  return { center, angle, start: openingStart, end: openingEnd };
}

/**
 * Generate wall segments from a room's polygon shape.
 * Each consecutive pair of points becomes a wall segment.
 * The polygon is automatically closed (last point connects to first).
 */
export function wallsFromShape(
  shape: readonly Point[],
  thickness: number = 0.1,
): readonly { startX: number; startY: number; endX: number; endY: number; thickness: number }[] {
  if (shape.length < 2) return [];

  const walls: { startX: number; startY: number; endX: number; endY: number; thickness: number }[] = [];

  for (let i = 0; i < shape.length; i++) {
    const start = shape[i];
    const end = shape[(i + 1) % shape.length];
    walls.push({
      startX: start.x,
      startY: start.y,
      endX: end.x,
      endY: end.y,
      thickness,
    });
  }

  return walls;
}

/**
 * Generate the wall outline polygon for rendering a wall with thickness.
 * Returns four corner points forming the wall rectangle.
 */
/**
 * Compute the wall outline polygon. The wall extends outward from the room
 * boundary by its full thickness, never inward into the room.
 *
 * @param wall The wall segment
 * @param roomCentroid Optional room centroid to determine outward direction.
 *   If not provided, the polygon extends equally on both sides (legacy behavior).
 */
export function wallOutlinePolygon(wall: Wall, roomCentroid?: Point): readonly Point[] {
  const { start, end } = wallStartEnd(wall);
  const angle = wallAngle(wall);
  const thickness = wall.thickness;

  // Perpendicular unit vector
  const nx = -Math.sin(angle);
  const ny = Math.cos(angle);

  if (roomCentroid) {
    // Determine which perpendicular direction points away from the room center
    const wallMidX = (start.x + end.x) / 2;
    const wallMidY = (start.y + end.y) / 2;
    const toCenterX = roomCentroid.x - wallMidX;
    const toCenterY = roomCentroid.y - wallMidY;
    // Dot product: if positive, (nx,ny) points toward center → flip to point outward
    const dot = toCenterX * nx + toCenterY * ny;
    const outNx = dot > 0 ? -nx : nx;
    const outNy = dot > 0 ? -ny : ny;

    // Wall sits on the room boundary; extend outward only
    return [
      { x: start.x, y: start.y },
      { x: end.x, y: end.y },
      { x: end.x + outNx * thickness, y: end.y + outNy * thickness },
      { x: start.x + outNx * thickness, y: start.y + outNy * thickness },
    ];
  }

  // Fallback: extend half-thickness on each side
  const halfThick = thickness / 2;
  return [
    { x: start.x + nx * halfThick, y: start.y + ny * halfThick },
    { x: end.x + nx * halfThick, y: end.y + ny * halfThick },
    { x: end.x - nx * halfThick, y: end.y - ny * halfThick },
    { x: start.x - nx * halfThick, y: start.y - ny * halfThick },
  ];
}
