import { memo, useMemo } from 'react';
import { Line, Group } from 'react-konva';
import type { Point, Wall } from '@house-plan-maker/shared';
import { polygonCentroid } from '../utils/geometry';

interface WallLayerProps {
  readonly walls: readonly Wall[];
  readonly roomShape: readonly Point[];
  readonly zoom: number;
  readonly panOffset: Point;
  readonly selectedIds: ReadonlySet<string>;
  readonly plinthThickness?: number;
}

/** Wall fill color. */
const WALL_FILL = '#495057';
/** Wall stroke color. */
const WALL_STROKE = '#343a40';
/** Room interior fill color. */
const ROOM_FILL = '#f8f9fa';
/** Room interior stroke. */
const ROOM_STROKE = '#dee2e6';
/** Selected wall highlight. */
const WALL_SELECTED_STROKE = '#4c6ef5';

/**
 * Offset a polygon outward by a fixed distance.
 * Uses the angle bisector at each vertex to push the point outward.
 */
function offsetPolygonOutward(shape: readonly Point[], thickness: number, centroid: Point): Point[] {
  const n = shape.length;
  if (n < 3) return [...shape];

  const result: Point[] = [];

  for (let i = 0; i < n; i++) {
    const prev = shape[(i - 1 + n) % n];
    const curr = shape[i];
    const next = shape[(i + 1) % n];

    // Edge vectors
    const e1x = curr.x - prev.x;
    const e1y = curr.y - prev.y;
    const e2x = next.x - curr.x;
    const e2y = next.y - curr.y;

    // Outward normals for each edge (perpendicular, pointing away from centroid)
    let n1x = -e1y;
    let n1y = e1x;
    // Normalize
    const len1 = Math.sqrt(n1x * n1x + n1y * n1y) || 1;
    n1x /= len1;
    n1y /= len1;

    let n2x = -e2y;
    let n2y = e2x;
    const len2 = Math.sqrt(n2x * n2x + n2y * n2y) || 1;
    n2x /= len2;
    n2y /= len2;

    // Ensure normals point away from centroid
    const midX = curr.x;
    const midY = curr.y;
    const toCenterX = centroid.x - midX;
    const toCenterY = centroid.y - midY;

    if (n1x * toCenterX + n1y * toCenterY > 0) {
      n1x = -n1x;
      n1y = -n1y;
    }
    if (n2x * toCenterX + n2y * toCenterY > 0) {
      n2x = -n2x;
      n2y = -n2y;
    }

    // Average bisector direction
    let bx = n1x + n2x;
    let by = n1y + n2y;
    const bLen = Math.sqrt(bx * bx + by * by);

    if (bLen < 0.001) {
      // Parallel edges — just use one normal
      bx = n1x;
      by = n1y;
    } else {
      bx /= bLen;
      by /= bLen;
      // Scale by 1/cos(half-angle) to maintain thickness at the corner
      const cosHalf = bx * n1x + by * n1y;
      const scale = cosHalf > 0.1 ? 1 / cosHalf : 1;
      bx *= Math.min(scale, 3); // cap to prevent extreme spikes
      by *= Math.min(scale, 3);
    }

    result.push({
      x: curr.x + bx * thickness,
      y: curr.y + by * thickness,
    });
  }

  return result;
}

/** Plinth color. */
const PLINTH_COLOR = '#8b7355';

export const WallLayer = memo(function WallLayer({
  walls,
  roomShape,
  zoom,
  panOffset,
  selectedIds,
  plinthThickness = 0.01,
}: WallLayerProps) {
  // Get wall thickness (use first wall's thickness as representative)
  const wallThickness = walls.length > 0 ? walls[0].thickness : 0.1;

  // Convert room shape to screen coordinates
  const roomShapeScreen = useMemo(() => {
    if (roomShape.length < 3) return [];
    return roomShape.flatMap((p) => [
      p.x * zoom + panOffset.x,
      p.y * zoom + panOffset.y,
    ]);
  }, [roomShape, zoom, panOffset]);

  // Compute outer wall boundary (room shape offset outward by wall thickness)
  const outerWallScreen = useMemo(() => {
    if (roomShape.length < 3) return [];
    const centroid = polygonCentroid(roomShape);
    const outer = offsetPolygonOutward(roomShape, wallThickness, centroid);
    return outer.flatMap((p) => [
      p.x * zoom + panOffset.x,
      p.y * zoom + panOffset.y,
    ]);
  }, [roomShape, wallThickness, zoom, panOffset]);

  // Compute inner plinth boundary (room shape offset inward by plinth thickness)
  const plinthScreen = useMemo(() => {
    if (roomShape.length < 3 || plinthThickness <= 0) return [];
    const centroid = polygonCentroid(roomShape);
    // Offset inward (toward centroid) = negative outward offset
    const inner = offsetPolygonOutward(roomShape, -plinthThickness, centroid);
    return inner.flatMap((p) => [
      p.x * zoom + panOffset.x,
      p.y * zoom + panOffset.y,
    ]);
  }, [roomShape, plinthThickness, zoom, panOffset]);

  // Selected wall segments for highlighting
  const selectedWallSegments = useMemo(() => {
    return walls
      .filter((w) => selectedIds.has(w.id))
      .map((wall) => {
        const points = [
          wall.startX * zoom + panOffset.x,
          wall.startY * zoom + panOffset.y,
          wall.endX * zoom + panOffset.x,
          wall.endY * zoom + panOffset.y,
        ];
        return { wall, points };
      });
  }, [walls, selectedIds, zoom, panOffset]);

  return (
    <Group>
      {/* Room interior fill */}
      {roomShapeScreen.length >= 6 && (
        <Line
          points={roomShapeScreen}
          closed
          fill={ROOM_FILL}
          stroke={ROOM_STROKE}
          strokeWidth={1}
          listening={false}
        />
      )}

      {/* Outer wall boundary — single continuous polygon, no corner gaps */}
      {outerWallScreen.length >= 6 && (
        <Group listening={false}>
          {/* Outer fill */}
          <Line
            points={outerWallScreen}
            closed
            fill={WALL_FILL}
            stroke={WALL_STROKE}
            strokeWidth={1}
            listening={false}
          />
          {/* Cut out room interior by drawing room shape on top with room fill */}
          <Line
            points={roomShapeScreen}
            closed
            fill={ROOM_FILL}
            stroke={WALL_STROKE}
            strokeWidth={1}
            listening={false}
          />
        </Group>
      )}

      {/* Plinth strip along inside of walls */}
      {plinthScreen.length >= 6 && roomShapeScreen.length >= 6 && (
        <Group listening={false}>
          {/* Draw room shape filled with plinth color */}
          <Line
            points={roomShapeScreen}
            closed
            fill={PLINTH_COLOR}
            listening={false}
          />
          {/* Cut out inner area (room minus plinth) */}
          <Line
            points={plinthScreen}
            closed
            fill={ROOM_FILL}
            listening={false}
          />
        </Group>
      )}

      {/* Selected wall highlights */}
      {selectedWallSegments.map(({ wall, points }) => (
        <Line
          key={`sel-${wall.id}`}
          points={points}
          stroke={WALL_SELECTED_STROKE}
          strokeWidth={wallThickness * zoom + 2}
          lineCap="square"
          listening={false}
        />
      ))}
    </Group>
  );
});
