import { memo, useMemo } from 'react';
import { Group, Line, Text, Rect } from 'react-konva';
import type { Point } from '@house-plan-maker/shared';
import { useCanvasColors } from '../utils/canvasThemeColors';

interface GridLayerProps {
  readonly zoom: number;
  readonly panOffset: Point;
  readonly stageWidth: number;
  readonly stageHeight: number;
  readonly gridSize: number;
  readonly visible: boolean;
}
/** Ruler size in pixels. */
const RULER_SIZE = 24;

export const GridLayer = memo(function GridLayer({
  zoom,
  panOffset,
  stageWidth,
  stageHeight,
  gridSize,
  visible,
}: GridLayerProps) {
  const colors = useCanvasColors();
  const gridLines = useMemo(() => {
    if (!visible) return { lines: [], majorLines: [] };

    const gridPixels = gridSize * zoom;

    // Skip grid lines if they would be too dense
    if (gridPixels < 8) {
      return { lines: [], majorLines: [] };
    }

    // Calculate visible world bounds
    const worldLeft = -panOffset.x / zoom;
    const worldTop = -panOffset.y / zoom;
    const worldRight = (stageWidth - panOffset.x) / zoom;
    const worldBottom = (stageHeight - panOffset.y) / zoom;

    // Snap to grid boundaries
    const startX = Math.floor(worldLeft / gridSize) * gridSize;
    const endX = Math.ceil(worldRight / gridSize) * gridSize;
    const startY = Math.floor(worldTop / gridSize) * gridSize;
    const endY = Math.ceil(worldBottom / gridSize) * gridSize;

    const lines: { points: number[]; isMajor: boolean }[] = [];

    // Steps per meter
    const stepsPerMeter = Math.round(1 / gridSize);

    // Vertical lines
    for (let x = startX; x <= endX; x += gridSize) {
      const screenX = x * zoom + panOffset.x;
      const roundedX = Math.round(x * 1000) / 1000; // avoid floating point noise
      const isMajor = stepsPerMeter > 0 && Math.abs(roundedX % 1) < gridSize / 2;
      lines.push({
        points: [screenX, 0, screenX, stageHeight],
        isMajor,
      });
    }

    // Horizontal lines
    for (let y = startY; y <= endY; y += gridSize) {
      const screenY = y * zoom + panOffset.y;
      const roundedY = Math.round(y * 1000) / 1000;
      const isMajor = stepsPerMeter > 0 && Math.abs(roundedY % 1) < gridSize / 2;
      lines.push({
        points: [0, screenY, stageWidth, screenY],
        isMajor,
      });
    }

    const normal = lines.filter((l) => !l.isMajor);
    const major = lines.filter((l) => l.isMajor);

    return { lines: normal, majorLines: major };
  }, [visible, zoom, panOffset, stageWidth, stageHeight, gridSize]);

  const rulerMarks = useMemo(() => {
    // Determine ruler step: aim for marks every ~60-120 pixels
    let rulerStep = 1; // meters
    const pixelsPerMeter = zoom;
    if (pixelsPerMeter < 30) rulerStep = 5;
    else if (pixelsPerMeter < 60) rulerStep = 2;
    else if (pixelsPerMeter > 300) rulerStep = 0.5;
    else if (pixelsPerMeter > 600) rulerStep = 0.1;

    const worldLeft = -panOffset.x / zoom;
    const worldTop = -panOffset.y / zoom;
    const worldRight = (stageWidth - panOffset.x) / zoom;
    const worldBottom = (stageHeight - panOffset.y) / zoom;

    const hMarks: { screenX: number; label: string }[] = [];
    const startX = Math.floor(worldLeft / rulerStep) * rulerStep;
    const endX = Math.ceil(worldRight / rulerStep) * rulerStep;
    for (let x = startX; x <= endX; x += rulerStep) {
      const screenX = x * zoom + panOffset.x;
      if (screenX >= RULER_SIZE && screenX <= stageWidth) {
        hMarks.push({
          screenX,
          label: formatRulerLabel(x),
        });
      }
    }

    const vMarks: { screenY: number; label: string }[] = [];
    const startY = Math.floor(worldTop / rulerStep) * rulerStep;
    const endY = Math.ceil(worldBottom / rulerStep) * rulerStep;
    for (let y = startY; y <= endY; y += rulerStep) {
      const screenY = y * zoom + panOffset.y;
      if (screenY >= RULER_SIZE && screenY <= stageHeight) {
        vMarks.push({
          screenY,
          label: formatRulerLabel(y),
        });
      }
    }

    return { hMarks, vMarks };
  }, [zoom, panOffset, stageWidth, stageHeight]);

  return (
    <Group listening={false}>
      {/* Grid lines */}
      {visible &&
        gridLines.lines.map((line, i) => (
          <Line
            key={`g-${i}`}
            points={line.points}
            stroke={colors.gridLine}
            strokeWidth={0.5}
            listening={false}
          />
        ))}
      {visible &&
        gridLines.majorLines.map((line, i) => (
          <Line
            key={`gm-${i}`}
            points={line.points}
            stroke={colors.gridLineMajor}
            strokeWidth={1}
            listening={false}
          />
        ))}

      {/* Horizontal ruler */}
      <Rect
        x={0}
        y={0}
        width={stageWidth}
        height={RULER_SIZE}
        fill={colors.rulerBg}
        listening={false}
      />
      {rulerMarks.hMarks.map((mark, i) => (
        <Text
          key={`rh-${i}`}
          x={mark.screenX - 12}
          y={4}
          text={mark.label}
          fontSize={9}
          fill={colors.rulerText}
          listening={false}
        />
      ))}
      {rulerMarks.hMarks.map((mark, i) => (
        <Line
          key={`rht-${i}`}
          points={[mark.screenX, RULER_SIZE - 6, mark.screenX, RULER_SIZE]}
          stroke={colors.rulerText}
          strokeWidth={1}
          listening={false}
        />
      ))}

      {/* Vertical ruler */}
      <Rect
        x={0}
        y={0}
        width={RULER_SIZE}
        height={stageHeight}
        fill={colors.rulerBg}
        listening={false}
      />
      {rulerMarks.vMarks.map((mark, i) => (
        <Text
          key={`rv-${i}`}
          x={2}
          y={mark.screenY - 4}
          text={mark.label}
          fontSize={9}
          fill={colors.rulerText}
          listening={false}
        />
      ))}
      {rulerMarks.vMarks.map((mark, i) => (
        <Line
          key={`rvt-${i}`}
          points={[RULER_SIZE - 6, mark.screenY, RULER_SIZE, mark.screenY]}
          stroke={colors.rulerText}
          strokeWidth={1}
          listening={false}
        />
      ))}

      {/* Corner square */}
      <Rect
        x={0}
        y={0}
        width={RULER_SIZE}
        height={RULER_SIZE}
        fill={colors.rulerBg}
        listening={false}
      />
    </Group>
  );
});

function formatRulerLabel(meters: number): string {
  const rounded = Math.round(meters * 100) / 100;
  if (Number.isInteger(rounded)) return `${rounded}m`;
  return `${rounded}`;
}
