import { memo, useMemo } from 'react';
import { Line, Text, Group } from 'react-konva';
import type { Point, Wall, WallOpening } from '@house-plan-maker/shared';
import { wallLength, wallAngle, wallStartEnd } from '../utils/wallUtils';

interface MeasurementLayerProps {
  readonly walls: readonly Wall[];
  readonly openings: readonly WallOpening[];
  readonly zoom: number;
  readonly panOffset: Point;
  readonly selectedIds: ReadonlySet<string>;
  readonly roomShape: readonly Point[];
}

const MEASUREMENT_COLOR = '#868e96';
const MEASUREMENT_FONT_SIZE = 10;
const MEASUREMENT_OFFSET = 16; // pixels offset from wall
const MIN_ZOOM_FOR_MEASUREMENTS = 40; // hide at very low zoom
const SELECTED_MEASUREMENT_COLOR = '#4c6ef5';

export const MeasurementLayer = memo(function MeasurementLayer({
  walls,
  openings,
  zoom,
  panOffset,
  selectedIds,
  roomShape,
}: MeasurementLayerProps) {
  // Hide measurements at very low zoom levels
  if (zoom < MIN_ZOOM_FOR_MEASUREMENTS) {
    return <Group listening={false} />;
  }

  return (
    <Group listening={false}>
      {/* Outer room dimensions — one horizontal + one vertical label outside
          the room bounding box. The former per-wall inner labels were
          removed because they duplicated these numbers on every wall of a
          rectangular room. */}
      {roomShape.length >= 3 && (
        <RoomDimensions
          roomShape={roomShape}
          zoom={zoom}
          panOffset={panOffset}
        />
      )}

      {/* Distance from selected opening to wall ends */}
      {openings
        .filter((o) => selectedIds.has(o.id))
        .map((opening) => {
          const wall = walls.find((w) => w.id === opening.wallId);
          if (!wall) return null;
          return (
            <OpeningDistances
              key={`od-${opening.id}`}
              opening={opening}
              wall={wall}
              zoom={zoom}
              panOffset={panOffset}
            />
          );
        })}
    </Group>
  );
});

// ── Room overall dimensions (outer bbox labels) ──

interface RoomDimensionsProps {
  readonly roomShape: readonly Point[];
  readonly zoom: number;
  readonly panOffset: Point;
}

function RoomDimensions({ roomShape, zoom, panOffset }: RoomDimensionsProps) {
  const bounds = useMemo(() => {
    let minX = Infinity;
    let minY = Infinity;
    let maxX = -Infinity;
    let maxY = -Infinity;
    for (const p of roomShape) {
      if (p.x < minX) minX = p.x;
      if (p.y < minY) minY = p.y;
      if (p.x > maxX) maxX = p.x;
      if (p.y > maxY) maxY = p.y;
    }
    return { minX, minY, maxX, maxY };
  }, [roomShape]);

  const width = bounds.maxX - bounds.minX;
  const height = bounds.maxY - bounds.minY;

  if (width < 0.01 || height < 0.01) return null;

  const topY = bounds.minY * zoom + panOffset.y - 30;
  const leftX = bounds.minX * zoom + panOffset.x - 30;

  // Horizontal dimension (width)
  const hStartX = bounds.minX * zoom + panOffset.x;
  const hEndX = bounds.maxX * zoom + panOffset.x;
  const hMidX = (hStartX + hEndX) / 2;

  // Vertical dimension (height)
  const vStartY = bounds.minY * zoom + panOffset.y;
  const vEndY = bounds.maxY * zoom + panOffset.y;
  const vMidY = (vStartY + vEndY) / 2;

  return (
    <Group>
      {/* Horizontal dimension line */}
      <Line
        points={[hStartX, topY, hEndX, topY]}
        stroke={MEASUREMENT_COLOR}
        strokeWidth={1}
        listening={false}
      />
      <Line
        points={[hStartX, topY - 4, hStartX, topY + 4]}
        stroke={MEASUREMENT_COLOR}
        strokeWidth={1}
        listening={false}
      />
      <Line
        points={[hEndX, topY - 4, hEndX, topY + 4]}
        stroke={MEASUREMENT_COLOR}
        strokeWidth={1}
        listening={false}
      />
      <Text
        x={hMidX - 15}
        y={topY - 14}
        text={formatMeasurement(width)}
        fontSize={MEASUREMENT_FONT_SIZE}
        fill={MEASUREMENT_COLOR}
        listening={false}
      />

      {/* Vertical dimension line */}
      <Line
        points={[leftX, vStartY, leftX, vEndY]}
        stroke={MEASUREMENT_COLOR}
        strokeWidth={1}
        listening={false}
      />
      <Line
        points={[leftX - 4, vStartY, leftX + 4, vStartY]}
        stroke={MEASUREMENT_COLOR}
        strokeWidth={1}
        listening={false}
      />
      <Line
        points={[leftX - 4, vEndY, leftX + 4, vEndY]}
        stroke={MEASUREMENT_COLOR}
        strokeWidth={1}
        listening={false}
      />
      <Text
        x={leftX - 30}
        y={vMidY - 5}
        text={formatMeasurement(height)}
        fontSize={MEASUREMENT_FONT_SIZE}
        fill={MEASUREMENT_COLOR}
        rotation={-90}
        listening={false}
      />
    </Group>
  );
}

// ── Opening distance annotations ──

interface OpeningDistancesProps {
  readonly opening: WallOpening;
  readonly wall: Wall;
  readonly zoom: number;
  readonly panOffset: Point;
}

function OpeningDistances({ opening, wall, zoom, panOffset }: OpeningDistancesProps) {
  const wLen = wallLength(wall);
  const halfWidth = opening.width / 2;
  const distToStart = opening.positionAlongWall - halfWidth;
  const distToEnd = wLen - opening.positionAlongWall - halfWidth;

  const { start, end } = wallStartEnd(wall);
  const angle = wallAngle(wall);
  const dx = Math.cos(angle);
  const dy = Math.sin(angle);

  // Perpendicular offset for annotation
  const ox = -Math.sin(angle) * (MEASUREMENT_OFFSET + 12);
  const oy = Math.cos(angle) * (MEASUREMENT_OFFSET + 12);

  const openingStartX = (start.x + dx * distToStart) * zoom + panOffset.x;
  const openingStartY = (start.y + dy * distToStart) * zoom + panOffset.y;
  const openingEndX = (start.x + dx * (opening.positionAlongWall + halfWidth)) * zoom + panOffset.x;
  const openingEndY = (start.y + dy * (opening.positionAlongWall + halfWidth)) * zoom + panOffset.y;
  const wallStartX = start.x * zoom + panOffset.x;
  const wallStartY = start.y * zoom + panOffset.y;
  const wallEndX = end.x * zoom + panOffset.x;
  const wallEndY = end.y * zoom + panOffset.y;

  return (
    <Group>
      {distToStart > 0.01 && (
        <>
          <Line
            points={[wallStartX + ox, wallStartY + oy, openingStartX + ox, openingStartY + oy]}
            stroke={SELECTED_MEASUREMENT_COLOR}
            strokeWidth={1}
            dash={[3, 3]}
            listening={false}
          />
          <Text
            x={(wallStartX + openingStartX) / 2 + ox - 10}
            y={(wallStartY + openingStartY) / 2 + oy - 5}
            text={formatMeasurement(distToStart)}
            fontSize={MEASUREMENT_FONT_SIZE}
            fill={SELECTED_MEASUREMENT_COLOR}
            listening={false}
          />
        </>
      )}
      {distToEnd > 0.01 && (
        <>
          <Line
            points={[openingEndX + ox, openingEndY + oy, wallEndX + ox, wallEndY + oy]}
            stroke={SELECTED_MEASUREMENT_COLOR}
            strokeWidth={1}
            dash={[3, 3]}
            listening={false}
          />
          <Text
            x={(openingEndX + wallEndX) / 2 + ox - 10}
            y={(openingEndY + wallEndY) / 2 + oy - 5}
            text={formatMeasurement(distToEnd)}
            fontSize={MEASUREMENT_FONT_SIZE}
            fill={SELECTED_MEASUREMENT_COLOR}
            listening={false}
          />
        </>
      )}
    </Group>
  );
}

function formatMeasurement(meters: number): string {
  if (meters >= 1) {
    const rounded = Math.round(meters * 100) / 100;
    return `${rounded}m`;
  }
  const cm = Math.round(meters * 100);
  return `${cm}cm`;
}
