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

interface OpeningLayerProps {
  readonly openings: readonly WallOpening[];
  readonly walls: readonly Wall[];
  readonly roomShape: readonly Point[];
  readonly zoom: number;
  readonly panOffset: Point;
  readonly selectedIds: ReadonlySet<string>;
  /** Optional preview for door/window placement. */
  readonly preview?: OpeningPreview | null;
}

export interface OpeningPreview {
  readonly wall: Wall;
  readonly positionAlongWall: number;
  readonly width: number;
  readonly type: 'DOOR' | 'WINDOW';
  readonly isValid: boolean;
}

const DOOR_COLOR = '#7048e8';
const DOOR_ARC_COLOR = 'rgba(112, 72, 232, 0.15)';
const WINDOW_COLOR = '#1c7ed6';
const SELECTED_COLOR = '#4c6ef5';
const PREVIEW_OPACITY = 0.5;
/** Gap in wall fill (background color) behind openings. */
const WALL_GAP_COLOR = '#f8f9fa';

export const OpeningLayer = memo(function OpeningLayer({
  openings,
  walls,
  roomShape,
  zoom,
  panOffset,
  selectedIds,
  preview,
}: OpeningLayerProps) {
  const wallMap = useMemo(() => {
    const map = new Map<string, Wall>();
    for (const wall of walls) {
      map.set(wall.id, wall);
    }
    return map;
  }, [walls]);

  const renderedOpenings = useMemo(() => {
    return openings
      .map((opening) => {
        const wall = wallMap.get(opening.wallId);
        if (!wall) return null;

        const pos = openingWorldPosition(opening, wall);
        const isSelected = selectedIds.has(opening.id);

        return {
          opening,
          wall,
          pos,
          isSelected,
        };
      })
      .filter(Boolean) as {
      opening: WallOpening;
      wall: Wall;
      pos: ReturnType<typeof openingWorldPosition>;
      isSelected: boolean;
    }[];
  }, [openings, wallMap, selectedIds]);

  // Compute room centroid to determine which side of each wall is "inward"
  const centroid = useMemo(
    () => (roomShape.length >= 3 ? polygonCentroid(roomShape) : { x: 0, y: 0 }),
    [roomShape],
  );

  return (
    <Group>
      {renderedOpenings.map(({ opening, wall, pos, isSelected }) => {
        const screenCenter = toScreen(pos.center, zoom, panOffset);
        const angle = wallAngle(wall);
        const angleDeg = (angle * 180) / Math.PI;
        const halfWidthPx = (opening.width / 2) * zoom;

        if (opening.type === 'DOOR') {
          // Determine if arc should flip: the perpendicular "negative Y" in local wall
          // coords should point toward the room center. If it doesn't, flip.
          const nx = -Math.sin(angle);
          const ny = Math.cos(angle);
          const wallMidX = (wall.startX + wall.endX) / 2;
          const wallMidY = (wall.startY + wall.endY) / 2;
          const toCenterX = centroid.x - wallMidX;
          const toCenterY = centroid.y - wallMidY;
          // dot > 0 means the normal points toward center (inward) — no flip needed
          const dot = nx * toCenterX + ny * toCenterY;
          const arcFlip = dot < 0;

          return (
            <DoorSymbol
              key={opening.id}
              x={screenCenter.x}
              y={screenCenter.y}
              angleDeg={angleDeg}
              halfWidthPx={halfWidthPx}
              wallThicknessPx={wall.thickness * zoom}
              isSelected={isSelected}
              openDirection={opening.openDirection}
              flipInward={arcFlip}
            />
          );
        }

        // WINDOW
        return (
          <WindowSymbol
            key={opening.id}
            x={screenCenter.x}
            y={screenCenter.y}
            angleDeg={angleDeg}
            halfWidthPx={halfWidthPx}
            wallThicknessPx={wall.thickness * zoom}
            isSelected={isSelected}
          />
        );
      })}

      {/* Placement preview */}
      {preview && (
        <PreviewSymbol
          wall={preview.wall}
          positionAlongWall={preview.positionAlongWall}
          width={preview.width}
          type={preview.type}
          isValid={preview.isValid}
          zoom={zoom}
          panOffset={panOffset}
        />
      )}
    </Group>
  );
});

// ── Door Symbol (arc showing swing direction) ──

interface DoorSymbolProps {
  readonly x: number;
  readonly y: number;
  readonly angleDeg: number;
  readonly halfWidthPx: number;
  readonly wallThicknessPx: number;
  readonly isSelected: boolean;
  readonly openDirection?: DoorOpenDirection;
  /** Flip the arc direction when the default "inward" points outside the room. */
  readonly flipInward?: boolean;
}

function DoorSymbol({ x, y, angleDeg, halfWidthPx, wallThicknessPx, isSelected, openDirection = 'LEFT', flipInward = false }: DoorSymbolProps) {
  const color = isSelected ? SELECTED_COLOR : DOOR_COLOR;
  const doorWidthPx = halfWidthPx * 2;

  // The four enum values encode the two orthogonal axes (hinge side × swing
  // direction) — this mapping keeps LEFT/RIGHT/OUTWARD intact and gives
  // INWARD its own visual (right hinge swinging outward) so it no longer
  // collides with LEFT.
  const isRight = openDirection === 'RIGHT' || openDirection === 'INWARD';
  const isOutward = openDirection === 'OUTWARD' || openDirection === 'INWARD';

  // Mirror the entire door group for RIGHT hinge
  const groupScaleX = isRight ? -1 : 1;

  // Hinge is always at -halfWidthPx in local coords (left edge); mirror flips it
  const hingeX = -halfWidthPx;

  // Determine inward direction: flipInward corrects for wall orientation
  const inwardIsNegativeY = flipInward ? false : true;
  const swingInward = !isOutward;
  const leafGoesNegativeY = swingInward ? inwardIsNegativeY : !inwardIsNegativeY;

  const leafEndY = leafGoesNegativeY ? -doorWidthPx : doorWidthPx;
  const arcRotation = leafGoesNegativeY ? -90 : 0;

  return (
    <Group x={x} y={y} rotation={angleDeg} scaleX={groupScaleX}>
      {/* Wall gap background */}
      <Line
        points={[-halfWidthPx, -wallThicknessPx / 2, halfWidthPx, -wallThicknessPx / 2]}
        stroke={WALL_GAP_COLOR}
        strokeWidth={wallThicknessPx}
        listening={false}
      />
      {/* Door leaf line */}
      <Line
        points={[hingeX, 0, hingeX, leafEndY]}
        stroke={color}
        strokeWidth={2}
        listening={false}
      />
      {/* Arc showing swing */}
      <Arc
        x={hingeX}
        y={0}
        innerRadius={0}
        outerRadius={doorWidthPx}
        angle={90}
        rotation={arcRotation}
        fill={DOOR_ARC_COLOR}
        stroke={color}
        strokeWidth={1}
        dash={[4, 4]}
        listening={false}
      />
      {/* Door frame marks */}
      <Line
        points={[-halfWidthPx, -wallThicknessPx / 2, -halfWidthPx, wallThicknessPx / 2]}
        stroke={color}
        strokeWidth={2}
        listening={false}
      />
      <Line
        points={[halfWidthPx, -wallThicknessPx / 2, halfWidthPx, wallThicknessPx / 2]}
        stroke={color}
        strokeWidth={2}
        listening={false}
      />
    </Group>
  );
}

// ── Window Symbol (parallel lines in wall) ──

interface WindowSymbolProps {
  readonly x: number;
  readonly y: number;
  readonly angleDeg: number;
  readonly halfWidthPx: number;
  readonly wallThicknessPx: number;
  readonly isSelected: boolean;
}

function WindowSymbol({ x, y, angleDeg, halfWidthPx, wallThicknessPx, isSelected }: WindowSymbolProps) {
  const color = isSelected ? SELECTED_COLOR : WINDOW_COLOR;
  const halfThick = wallThicknessPx / 2;
  const innerOffset = halfThick * 0.3;

  return (
    <Group x={x} y={y} rotation={angleDeg}>
      {/* Wall gap background */}
      <Line
        points={[-halfWidthPx, -halfThick, halfWidthPx, -halfThick]}
        stroke={WALL_GAP_COLOR}
        strokeWidth={wallThicknessPx}
        listening={false}
      />
      {/* Outer parallel lines */}
      <Line
        points={[-halfWidthPx, -halfThick, halfWidthPx, -halfThick]}
        stroke={color}
        strokeWidth={2}
        listening={false}
      />
      <Line
        points={[-halfWidthPx, halfThick, halfWidthPx, halfThick]}
        stroke={color}
        strokeWidth={2}
        listening={false}
      />
      {/* Inner parallel lines (glass representation) */}
      <Line
        points={[-halfWidthPx, -innerOffset, halfWidthPx, -innerOffset]}
        stroke={color}
        strokeWidth={1}
        listening={false}
      />
      <Line
        points={[-halfWidthPx, innerOffset, halfWidthPx, innerOffset]}
        stroke={color}
        strokeWidth={1}
        listening={false}
      />
      {/* End caps */}
      <Line
        points={[-halfWidthPx, -halfThick, -halfWidthPx, halfThick]}
        stroke={color}
        strokeWidth={2}
        listening={false}
      />
      <Line
        points={[halfWidthPx, -halfThick, halfWidthPx, halfThick]}
        stroke={color}
        strokeWidth={2}
        listening={false}
      />
    </Group>
  );
}

// ── Preview Symbol ──

interface PreviewSymbolProps {
  readonly wall: Wall;
  readonly positionAlongWall: number;
  readonly width: number;
  readonly type: 'DOOR' | 'WINDOW';
  readonly isValid: boolean;
  readonly zoom: number;
  readonly panOffset: Point;
}

function PreviewSymbol({ wall, positionAlongWall, width, type, isValid: _isValid, zoom, panOffset }: PreviewSymbolProps) {
  const tempOpening: WallOpening = {
    id: 'preview',
    roomId: '',
    wallId: wall.id,
    type,
    positionAlongWall,
    width,
    height: 0,
    elevationFromFloor: 0,
    openDirection: 'LEFT',
    positionAnchor: { horizontal: 'middle', vertical: 'bottom' },
    gridCols: 2,
    gridRows: 2,
    slopeDepth: 0,
    frameThickness: 0.03,
  };

  const pos = openingWorldPosition(tempOpening, wall);
  const screenCenter = toScreen(pos.center, zoom, panOffset);
  const angleDeg = (wallAngle(wall) * 180) / Math.PI;
  const halfWidthPx = (width / 2) * zoom;
  const wallThicknessPx = wall.thickness * zoom;
  return (
    <Group opacity={PREVIEW_OPACITY}>
      {type === 'DOOR' ? (
        <DoorSymbol
          x={screenCenter.x}
          y={screenCenter.y}
          angleDeg={angleDeg}
          halfWidthPx={halfWidthPx}
          wallThicknessPx={wallThicknessPx}
          isSelected={false}
        />
      ) : (
        <WindowSymbol
          x={screenCenter.x}
          y={screenCenter.y}
          angleDeg={angleDeg}
          halfWidthPx={halfWidthPx}
          wallThicknessPx={wallThicknessPx}
          isSelected={false}
        />
      )}
    </Group>
  );
}

function toScreen(point: Point, zoom: number, panOffset: Point): { x: number; y: number } {
  return {
    x: point.x * zoom + panOffset.x,
    y: point.y * zoom + panOffset.y,
  };
}
