'use client'

import {
  type AnyNode,
  type AnyNodeId,
  type RoofNode,
  type RoofSegmentNode,
  SKYLIGHT_TYPE_ORDER,
  SKYLIGHT_TYPE_PRESETS,
  type SkylightNode,
  type SkylightType,
  sceneRegistry,
  useLiveNodeOverrides,
  useScene,
} from '@pascal-app/core'
import {
  ActionButton,
  ActionGroup,
  PanelSection,
  PanelWrapper,
  SegmentedControl,
  SliderControl,
  triggerSFX,
} from '@pascal-app/editor'
import { useViewer } from '@pascal-app/viewer'
import { Trash2 } from 'lucide-react'
import { useCallback } from 'react'
import { Vector3 } from 'three'

const cn = (...classes: Array<string | false | undefined | null>): string =>
  classes.filter(Boolean).join(' ')

export default function SkylightPanel() {
  const selectedId = useViewer((s) => s.selection.selectedIds[0])
  const setSelection = useViewer((s) => s.setSelection)
  const updateNode = useScene((s) => s.updateNode)
  const deleteNode = useScene((s) => s.deleteNode)

  const storeNode = useScene((s) =>
    selectedId ? (s.nodes[selectedId as AnyNode['id']] as SkylightNode | undefined) : undefined,
  )
  const overrides = useLiveNodeOverrides((s) =>
    selectedId ? (s.get(selectedId as AnyNodeId) as Partial<SkylightNode> | undefined) : undefined,
  )
  const node = storeNode && overrides ? ({ ...storeNode, ...overrides } as SkylightNode) : storeNode

  const handleUpdate = useCallback(
    (updates: Partial<SkylightNode>) => {
      if (!selectedId) return
      updateNode(selectedId as AnyNode['id'], updates)
    },
    [selectedId, updateNode],
  )

  const previewProp = useCallback(
    (updates: Partial<SkylightNode>) => {
      if (!selectedId) return
      useLiveNodeOverrides.getState().set(selectedId as AnyNodeId, updates)
    },
    [selectedId],
  )
  const commitProp = useCallback(
    (updates: Partial<SkylightNode>) => {
      if (!selectedId) return
      updateNode(selectedId as AnyNode['id'], updates)
      if (updates.roofSegmentId !== undefined) {
        const state = useScene.getState()
        const prev = node?.roofSegmentId
        if (prev) state.dirtyNodes.add(prev as AnyNodeId)
        state.dirtyNodes.add(updates.roofSegmentId as AnyNodeId)
        state.dirtyNodes.add(selectedId as AnyNodeId)
      }
      useLiveNodeOverrides.getState().clear(selectedId as AnyNodeId)
    },
    [node, selectedId, updateNode],
  )

  const handleClose = useCallback(() => {
    setSelection({ selectedIds: [] })
  }, [setSelection])

  const handleBack = useCallback(() => {
    if (node?.roofSegmentId) {
      setSelection({ selectedIds: [node.roofSegmentId as AnyNode['id']] })
    }
  }, [node?.roofSegmentId, setSelection])

  const handleDelete = useCallback(() => {
    if (!(selectedId && node)) return
    triggerSFX('sfx:item-delete')
    const segmentId = node.roofSegmentId
    if (segmentId) {
      const state = useScene.getState()
      const segment = state.nodes[segmentId as AnyNodeId] as RoofSegmentNode | undefined
      if (segment) {
        state.updateNode(segmentId as AnyNode['id'], {
          children: (segment.children ?? []).filter((id) => id !== selectedId),
        })
      }
    }
    deleteNode(selectedId as AnyNodeId)
    if (segmentId) {
      useScene.getState().dirtyNodes.add(segmentId as AnyNodeId)
      setSelection({ selectedIds: [segmentId as AnyNode['id']] })
    } else {
      setSelection({ selectedIds: [] })
    }
  }, [selectedId, node, deleteNode, setSelection])

  if (!(node && node.type === 'skylight' && selectedId)) return null

  const scenestate = useScene.getState()
  const segment = node.roofSegmentId
    ? (scenestate.nodes[node.roofSegmentId as AnyNodeId] as RoofSegmentNode | undefined)
    : undefined
  const roof = segment?.parentId
    ? (scenestate.nodes[segment.parentId as AnyNodeId] as RoofNode | undefined)
    : undefined

  const skylightObj = sceneRegistry.nodes.get(selectedId)
  if (skylightObj) skylightObj.updateWorldMatrix(true, false)

  const computeWorldPos = () => {
    if (!skylightObj) return { x: 0, z: 0 }
    const localPt = new Vector3(node.position[0] ?? 0, 0, node.position[2] ?? 0)
    const worldPt = localPt.applyMatrix4(skylightObj.matrixWorld)
    return { x: worldPt.x, z: worldPt.z }
  }
  const computeWorldRotation = () => {
    if (!skylightObj) return node.rotation ?? 0
    const m = skylightObj.matrixWorld.elements
    const ancestorWorldY = Math.atan2(-(m[2] ?? 0), m[0] ?? 1)
    return ancestorWorldY + (node.rotation ?? 0)
  }
  const { x: worldX_now, z: worldZ_now } = computeWorldPos()
  const worldRotation_now = computeWorldRotation()

  const findSegmentForWorldPoint = (
    wx: number,
    wz: number,
  ): { segment: RoofSegmentNode; localX: number; localZ: number } | null => {
    const state = useScene.getState()
    const worldPt = new Vector3(wx, 0, wz)
    for (const candidate of Object.values(state.nodes)) {
      if (!candidate || candidate.type !== 'roof-segment') continue
      const seg = candidate as RoofSegmentNode
      const segObj = sceneRegistry.nodes.get(seg.id)
      if (!segObj) continue
      segObj.updateWorldMatrix(true, false)
      const local = segObj.worldToLocal(worldPt.clone())
      if (Math.abs(local.x) <= seg.width / 2 && Math.abs(local.z) <= seg.depth / 2) {
        return { segment: seg, localX: local.x, localZ: local.z }
      }
    }
    return null
  }

  const worldToSegLocal = (
    wx: number,
    wz: number,
    seg: RoofSegmentNode,
  ): { localX: number; localZ: number } => {
    const segObj = sceneRegistry.nodes.get(seg.id)
    if (!segObj) return { localX: wx, localZ: wz }
    segObj.updateWorldMatrix(true, false)
    const local = segObj.worldToLocal(new Vector3(wx, 0, wz))
    return { localX: local.x, localZ: local.z }
  }

  let worldMinX = worldX_now - 20
  let worldMaxX = worldX_now + 20
  let worldMinZ = worldZ_now - 20
  let worldMaxZ = worldZ_now + 20
  if (roof) {
    let lo_x = Number.POSITIVE_INFINITY
    let hi_x = Number.NEGATIVE_INFINITY
    let lo_z = Number.POSITIVE_INFINITY
    let hi_z = Number.NEGATIVE_INFINITY
    for (const childId of roof.children ?? []) {
      const seg = scenestate.nodes[childId as AnyNodeId] as RoofSegmentNode | undefined
      if (!seg) continue
      const segObj = sceneRegistry.nodes.get(seg.id)
      if (!segObj) continue
      segObj.updateWorldMatrix(true, false)
      const segWorldCenter = new Vector3().applyMatrix4(segObj.matrixWorld)
      const r = Math.hypot(seg.width, seg.depth) / 2
      lo_x = Math.min(lo_x, segWorldCenter.x - r)
      hi_x = Math.max(hi_x, segWorldCenter.x + r)
      lo_z = Math.min(lo_z, segWorldCenter.z - r)
      hi_z = Math.max(hi_z, segWorldCenter.z + r)
    }
    if (Number.isFinite(lo_x)) {
      worldMinX = lo_x
      worldMaxX = hi_x
      worldMinZ = lo_z
      worldMaxZ = hi_z
    }
  }

  const commitWorldPosition = (newWorldX: number, newWorldZ: number) => {
    if (!segment) return
    const target = findSegmentForWorldPoint(newWorldX, newWorldZ)
    if (target && target.segment.id !== segment.id) {
      const oldWorldRotation = worldRotation_now
      const newSegObj = sceneRegistry.nodes.get(target.segment.id)
      let newAncestorWorldY = 0
      if (newSegObj) {
        newSegObj.updateWorldMatrix(true, false)
        const m = newSegObj.matrixWorld.elements
        newAncestorWorldY = Math.atan2(-(m[2] ?? 0), m[0] ?? 1)
      }
      const newSegLocalRot = oldWorldRotation - newAncestorWorldY
      commitProp({
        roofSegmentId: target.segment.id,
        parentId: target.segment.id,
        position: [target.localX, 0, target.localZ],
        rotation: newSegLocalRot,
      } as Partial<SkylightNode>)
    } else {
      const local = worldToSegLocal(newWorldX, newWorldZ, segment)
      commitProp({ position: [local.localX, 0, local.localZ] })
    }
  }

  const commitWorldRotation = (newWorldRot: number) => {
    if (!skylightObj) return
    const m = skylightObj.matrixWorld.elements
    const ancestorWorldY = Math.atan2(-(m[2] ?? 0), m[0] ?? 1)
    commitProp({ rotation: newWorldRot - ancestorWorldY })
  }

  const activeSkylightType = node.skylightType ?? 'flat'

  const handleTypeChange = (skylightType: SkylightType) => {
    const preset = SKYLIGHT_TYPE_PRESETS[skylightType]
    commitProp({
      skylightType,
      width: preset.width,
      height: preset.height,
      frameThickness: preset.frameThickness,
      frameDepth: preset.frameDepth,
      glassThickness: preset.glassThickness,
      curb: preset.curb,
      curbHeight: preset.curbHeight,
      cutoutOffset: preset.cutoutOffset,
      lanternHeight: preset.lanternHeight,
      lanternTopScale: preset.lanternTopScale,
      openingAngle: preset.openingAngle,
      openingSide: preset.openingSide,
      operationState: preset.operationState,
      motorHousing: preset.motorHousing,
      slideFraction: preset.slideFraction,
      slideDirection: preset.slideDirection,
      trackWidth: preset.trackWidth,
      motorHousingSize: preset.motorHousingSize,
    } as Partial<SkylightNode>)
  }

  return (
    <PanelWrapper
      icon="/icons/roof.png"
      onBack={node.roofSegmentId ? handleBack : undefined}
      onClose={handleClose}
      title={node.name || 'Skylight'}
      width={300}
    >
      <PanelSection title="Type">
        <div className="grid grid-cols-2 gap-2 px-1 pt-1">
          {SKYLIGHT_TYPE_ORDER.map((skylightType) => {
            const isSelected = activeSkylightType === skylightType
            return (
              <button
                className={cn(
                  'flex min-h-12 items-center gap-2.5 rounded-lg border px-3 py-2.5 text-left text-xs transition-colors',
                  isSelected
                    ? 'border-orange-400/60 bg-orange-400/10 text-foreground'
                    : 'border-border/50 bg-[#2C2C2E] text-muted-foreground hover:bg-[#3e3e3e] hover:text-foreground',
                )}
                key={skylightType}
                onClick={() => handleTypeChange(skylightType)}
                type="button"
              >
                <span className="min-w-0 truncate font-medium">
                  {SKYLIGHT_TYPE_PRESETS[skylightType].label}
                </span>
              </button>
            )
          })}
        </div>
        <SliderControl
          label="Glass Thickness"
          max={0.05}
          min={0.002}
          onChange={(v) => previewProp({ glassThickness: v })}
          onCommit={(v) => commitProp({ glassThickness: v })}
          precision={3}
          restoreOnCommit={false}
          step={0.001}
          unit="m"
          value={Math.round((node.glassThickness ?? 0.01) * 1000) / 1000}
        />
        {activeSkylightType === 'lantern' && (
          <>
            <SliderControl
              label="Lantern Height"
              max={1.0}
              min={0.05}
              onChange={(v) => previewProp({ lanternHeight: v })}
              onCommit={(v) => commitProp({ lanternHeight: v })}
              precision={3}
              restoreOnCommit={false}
              step={0.01}
              unit="m"
              value={Math.round((node.lanternHeight ?? 0.25) * 1000) / 1000}
            />
            <SliderControl
              label="Top Scale"
              max={0.95}
              min={0}
              onChange={(v) => previewProp({ lanternTopScale: v })}
              onCommit={(v) => commitProp({ lanternTopScale: v })}
              precision={2}
              restoreOnCommit={false}
              step={0.05}
              unit=""
              value={Math.round((node.lanternTopScale ?? 0.25) * 100) / 100}
            />
          </>
        )}
        {activeSkylightType === 'opening' && (
          <>
            <SliderControl
              label="Open"
              max={1}
              min={0}
              onChange={(v) => previewProp({ operationState: v })}
              onCommit={(v) => commitProp({ operationState: v })}
              precision={2}
              restoreOnCommit={false}
              step={0.01}
              unit=""
              value={Math.round((node.operationState ?? 0) * 100) / 100}
            />
            <SliderControl
              label="Opening Angle"
              max={80}
              min={0}
              onChange={(deg) => previewProp({ openingAngle: (deg * Math.PI) / 180 })}
              onCommit={(deg) => commitProp({ openingAngle: (deg * Math.PI) / 180 })}
              precision={0}
              restoreOnCommit={false}
              step={1}
              unit="°"
              value={Math.round(((node.openingAngle ?? Math.PI / 10) * 180) / Math.PI)}
            />
            <SegmentedControl
              onChange={(v) => commitProp({ openingSide: v as SkylightNode['openingSide'] })}
              options={[
                { label: 'Top', value: 'top' },
                { label: 'Bottom', value: 'bottom' },
                { label: 'Left', value: 'left' },
                { label: 'Right', value: 'right' },
              ]}
              value={(node.openingSide ?? 'top') as any}
            />
            <SegmentedControl
              onChange={(v) => commitProp({ motorHousing: v === 'yes' })}
              options={[
                { label: 'Motor', value: 'yes' },
                { label: 'No Motor', value: 'no' },
              ]}
              value={(node.motorHousing ?? false) ? 'yes' : 'no'}
            />
            {(node.motorHousing ?? false) && (
              <SliderControl
                label="Motor Housing"
                max={0.2}
                min={0.03}
                onChange={(v) => previewProp({ motorHousingSize: v })}
                onCommit={(v) => commitProp({ motorHousingSize: v })}
                precision={3}
                restoreOnCommit={false}
                step={0.005}
                unit="m"
                value={Math.round((node.motorHousingSize ?? 0.08) * 1000) / 1000}
              />
            )}
          </>
        )}
        {activeSkylightType === 'sliding' && (
          <>
            <SliderControl
              label="Open"
              max={1}
              min={0}
              onChange={(v) => previewProp({ operationState: v })}
              onCommit={(v) => commitProp({ operationState: v })}
              precision={2}
              restoreOnCommit={false}
              step={0.01}
              unit=""
              value={Math.round((node.operationState ?? 0) * 100) / 100}
            />
            <SegmentedControl
              onChange={(v) => commitProp({ slideDirection: v as SkylightNode['slideDirection'] })}
              options={[
                { label: 'Along Z', value: 'z' },
                { label: 'Along X', value: 'x' },
              ]}
              value={(node.slideDirection ?? 'z') as any}
            />
            <SliderControl
              label="Track Width"
              max={0.12}
              min={0.02}
              onChange={(v) => previewProp({ trackWidth: v })}
              onCommit={(v) => commitProp({ trackWidth: v })}
              precision={3}
              restoreOnCommit={false}
              step={0.005}
              unit="m"
              value={Math.round((node.trackWidth ?? 0.045) * 1000) / 1000}
            />
          </>
        )}
      </PanelSection>

      <PanelSection title="Dimensions">
        <SliderControl
          label="Width"
          max={3}
          min={0.3}
          onChange={(v) => previewProp({ width: v })}
          onCommit={(v) => commitProp({ width: v })}
          precision={2}
          restoreOnCommit={false}
          step={0.05}
          unit="m"
          value={Math.round(node.width * 100) / 100}
        />
        <SliderControl
          label="Height"
          max={3}
          min={0.3}
          onChange={(v) => previewProp({ height: v })}
          onCommit={(v) => commitProp({ height: v })}
          precision={2}
          restoreOnCommit={false}
          step={0.05}
          unit="m"
          value={Math.round(node.height * 100) / 100}
        />
      </PanelSection>

      <PanelSection title="Frame">
        <SliderControl
          label="Thickness"
          max={0.2}
          min={0.02}
          onChange={(v) => previewProp({ frameThickness: v })}
          onCommit={(v) => commitProp({ frameThickness: v })}
          precision={3}
          restoreOnCommit={false}
          step={0.005}
          unit="m"
          value={Math.round((node.frameThickness ?? 0.05) * 1000) / 1000}
        />
        <SliderControl
          label="Depth"
          max={0.3}
          min={0.02}
          onChange={(v) => previewProp({ frameDepth: v })}
          onCommit={(v) => commitProp({ frameDepth: v })}
          precision={3}
          restoreOnCommit={false}
          step={0.005}
          unit="m"
          value={Math.round((node.frameDepth ?? 0.08) * 1000) / 1000}
        />
        <SliderControl
          label="Cutout Offset"
          max={0.2}
          min={0}
          onChange={(v) => previewProp({ cutoutOffset: v })}
          onCommit={(v) => commitProp({ cutoutOffset: v })}
          precision={3}
          restoreOnCommit={false}
          step={0.005}
          unit="m"
          value={Math.round((node.cutoutOffset ?? 0.01) * 1000) / 1000}
        />
      </PanelSection>

      <PanelSection title="Curb">
        <SegmentedControl
          onChange={(v) => handleUpdate({ curb: v === 'yes' })}
          options={[
            { label: 'Yes', value: 'yes' },
            { label: 'No', value: 'no' },
          ]}
          value={(node.curb ?? false) ? 'yes' : 'no'}
        />
        {(node.curb ?? false) && (
          <SliderControl
            label="Height"
            max={0.3}
            min={0.02}
            onChange={(v) => previewProp({ curbHeight: v })}
            onCommit={(v) => commitProp({ curbHeight: v })}
            precision={3}
            restoreOnCommit={false}
            step={0.005}
            unit="m"
            value={Math.round((node.curbHeight ?? 0.1) * 1000) / 1000}
          />
        )}
      </PanelSection>

      <PanelSection title="Position">
        <SliderControl
          label="X"
          max={Math.round(worldMaxX * 10) / 10}
          min={Math.round(worldMinX * 10) / 10}
          onChange={(newWorldX) => {
            if (!segment) return
            const local = worldToSegLocal(newWorldX, worldZ_now, segment)
            previewProp({ position: [local.localX, 0, local.localZ] })
          }}
          onCommit={(newWorldX) => commitWorldPosition(newWorldX, worldZ_now)}
          precision={2}
          restoreOnCommit={false}
          step={0.05}
          unit="m"
          value={Math.round(worldX_now * 100) / 100}
        />
        <SliderControl
          label="Z"
          max={Math.round(worldMaxZ * 10) / 10}
          min={Math.round(worldMinZ * 10) / 10}
          onChange={(newWorldZ) => {
            if (!segment) return
            const local = worldToSegLocal(worldX_now, newWorldZ, segment)
            previewProp({ position: [local.localX, 0, local.localZ] })
          }}
          onCommit={(newWorldZ) => commitWorldPosition(worldX_now, newWorldZ)}
          precision={2}
          restoreOnCommit={false}
          step={0.05}
          unit="m"
          value={Math.round(worldZ_now * 100) / 100}
        />
        <SliderControl
          label="Rotation"
          max={180}
          min={-180}
          onChange={(degrees) => {
            const newWorldRot = (degrees * Math.PI) / 180
            if (!skylightObj) return
            const m = skylightObj.matrixWorld.elements
            const ancestorWorldY = Math.atan2(-(m[2] ?? 0), m[0] ?? 1)
            previewProp({ rotation: newWorldRot - ancestorWorldY })
          }}
          onCommit={(degrees) => commitWorldRotation((degrees * Math.PI) / 180)}
          precision={0}
          restoreOnCommit={false}
          step={1}
          unit="°"
          value={Math.round((worldRotation_now * 180) / Math.PI)}
        />
      </PanelSection>

      <PanelSection title="Actions">
        <ActionGroup>
          <ActionButton
            className="hover:bg-red-500/20"
            icon={<Trash2 className="h-3.5 w-3.5 text-red-400" />}
            label="Delete"
            onClick={handleDelete}
          />
        </ActionGroup>
      </PanelSection>
    </PanelWrapper>
  )
}
