import { useFrame } from '@react-three/fiber'
import { type AnyNodeId, type CeilingNode, sceneRegistry, useScene } from '@pascal-app/core'
import * as THREE from 'three'

function ensureUv2Attribute(geometry: THREE.BufferGeometry) {
  const uv = geometry.getAttribute('uv')
  if (!uv) return

  geometry.setAttribute('uv2', new THREE.Float32BufferAttribute(Array.from(uv.array), 2))
}

// ============================================================================
// CEILING SYSTEM
// ============================================================================

export const CeilingSystem = () => {
  const dirtyNodes = useScene((state) => state.dirtyNodes)
  const clearDirty = useScene((state) => state.clearDirty)

  useFrame(() => {
    if (dirtyNodes.size === 0) return

    const nodes = useScene.getState().nodes
    // Process dirty ceilings
    dirtyNodes.forEach((id) => {
      const node = nodes[id]
      if (!node || node.type !== 'ceiling') return

      const mesh = sceneRegistry.nodes.get(id) as THREE.Mesh
      if (mesh) {
        updateCeilingGeometry(node as CeilingNode, mesh)
        clearDirty(id as AnyNodeId)
      }
      // If mesh not found, keep it dirty for next frame
    })
  })

  return null
}

/**
 * Updates the geometry for a single ceiling
 */
function updateCeilingGeometry(node: CeilingNode, mesh: THREE.Mesh) {
  const newGeo = generateCeilingGeometry(node)

  mesh.geometry.dispose()
  mesh.geometry = newGeo

  const gridMesh = mesh.getObjectByName('ceiling-grid') as THREE.Mesh
  if (gridMesh) {
    gridMesh.geometry.dispose()
    gridMesh.geometry = newGeo
  }

  // Position at the ceiling height
  mesh.position.y = (node.height ?? 2.5) - 0.01 // Slight offset to avoid z-fighting with upper-level slabs
}

/**
 * Generates flat ceiling geometry from polygon (no extrusion)
 */
export function generateCeilingGeometry(ceilingNode: CeilingNode): THREE.BufferGeometry {
  const polygon = ceilingNode.polygon

  if (polygon.length < 3) {
    return new THREE.BufferGeometry()
  }

  // Create shape from polygon
  // Shape is in X-Y plane, we'll rotate to X-Z plane
  const shape = new THREE.Shape()
  const firstPt = polygon[0]!

  // Negate Y (which becomes Z) to get correct orientation after rotation
  shape.moveTo(firstPt[0], -firstPt[1])

  for (let i = 1; i < polygon.length; i++) {
    const pt = polygon[i]!
    shape.lineTo(pt[0], -pt[1])
  }
  shape.closePath()

  // Add holes to the shape
  const holes = ceilingNode.holes || []
  for (const holePolygon of holes) {
    if (holePolygon.length < 3) continue

    const holePath = new THREE.Path()
    const holeFirstPt = holePolygon[0]!
    holePath.moveTo(holeFirstPt[0], -holeFirstPt[1])

    for (let i = 1; i < holePolygon.length; i++) {
      const pt = holePolygon[i]!
      holePath.lineTo(pt[0], -pt[1])
    }
    holePath.closePath()

    shape.holes.push(holePath)
  }

  // Create flat shape geometry (no extrusion)
  const geometry = new THREE.ShapeGeometry(shape)

  // Rotate so the shape lies flat in X-Z plane
  geometry.rotateX(-Math.PI / 2)
  geometry.computeVertexNormals()
  ensureUv2Attribute(geometry)

  return geometry
}
