import {
  ShapeUtil,
  TLHandle,
  TLShapePartial,
  Polyline2d,
  HTMLContainer,
  IndexKey,
  sortByIndex,
  Vec,
} from 'tldraw'
import { PolylinieShape, PolyliniePoint, polylinieShapeProps } from './shape-types'
import { POLYLINIE_CONFIG as CFG } from './shape-config'
import { snapToAngleGrid } from '../utils/angle-snap'

/** Polylinie: Mehrsegment-Linie mit Winkel-Snapping bei der Bearbeitung. */
export class PolylinieShapeUtil extends ShapeUtil<PolylinieShape> {
  static override type = 'polylinie' as const
  static override props = polylinieShapeProps

  getDefaultProps(): PolylinieShape['props'] {
    return {
      points: {},
      label: 'Polylinie',
    }
  }

  /** Punkte sortiert nach Index zurückgeben. */
  private _sortedPoints(shape: PolylinieShape): PolyliniePoint[] {
    return Object.values(shape.props.points).sort(sortByIndex)
  }

  getGeometry(shape: PolylinieShape) {
    const pts = this._sortedPoints(shape)
    if (pts.length < 2) {
      return new Polyline2d({ points: [new Vec(0, 0), new Vec(1, 1)] })
    }
    return new Polyline2d({
      points: pts.map((p) => new Vec(p.x, p.y)),
    })
  }

  override canResize() {
    return false
  }

  override hideRotateHandle() {
    return true
  }

  override hideSelectionBoundsFg() {
    return true
  }

  override hideSelectionBoundsBg() {
    return true
  }

  override getHandles(shape: PolylinieShape): TLHandle[] {
    const pts = this._sortedPoints(shape)
    return pts.map((p) => ({
      id: p.id,
      type: 'vertex' as const,
      x: p.x,
      y: p.y,
      index: p.index as IndexKey,
      canSnap: true,
    }))
  }

  override onHandleDrag(
    shape: PolylinieShape,
    { handle }: { handle: TLHandle }
  ): TLShapePartial<PolylinieShape> | void {
    const pts = this._sortedPoints(shape)
    const idx = pts.findIndex((p) => p.id === handle.id)
    if (idx < 0) return

    // Winkel-Snapping: nur wenn es einen Vorgänger UND Vor-Vorgänger gibt
    const prev = idx > 0 ? pts[idx - 1] : null
    const prevPrev = idx > 1 ? pts[idx - 2] : null

    let newPos = { x: handle.x, y: handle.y }
    if (prev && prevPrev) {
      newPos = snapToAngleGrid(prev, prevPrev, newPos)
    }

    // Auch Nachfolger-Richtung berücksichtigen: snap relativ zum Nachfolger
    const next = idx < pts.length - 1 ? pts[idx + 1] : null
    const nextNext = idx < pts.length - 2 ? pts[idx + 2] : null
    if (next && nextNext) {
      // Zweiter Snap-Pass für Nachfolger-Kompatibilität (optional, Hauptsnap ist Vorgänger)
    }

    const updatedPoints = { ...shape.props.points }
    updatedPoints[handle.id] = {
      ...updatedPoints[handle.id],
      x: newPos.x,
      y: newPos.y,
    }

    return {
      id: shape.id,
      type: 'polylinie',
      props: { ...shape.props, points: updatedPoints },
    }
  }

  component(shape: PolylinieShape) {
    const pts = this._sortedPoints(shape)
    if (pts.length < 2) return null

    // Bounding-Box berechnen
    let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity
    for (const p of pts) {
      minX = Math.min(minX, p.x)
      minY = Math.min(minY, p.y)
      maxX = Math.max(maxX, p.x)
      maxY = Math.max(maxY, p.y)
    }
    const pad = 4
    const w = maxX - minX + pad * 2
    const h = maxY - minY + pad * 2

    const pointStr = pts.map((p) => `${p.x - minX + pad},${p.y - minY + pad}`).join(' ')

    return (
      <HTMLContainer>
        <svg
          width={w}
          height={h}
          style={{
            overflow: 'visible',
            position: 'absolute',
            left: minX - pad,
            top: minY - pad,
          }}
        >
          <polyline
            points={pointStr}
            fill="none"
            stroke={CFG.strokeColor}
            strokeWidth={CFG.strokeWidth}
            strokeLinejoin="round"
            strokeLinecap="round"
          />
        </svg>
      </HTMLContainer>
    )
  }

  indicator(shape: PolylinieShape) {
    const pts = this._sortedPoints(shape)
    if (pts.length < 2) return null
    const d = pts.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.y}`).join(' ')
    return <path d={d} />
  }
}
