import { useMemo } from 'react';
import * as THREE from 'three';
import type { ElectricalItem, ElectricalType, Point, Wall, WallLightStyle } from '@house-plan-maker/shared';
import { wallRotationY, positionAlongWall3D, wallVector, wallNormal } from './utils/wallGeometry';
import { getWallLightStyle, getWallLightCordLength, getCeilingLampSize, getOutletDirection } from '../symbols/electrical';

interface ElectricalMeshWithHeightProps {
  readonly item: ElectricalItem;
  readonly wallMap: ReadonlyMap<string, Wall>;
  readonly wallHeight: number;
  readonly isSelected: boolean;
  readonly onSelect?: (id: string) => void;
  /** Physical width of a single outlet face plate (meters). */
  readonly outletWidth: number;
  /** Physical height of a single outlet face plate (meters). */
  readonly outletHeight: number;
  /**
   * Centroid of the room polygon (2D editor coords). Used to pick the
   * inward-pointing side of a wall so wall-mounted items (outlets,
   * switches, wall lights) sit on the room-facing face regardless of
   * which direction `wallNormal` happens to return.
   */
  readonly roomCentroid?: Point;
}

/**
 * Compute the local-space offset from the anchored origin (item.x, elevation,
 * item.y projected onto wall) to the geometric center of the outlet group's
 * bounding box. Local axes: +x along wall, +y vertical (up).
 */
function outletAnchorOffset(
  item: ElectricalItem,
  outletWidth: number,
  outletHeight: number,
): { readonly cx: number; readonly cy: number } {
  const safeCount = Math.max(1, item.count);
  const direction = getOutletDirection(item.metadata);
  const isVertical = direction === 'vertical';
  const totalWidth = isVertical ? outletWidth : safeCount * outletWidth;
  const totalHeight = isVertical ? safeCount * outletHeight : outletHeight;
  const h = item.positionAnchor.horizontal;
  const v = item.positionAnchor.vertical;
  const cx = h === 'left' ? totalWidth / 2 : h === 'right' ? -totalWidth / 2 : 0;
  // Note: in 3D +y is up, so 'top' anchor means the center is BELOW (negative y).
  const cy = v === 'top' ? -totalHeight / 2 : v === 'bottom' ? totalHeight / 2 : 0;
  return { cx, cy };
}

const ELECTRICAL_COLORS: Record<ElectricalType, string> = {
  OUTLET: '#f5f5f0',
  SWITCH: '#f5f5f0',
  JUNCTION_BOX: '#808080',
  LIGHT_CEILING: '#fff8dc',
  LIGHT_WALL: '#fff8dc',
  CABLE_ROUTE: '#ff6b35',
  WALL_CABLE: '#c0c0c0',
};

const SELECTED_COLOR = '#6fa8dc';

function degToRad(degrees: number): number {
  return (degrees * Math.PI) / 180;
}

/** Find the wall that this electrical item is attached to via O(1) map lookup. */
function findWallInMap(wallId: string | null, wallMap: ReadonlyMap<string, Wall>): Wall | null {
  if (!wallId) return null;
  return wallMap.get(wallId) ?? null;
}

/**
 * Outlet group: renders `count` adjacent face plates side by side along the
 * local x-axis (which is aligned with the wall). Each face plate is sized by
 * the room's outlet dimensions and the whole group is offset so that the
 * (item.x, item.y) anchor lies on the box edge or center as configured.
 */
function OutletMesh({
  color,
  count,
  outletWidth,
  outletHeight,
  centerX,
  centerY,
  isVertical,
}: {
  readonly color: string;
  readonly count: number;
  readonly outletWidth: number;
  readonly outletHeight: number;
  readonly centerX: number;
  readonly centerY: number;
  readonly isVertical: boolean;
}) {
  const safeCount = Math.max(1, Math.round(count));
  // Depth into the wall: stays small, just enough to be visible.
  const depth = 0.02;
  return (
    <group position={[centerX, centerY, 0]}>
      {Array.from({ length: safeCount }).map((_, i) => {
        // Center index 0..N-1 around 0 along the layout axis.
        const localX = isVertical ? 0 : (i - (safeCount - 1) / 2) * outletWidth;
        const localY = isVertical ? (i - (safeCount - 1) / 2) * outletHeight : 0;
        return (
          <mesh key={i} position={[localX, localY, 0]} castShadow>
            <boxGeometry args={[outletWidth * 0.95, outletHeight * 0.95, depth]} />
            <meshStandardMaterial color={color} roughness={0.3} />
          </mesh>
        );
      })}
    </group>
  );
}

/** Switch: slightly taller rectangular box on wall */
function SwitchMesh({ color }: { readonly color: string }) {
  return (
    <group>
      <mesh castShadow>
        <boxGeometry args={[0.06, 0.10, 0.02]} />
        <meshStandardMaterial color={color} roughness={0.3} />
      </mesh>
      {/* Toggle indicator */}
      <mesh position={[0, 0.01, 0.011]} castShadow>
        <boxGeometry args={[0.02, 0.04, 0.005]} />
        <meshStandardMaterial color="#cccccc" roughness={0.3} />
      </mesh>
    </group>
  );
}

/** Junction box: small gray box */
function JunctionBoxMesh({ color }: { readonly color: string }) {
  return (
    <mesh castShadow>
      <boxGeometry args={[0.10, 0.10, 0.05]} />
      <meshStandardMaterial color={color} roughness={0.5} />
    </mesh>
  );
}

/** Ceiling light: disc or sphere hanging from ceiling, scalable by lampSize (diameter in meters). */
function CeilingLightMesh({ color, wallHeight, lampSize }: {
  readonly color: string;
  readonly wallHeight: number;
  readonly lampSize: number;
}) {
  // lampSize is the overall diameter; derive component sizes from it.
  const radius = lampSize / 2;
  const canopyRadius = radius * 0.625; // proportional canopy
  const canopyHeight = Math.max(0.015, lampSize * 0.125);
  const dropDistance = lampSize * 0.75; // how far shade hangs below canopy

  return (
    <group position={[0, wallHeight - 0.05, 0]}>
      {/* Canopy */}
      <mesh castShadow>
        <cylinderGeometry args={[canopyRadius, canopyRadius, canopyHeight, 16]} />
        <meshStandardMaterial color="#666666" roughness={0.4} />
      </mesh>
      {/* Shade / bulb */}
      <mesh position={[0, -dropDistance, 0]}>
        <sphereGeometry args={[radius, 16, 16]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.3}
          roughness={0.2}
        />
      </mesh>
    </group>
  );
}

/** Classic wall light: half-sphere attached to wall, scalable by lampSize (diameter in meters). */
function WallLightClassic({ color, lampSize }: { readonly color: string; readonly lampSize: number }) {
  const scale = lampSize / 0.12; // 0.12 = default diameter (2 * 0.06 shade radius)
  const plateSize = 0.10 * scale;
  const shadeRadius = 0.06 * scale;

  return (
    <group>
      {/* Base plate */}
      <mesh castShadow>
        <boxGeometry args={[plateSize, plateSize, 0.02]} />
        <meshStandardMaterial color="#666666" roughness={0.4} />
      </mesh>
      {/* Half sphere shade */}
      <mesh position={[0, 0, 0.03]}>
        <sphereGeometry args={[shadeRadius, 16, 8, 0, Math.PI * 2, 0, Math.PI / 2]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.2}
          roughness={0.3}
          side={THREE.DoubleSide}
        />
      </mesh>
    </group>
  );
}

/** Pendant globe wall light: gooseneck arm with hanging glass globe (adjustable cord and size). */
function WallLightPendantGlobe({ color, cordLength, lampSize }: {
  readonly color: string;
  readonly cordLength: number;
  readonly lampSize: number;
}) {
  const armColor = '#b8860b'; // brass/dark gold
  const cordLen = Math.max(0.05, cordLength);
  const scale = lampSize / 0.14; // 0.14 = default diameter (2 * 0.07 globe radius)
  const globeRadius = 0.07 * scale;
  const armHeight = 0.12 * scale;
  const armReach = 0.06 * scale;
  const mountRadius = 0.04 * scale;
  const armThickness = 0.006 * scale;
  const canopyTop = 0.02 * scale;
  const canopyBot = 0.025 * scale;
  const filamentRadius = 0.015 * scale;

  return (
    <group>
      {/* Round wall mount plate */}
      <mesh castShadow>
        <cylinderGeometry args={[mountRadius, mountRadius, 0.015, 16]} />
        <meshStandardMaterial color={armColor} roughness={0.3} metalness={0.6} />
      </mesh>

      {/* Vertical arm going up */}
      <mesh position={[0, armHeight / 2, 0]} castShadow>
        <cylinderGeometry args={[armThickness, armThickness, armHeight, 8]} />
        <meshStandardMaterial color={armColor} roughness={0.3} metalness={0.6} />
      </mesh>

      {/* Curved gooseneck top (simplified as a horizontal segment) */}
      <mesh position={[0, armHeight, armReach / 2]} rotation={[Math.PI / 2, 0, 0]} castShadow>
        <cylinderGeometry args={[armThickness, armThickness, armReach, 8]} />
        <meshStandardMaterial color={armColor} roughness={0.3} metalness={0.6} />
      </mesh>

      {/* Cord / rope hanging down */}
      <mesh position={[0, armHeight - cordLen / 2, armReach]} castShadow>
        <cylinderGeometry args={[0.003, 0.003, cordLen, 6]} />
        <meshStandardMaterial color="#f5f0e0" roughness={0.8} />
      </mesh>

      {/* Globe canopy (brass ring at top of globe) */}
      <mesh position={[0, armHeight - cordLen, armReach]}>
        <cylinderGeometry args={[canopyTop, canopyBot, 0.015, 16]} />
        <meshStandardMaterial color={armColor} roughness={0.3} metalness={0.6} />
      </mesh>

      {/* Glass globe */}
      <mesh position={[0, armHeight - cordLen - globeRadius * 0.7, armReach]}>
        <sphereGeometry args={[globeRadius, 24, 24]} />
        <meshPhysicalMaterial
          color="#ffffff"
          transmission={0.85}
          roughness={0.05}
          thickness={0.5}
          ior={1.5}
          transparent
          opacity={0.35}
          side={THREE.DoubleSide}
        />
      </mesh>

      {/* Bulb filament inside globe */}
      <mesh position={[0, armHeight - cordLen - globeRadius * 0.7, armReach]}>
        <sphereGeometry args={[filamentRadius, 8, 8]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.5}
          roughness={0.2}
        />
      </mesh>
    </group>
  );
}

/** Sconce pointing upward */
function WallLightSconceUp({ color, lampSize }: { readonly color: string; readonly lampSize: number }) {
  const scale = lampSize / 0.10; // 0.10 = default diameter (2 * 0.05 cone radius)
  const plateW = 0.08 * scale;
  const plateH = 0.12 * scale;
  const coneRadius = 0.05 * scale;
  const coneHeight = 0.10 * scale;
  const glowRadius = 0.02 * scale;

  return (
    <group>
      {/* Base plate */}
      <mesh castShadow>
        <boxGeometry args={[plateW, plateH, 0.02]} />
        <meshStandardMaterial color="#666666" roughness={0.4} />
      </mesh>
      {/* Upward cone shade */}
      <mesh position={[0, coneHeight * 0.4, 0.03]} rotation={[0, 0, 0]}>
        <coneGeometry args={[coneRadius, coneHeight, 16, 1, true]} />
        <meshStandardMaterial
          color="#e0d5c0"
          roughness={0.5}
          side={THREE.DoubleSide}
        />
      </mesh>
      {/* Light glow */}
      <mesh position={[0, coneHeight * 0.6, 0.03]}>
        <sphereGeometry args={[glowRadius, 8, 8]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.4}
          roughness={0.2}
        />
      </mesh>
    </group>
  );
}

/** Sconce pointing downward */
function WallLightSconceDown({ color, lampSize }: { readonly color: string; readonly lampSize: number }) {
  const scale = lampSize / 0.10; // 0.10 = default diameter (2 * 0.05 cone radius)
  const plateW = 0.08 * scale;
  const plateH = 0.12 * scale;
  const coneRadius = 0.05 * scale;
  const coneHeight = 0.10 * scale;
  const glowRadius = 0.02 * scale;

  return (
    <group>
      {/* Base plate */}
      <mesh castShadow>
        <boxGeometry args={[plateW, plateH, 0.02]} />
        <meshStandardMaterial color="#666666" roughness={0.4} />
      </mesh>
      {/* Downward cone shade (rotated 180°) */}
      <mesh position={[0, -coneHeight * 0.4, 0.03]} rotation={[Math.PI, 0, 0]}>
        <coneGeometry args={[coneRadius, coneHeight, 16, 1, true]} />
        <meshStandardMaterial
          color="#e0d5c0"
          roughness={0.5}
          side={THREE.DoubleSide}
        />
      </mesh>
      {/* Light glow */}
      <mesh position={[0, -coneHeight * 0.6, 0.03]}>
        <sphereGeometry args={[glowRadius, 8, 8]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.4}
          roughness={0.2}
        />
      </mesh>
    </group>
  );
}

/** Wood cube wall sconce: a wooden block mounted on the wall emitting warm light up and down. */
function WallLightWoodCube({ color, lampSize }: { readonly color: string; readonly lampSize: number }) {
  const scale = lampSize / 0.12;
  const cubeW = 0.12 * scale;
  const cubeH = 0.12 * scale;
  const cubeD = 0.10 * scale;
  const slitH = 0.008 * scale;
  const glowRadius = 0.02 * scale;

  return (
    <group>
      {/* Wooden cube body */}
      <mesh position={[0, 0, cubeD / 2]} castShadow>
        <boxGeometry args={[cubeW, cubeH, cubeD]} />
        <meshStandardMaterial color="#b08050" roughness={0.75} metalness={0.0} />
      </mesh>
      {/* Top light slit glow */}
      <mesh position={[0, cubeH / 2 + slitH, cubeD / 2]}>
        <boxGeometry args={[cubeW * 0.85, slitH * 2, cubeD * 0.6]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.6}
          transparent
          opacity={0.7}
        />
      </mesh>
      {/* Bottom light slit glow */}
      <mesh position={[0, -cubeH / 2 - slitH, cubeD / 2]}>
        <boxGeometry args={[cubeW * 0.85, slitH * 2, cubeD * 0.6]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.6}
          transparent
          opacity={0.7}
        />
      </mesh>
      {/* Glow sphere above */}
      <mesh position={[0, cubeH / 2 + glowRadius * 2, cubeD / 2]}>
        <sphereGeometry args={[glowRadius, 8, 8]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.3}
          transparent
          opacity={0.5}
        />
      </mesh>
      {/* Glow sphere below */}
      <mesh position={[0, -cubeH / 2 - glowRadius * 2, cubeD / 2]}>
        <sphereGeometry args={[glowRadius, 8, 8]} />
        <meshStandardMaterial
          color={color}
          emissive={color}
          emissiveIntensity={0.3}
          transparent
          opacity={0.5}
        />
      </mesh>
    </group>
  );
}

/** Wall light: renders the appropriate style variant */
function WallLightMesh({ color, style, cordLength, lampSize }: {
  readonly color: string;
  readonly style: WallLightStyle;
  readonly cordLength: number;
  readonly lampSize: number;
}) {
  switch (style) {
    case 'pendant-globe':
      return <WallLightPendantGlobe color={color} cordLength={cordLength} lampSize={lampSize} />;
    case 'sconce-up':
      return <WallLightSconceUp color={color} lampSize={lampSize} />;
    case 'sconce-down':
      return <WallLightSconceDown color={color} lampSize={lampSize} />;
    case 'wood-cube':
      return <WallLightWoodCube color={color} lampSize={lampSize} />;
    case 'classic':
    default:
      return <WallLightClassic color={color} lampSize={lampSize} />;
  }
}

/** Wall cable: round plate on wall with a cable stub protruding into the room. */
function WallCableMesh({ color }: { readonly color: string }) {
  return (
    <group>
      {/* Wall plate */}
      <mesh castShadow>
        <cylinderGeometry args={[0.03, 0.03, 0.01, 16]} />
        <meshStandardMaterial color={color} roughness={0.4} />
      </mesh>
      {/* Cable stub protruding from the wall */}
      <mesh position={[0, 0, 0.025]} rotation={[Math.PI / 2, 0, 0]} castShadow>
        <cylinderGeometry args={[0.006, 0.006, 0.04, 8]} />
        <meshStandardMaterial color="#333333" roughness={0.7} />
      </mesh>
    </group>
  );
}

/** Cable route: small orange marker */
function CableRouteMesh({ color }: { readonly color: string }) {
  return (
    <mesh castShadow>
      <sphereGeometry args={[0.03, 8, 8]} />
      <meshStandardMaterial color={color} roughness={0.5} />
    </mesh>
  );
}

/** Compute 3D position for an electrical item. */
function useElectricalPosition(
  item: ElectricalItem,
  wall: Wall | null,
  roomCentroid: Point | null,
): [number, number, number] {
  return useMemo<[number, number, number]>(() => {
    if (item.type === 'LIGHT_CEILING') {
      return [item.x, 0, item.y];
    }

    if (wall && item.wallId) {
      const { nx, ny } = wallNormal(wall);
      const { length } = wallVector(wall);
      const dx = item.x - wall.startX;
      const dy = item.y - wall.startY;
      const wallDx = wall.endX - wall.startX;
      const wallDy = wall.endY - wall.startY;
      const t = length > 0 ? (dx * wallDx + dy * wallDy) / (length * length) : 0;
      const clampedT = Math.max(0, Math.min(1, t));
      const [wx, wz] = positionAlongWall3D(wall, clampedT * length);

      // The wall's stored line is its INNER face (WallMesh shifts the box
      // outward by thickness/2 so the inner face sits on the polygon edge).
      // We want the outlet flush against that inner face, protruding a
      // small gap (~1.5cm) INTO the room. `wallNormal` returns one of the
      // two perpendiculars without knowing which side is the room interior;
      // use the centroid to pick the inward-pointing sign. This is the same
      // trick WallMesh uses to decide its outward shift.
      let inwardSign = 1;
      if (roomCentroid) {
        const midX = (wall.startX + wall.endX) / 2;
        const midY = (wall.startY + wall.endY) / 2;
        // Vector from wall midpoint to centroid points INTO the room.
        const toCentroidX = roomCentroid.x - midX;
        const toCentroidY = roomCentroid.y - midY;
        // Positive dot product → normal already points inward; otherwise flip.
        inwardSign = nx * toCentroidX + ny * toCentroidY >= 0 ? 1 : -1;
      }

      const offset = 0.015 * inwardSign;
      const elevation = item.elevationFromFloor ?? 1.2;
      return [wx + nx * offset, elevation, wz + ny * offset];
    }

    const elevation = item.elevationFromFloor ?? 0.3;
    return [item.x, elevation, item.y];
  }, [item, wall, roomCentroid]);
}

export function ElectricalMeshWithHeight({
  item,
  wallMap,
  wallHeight,
  isSelected,
  onSelect,
  outletWidth,
  outletHeight,
  roomCentroid,
}: ElectricalMeshWithHeightProps) {
  const color = isSelected ? SELECTED_COLOR : ELECTRICAL_COLORS[item.type];
  const wall = useMemo(() => {
    // Try exact wallId match first
    const exact = findWallInMap(item.wallId, wallMap);
    if (exact) return exact;
    // Fallback: find nearest wall by proximity (wallId may be stale after save)
    if (item.wallId || item.type === 'OUTLET' || item.type === 'SWITCH' || item.type === 'LIGHT_WALL' || item.type === 'JUNCTION_BOX') {
      let bestWall: Wall | null = null;
      let bestDist = 0.3; // max 30cm threshold
      for (const w of wallMap.values()) {
        const dx = item.x - w.startX;
        const dy = item.y - w.startY;
        const wx = w.endX - w.startX;
        const wy = w.endY - w.startY;
        const len = Math.sqrt(wx * wx + wy * wy);
        if (len === 0) continue;
        const perpDist = Math.abs(dx * (-wy / len) + dy * (wx / len));
        const along = (dx * wx + dy * wy) / len;
        if (perpDist < bestDist && along >= -0.1 && along <= len + 0.1) {
          bestDist = perpDist;
          bestWall = w;
        }
      }
      return bestWall;
    }
    return null;
  }, [item, wallMap]);
  const position = useElectricalPosition(item, wall, roomCentroid ?? null);

  const rotY = useMemo(() => {
    if (wall) return wallRotationY(wall);
    return -degToRad(item.rotation);
  }, [wall, item.rotation]);

  return (
    <group
      position={position}
      rotation={[0, rotY, 0]}
      onClick={onSelect ? (e) => { e.stopPropagation(); onSelect(item.id); } : undefined}
    >
      {item.type === 'OUTLET' && (() => {
        const offset = outletAnchorOffset(item, outletWidth, outletHeight);
        return (
          <OutletMesh
            color={color}
            count={item.count}
            outletWidth={outletWidth}
            outletHeight={outletHeight}
            centerX={offset.cx}
            centerY={offset.cy}
            isVertical={getOutletDirection(item.metadata) === 'vertical'}
          />
        );
      })()}
      {item.type === 'SWITCH' && <SwitchMesh color={color} />}
      {item.type === 'JUNCTION_BOX' && <JunctionBoxMesh color={color} />}
      {item.type === 'LIGHT_CEILING' && (
        <CeilingLightMesh color={color} wallHeight={wallHeight} lampSize={getCeilingLampSize(item.metadata)} />
      )}
      {item.type === 'LIGHT_WALL' && (
        <WallLightMesh
          color={color}
          style={getWallLightStyle(item.metadata)}
          cordLength={getWallLightCordLength(item.metadata)}
          lampSize={getCeilingLampSize(item.metadata)}
        />
      )}
      {item.type === 'CABLE_ROUTE' && <CableRouteMesh color={color} />}
      {item.type === 'WALL_CABLE' && <WallCableMesh color={color} />}
    </group>
  );
}
