import type { ReactNode } from 'react';
import { Group, Rect, Line, Text } from 'react-konva';
import { useCanvasColors } from '../utils/canvasThemeColors';
import type {
  ProjectedOpening,
  ProjectedElectrical,
  ProjectedFurniture,
} from '../utils/projectionMapping';
import { projectionToPixel } from '../utils/projectionMapping';
import { DEFAULT_OUTLET_WIDTH, DEFAULT_OUTLET_HEIGHT } from '@house-plan-maker/shared';
import { getOutletInvertCoordX, getOutletInvertCoordY } from '../symbols/electrical';

interface ProjectionMeasurementsProps {
  readonly projectedOpenings: readonly ProjectedOpening[];
  readonly projectedElectrical: readonly ProjectedElectrical[];
  readonly projectedFurniture?: readonly ProjectedFurniture[];
  readonly wallLength: number;
  readonly wallHeight: number;
  readonly scale: number;
  readonly padding: number;
  readonly outletWidth?: number;
  readonly outletHeight?: number;
  /** When false, the wall-level dimensions/labels are skipped (useful when the
   * measurements layer is toggled off but per-item overlays should still draw). */
  readonly showWallDimensions?: boolean;
  /** Multiplier applied to every hardcoded fontSize so labels stay legible
   * when the parent renders on a large export stage. Default 1. */
  readonly fontScale?: number;
}

/** Dimension line with arrows and text. */
function DimensionLine({
  x1, y1, x2, y2, label, offset, horizontal, lineColor, textColor, fontScale = 1,
}: {
  readonly x1: number;
  readonly y1: number;
  readonly x2: number;
  readonly y2: number;
  readonly label: string;
  readonly offset: number;
  readonly horizontal: boolean;
  readonly lineColor: string;
  readonly textColor: string;
  readonly fontScale?: number;
}) {
  const arrowSize = 4;

  if (horizontal) {
    const lineY = y1 + offset;
    const midX = (x1 + x2) / 2;
    return (
      <Group>
        {/* Extension lines */}
        <Line points={[x1, y1, x1, lineY]} stroke={lineColor} strokeWidth={0.5} />
        <Line points={[x2, y2, x2, lineY]} stroke={lineColor} strokeWidth={0.5} />
        {/* Main line */}
        <Line points={[x1, lineY, x2, lineY]} stroke={lineColor} strokeWidth={0.75} />
        {/* Arrows */}
        <Line
          points={[x1, lineY, x1 + arrowSize, lineY - arrowSize / 2, x1 + arrowSize, lineY + arrowSize / 2]}
          fill={lineColor}
          closed
        />
        <Line
          points={[x2, lineY, x2 - arrowSize, lineY - arrowSize / 2, x2 - arrowSize, lineY + arrowSize / 2]}
          fill={lineColor}
          closed
        />
        {/* Label */}
        <Text
          x={midX - 20}
          y={lineY - 12}
          width={40}
          text={label}
          align="center"
          fontSize={9 * fontScale}
          fill={textColor}
        />
      </Group>
    );
  }

  // Vertical dimension
  const lineX = x1 + offset;
  const midY = (y1 + y2) / 2;
  return (
    <Group>
      <Line points={[x1, y1, lineX, y1]} stroke={lineColor} strokeWidth={0.5} />
      <Line points={[x1, y2, lineX, y2]} stroke={lineColor} strokeWidth={0.5} />
      <Line points={[lineX, y1, lineX, y2]} stroke={lineColor} strokeWidth={0.75} />
      <Line
        points={[lineX, y1, lineX - arrowSize / 2, y1 + arrowSize, lineX + arrowSize / 2, y1 + arrowSize]}
        fill={lineColor}
        closed
      />
      <Line
        points={[lineX, y2, lineX - arrowSize / 2, y2 - arrowSize, lineX + arrowSize / 2, y2 - arrowSize]}
        fill={lineColor}
        closed
      />
      <Text
        x={lineX + 3}
        y={midY - 5}
        text={label}
        fontSize={9 * fontScale}
        fill={textColor}
      />
    </Group>
  );
}

function formatM(meters: number): string {
  return `${meters.toFixed(2)}m`;
}

/** Render measurement annotations on a wall projection view. */
export function ProjectionMeasurements({
  projectedOpenings,
  projectedElectrical,
  projectedFurniture = [],
  wallLength: wallLen,
  wallHeight,
  scale,
  padding,
  outletWidth = DEFAULT_OUTLET_WIDTH,
  outletHeight = DEFAULT_OUTLET_HEIGHT,
  showWallDimensions = true,
  fontScale = 1,
}: ProjectionMeasurementsProps) {
  const colors = useCanvasColors();
  const elements: ReactNode[] = [];

  if (showWallDimensions) {
    // Wall width dimension (along bottom)
    const floorLeft = projectionToPixel(0, 0, wallHeight, scale, padding);
    const floorRight = projectionToPixel(wallLen, 0, wallHeight, scale, padding);
    elements.push(
      <DimensionLine
        fontScale={fontScale}
        key="wall-width"
        x1={floorLeft.x}
        y1={floorLeft.y}
        x2={floorRight.x}
        y2={floorRight.y}
        label={formatM(wallLen)}
        offset={18}
        horizontal
        lineColor={colors.dimensionLine}
        textColor={colors.dimensionText}
      />,
    );

    // Wall height dimension (along right side)
    const topRight = projectionToPixel(wallLen, wallHeight, wallHeight, scale, padding);
    const bottomRight = projectionToPixel(wallLen, 0, wallHeight, scale, padding);
    elements.push(
      <DimensionLine
        fontScale={fontScale}
        key="wall-height"
        x1={topRight.x}
        y1={topRight.y}
        x2={bottomRight.x}
        y2={bottomRight.y}
        label={formatM(wallHeight)}
        offset={18}
        horizontal={false}
        lineColor={colors.dimensionLine}
        textColor={colors.dimensionText}
      />,
    );
  }

  // Opening dimensions: sill height for windows, door height for doors
  for (const po of projectedOpenings) {
    const { rect, opening } = po;
    const topLeft = projectionToPixel(rect.x, rect.y + rect.height, wallHeight, scale, padding);
    const bottomLeft = projectionToPixel(rect.x, rect.y, wallHeight, scale, padding);

    // Height annotation (vertical, left side of opening)
    elements.push(
      <DimensionLine
        fontScale={fontScale}
        key={`opening-h-${opening.id}`}
        x1={topLeft.x}
        y1={topLeft.y}
        x2={bottomLeft.x}
        y2={bottomLeft.y}
        label={formatM(rect.height)}
        offset={-14}
        horizontal={false}
        lineColor={colors.dimensionLine}
        textColor={colors.dimensionText}
      />,
    );

    // Sill height for windows
    if (opening.type === 'WINDOW' && rect.y > 0.01) {
      const floorBelow = projectionToPixel(rect.x, 0, wallHeight, scale, padding);
      elements.push(
        <DimensionLine
        fontScale={fontScale}
          key={`sill-${opening.id}`}
          x1={bottomLeft.x}
          y1={bottomLeft.y}
          x2={floorBelow.x}
          y2={floorBelow.y}
          label={formatM(rect.y)}
          offset={-14}
          horizontal={false}
          lineColor={colors.dimensionLine}
          textColor={colors.dimensionText}
        />,
      );
    }

    // Width annotation (horizontal, above opening)
    const topRight2 = projectionToPixel(rect.x + rect.width, rect.y + rect.height, wallHeight, scale, padding);
    elements.push(
      <DimensionLine
        fontScale={fontScale}
        key={`opening-w-${opening.id}`}
        x1={topLeft.x}
        y1={topLeft.y}
        x2={topRight2.x}
        y2={topRight2.y}
        label={formatM(rect.width)}
        offset={-12}
        horizontal
        lineColor={colors.dimensionLine}
        textColor={colors.dimensionText}
      />,
    );
  }

  // Electrical item coordinate labels: (X; Y) near each item.
  // For OUTLET groups with count > 1, the symbol bounding box extends to
  // either side of the anchor by `count * outletWidth / 2`. Push the label
  // past the right edge of the box (plus a small gap) so it doesn't overlap
  // the outlet face plates. A semi-opaque background pill keeps it readable
  // even when it sits over a wall stripe or other UI.
  for (const pe of projectedElectrical) {
    const center = projectionToPixel(
      pe.position.alongWall,
      pe.position.fromFloor,
      wallHeight,
      scale,
      padding,
    );

    // Half-width of the visible symbol along the wall axis, in pixels.
    let halfWidthPx = 8; // default for non-outlet symbols (~SYMBOL_SIZE/2 + margin)
    if (pe.item.type === 'OUTLET') {
      const safeCount = Math.max(1, Math.round(pe.item.count));
      halfWidthPx = (safeCount * outletWidth * scale) / 2;
    }

    // Outlets can opt into inverted coordinate read-outs per axis — useful
    // when the user measures distances from the opposite wall edge or from
    // the ceiling. Display-only: the stored position is untouched.
    const invertX = pe.item.type === 'OUTLET' && getOutletInvertCoordX(pe.item.metadata);
    const invertY = pe.item.type === 'OUTLET' && getOutletInvertCoordY(pe.item.metadata);
    const displayX = invertX ? wallLen - pe.position.alongWall : pe.position.alongWall;
    const displayY = invertY ? wallHeight - pe.elevation : pe.elevation;
    const coordLabel = `(${displayX.toFixed(3)}; ${displayY.toFixed(3)})`;
    const labelX = center.x + halfWidthPx + 6;
    const labelY = center.y - 6 * fontScale;
    // Rough text-width estimate (monospace-ish): ~5.5px per char at fontSize 9.
    const labelWidth = coordLabel.length * 5.5 * fontScale + 4;
    elements.push(
      <Group key={`elec-coord-${pe.item.id}`}>
        <Rect
          x={labelX - 2}
          y={labelY - 1}
          width={labelWidth}
          height={12 * fontScale}
          fill={colors.coordLabelBg}
          cornerRadius={2}
          listening={false}
        />
        <Text
          x={labelX}
          y={labelY}
          text={coordLabel}
          fontSize={9 * fontScale}
          fill={colors.coordLabelText}
          listening={false}
        />
      </Group>,
    );
  }

  // Opening horizontal position labels
  for (const po of projectedOpenings) {
    const { rect, opening } = po;
    const openingCenter = projectionToPixel(
      rect.x + rect.width / 2,
      rect.y + rect.height,
      wallHeight,
      scale,
      padding,
    );
    elements.push(
      <Text
        key={`opening-x-${opening.id}`}
        x={openingCenter.x - 16}
        y={openingCenter.y - 22}
        text={formatM(rect.x + rect.width / 2)}
        fontSize={8 * fontScale}
        fill={colors.openingLabel}
        align="center"
        width={32}
      />,
    );
  }

  // Per-furniture dimension overlay (only when toggled on).
  // Coordinates are only projected onto an axis when the item is *near* that
  // axis — otherwise the extension lines would sprawl across the whole view
  // and add more noise than information.
  const NEAR_AXIS = 0.1; // meters
  for (const pf of projectedFurniture) {
    if (!pf.item.showProjection) continue;
    const { rect, item } = pf;
    {

      const isNearFloor = rect.y <= NEAR_AXIS;
      const isNearLeft = rect.x <= NEAR_AXIS;

      // ── Horizontal axis (along-wall) projection ──
      // If the item touches the floor we extend the dimension all the way to
      // the bottom ruler; otherwise we draw the width dimension just below
      // the item itself.
      const wLeft = projectionToPixel(rect.x, rect.y, wallHeight, scale, padding);
      const wRight = projectionToPixel(rect.x + rect.width, rect.y, wallHeight, scale, padding);
      if (isNearFloor) {
        const wLeftFloor = projectionToPixel(rect.x, 0, wallHeight, scale, padding);
        const wRightFloor = projectionToPixel(rect.x + rect.width, 0, wallHeight, scale, padding);
        elements.push(
          <DimensionLine
        fontScale={fontScale}
            key={`furn-w-${item.id}`}
            x1={wLeftFloor.x}
            y1={wLeftFloor.y}
            x2={wRightFloor.x}
            y2={wRightFloor.y}
            label={formatM(rect.width)}
            offset={32}
            horizontal
            lineColor={colors.dimensionLine}
            textColor={colors.dimensionText}
          />,
        );
        if (rect.x > 0.001) {
          const oLeft = projectionToPixel(0, 0, wallHeight, scale, padding);
          elements.push(
            <DimensionLine
        fontScale={fontScale}
              key={`furn-off-${item.id}`}
              x1={oLeft.x}
              y1={oLeft.y}
              x2={wLeftFloor.x}
              y2={wLeftFloor.y}
              label={formatM(rect.x)}
              offset={46}
              horizontal
              lineColor={colors.dimensionLine}
              textColor={colors.dimensionText}
            />,
          );
        }
      } else {
        // Inline width dimension drawn just below the bottom edge of the item
        elements.push(
          <DimensionLine
        fontScale={fontScale}
            key={`furn-w-${item.id}`}
            x1={wLeft.x}
            y1={wLeft.y}
            x2={wRight.x}
            y2={wRight.y}
            label={formatM(rect.width)}
            offset={14}
            horizontal
            lineColor={colors.dimensionLine}
            textColor={colors.dimensionText}
          />,
        );
        // Inline start-offset (distance from wall start to the left edge),
        // shown as a thin extension line + label so the user still sees the
        // horizontal position when the item is not on the floor.
        if (rect.x > 0.001) {
          const oLeft = projectionToPixel(0, rect.y, wallHeight, scale, padding);
          elements.push(
            <DimensionLine
        fontScale={fontScale}
              key={`furn-off-${item.id}`}
              x1={oLeft.x}
              y1={oLeft.y}
              x2={wLeft.x}
              y2={wLeft.y}
              label={formatM(rect.x)}
              offset={-6}
              horizontal
              lineColor={colors.dimensionLine}
              textColor={colors.dimensionText}
            />,
          );
        }
      }

      // ── Vertical axis (height) projection ──
      // If the item touches the left edge of the wall we extend to the left
      // ruler; otherwise we draw the height dimension just to the left of
      // the item itself.
      const hBottom = projectionToPixel(rect.x, rect.y, wallHeight, scale, padding);
      const hTop = projectionToPixel(rect.x, rect.y + rect.height, wallHeight, scale, padding);
      if (isNearLeft) {
        elements.push(
          <DimensionLine
        fontScale={fontScale}
            key={`furn-h-${item.id}`}
            x1={hTop.x}
            y1={hTop.y}
            x2={hBottom.x}
            y2={hBottom.y}
            label={formatM(rect.height)}
            offset={-32}
            horizontal={false}
            lineColor={colors.dimensionLine}
            textColor={colors.dimensionText}
          />,
        );
        if (rect.y > 0.001) {
          const eFloor = projectionToPixel(rect.x, 0, wallHeight, scale, padding);
          elements.push(
            <DimensionLine
        fontScale={fontScale}
              key={`furn-elev-${item.id}`}
              x1={hBottom.x}
              y1={hBottom.y}
              x2={eFloor.x}
              y2={eFloor.y}
              label={formatM(rect.y)}
              offset={-46}
              horizontal={false}
              lineColor={colors.dimensionLine}
              textColor={colors.dimensionText}
            />,
          );
        }
      } else {
        // Inline height dimension drawn just to the left of the item
        elements.push(
          <DimensionLine
        fontScale={fontScale}
            key={`furn-h-${item.id}`}
            x1={hTop.x}
            y1={hTop.y}
            x2={hBottom.x}
            y2={hBottom.y}
            label={formatM(rect.height)}
            offset={-14}
            horizontal={false}
            lineColor={colors.dimensionLine}
            textColor={colors.dimensionText}
          />,
        );
        // Inline elevation (distance from floor to the bottom of the item),
        // so wall-mounted items still show their vertical position.
        if (rect.y > 0.001) {
          const eFloor = projectionToPixel(rect.x + rect.width, 0, wallHeight, scale, padding);
          const eBottom = projectionToPixel(rect.x + rect.width, rect.y, wallHeight, scale, padding);
          elements.push(
            <DimensionLine
        fontScale={fontScale}
              key={`furn-elev-${item.id}`}
              x1={eBottom.x}
              y1={eBottom.y}
              x2={eFloor.x}
              y2={eFloor.y}
              label={formatM(rect.y)}
              offset={6}
              horizontal={false}
              lineColor={colors.dimensionLine}
              textColor={colors.dimensionText}
            />,
          );
        }
      }
    }
  }

  return <Group>{elements}</Group>;
}

