import { useState, useEffect, useMemo, useRef } from 'react'
import GeschossManager from './components/GeschossManager'
import EbenenManager from './components/EbenenManager'
import AusschnittLayerDialog from './components/AusschnittLayerDialog'
import {
  applyAll, setActiveZeichnungsebene, setActiveEbene,
  onMessage, notifyReady, applyVisibility,
  getCombination, applyCombination,
  saveCurrentAsCombination, deleteCombinationPreset,
  saveCombinationPreset,
} from './lib/rhinoBridge'

export function recalcOkff(list) {
  let acc = 0
  return list.map(z => {
    if (z.isGeschoss) {
      const next = { ...z, okff: parseFloat(acc.toFixed(3)) }
      acc += (z.hoehe ?? 3.0)
      return next
    }
    return { ...z, okff: undefined }
  })
}

const INITIAL_ZEICHNUNGSEBENEN = recalcOkff([
  { id: 'eg',  name: 'EG',  isGeschoss: true, hoehe: 3.50, schnitthoehe: 1.00, visible: true },
  { id: '1og', name: '1OG', isGeschoss: true, hoehe: 3.00, schnitthoehe: 1.00, visible: true },
  { id: '2og', name: '2OG', isGeschoss: true, hoehe: 3.00, schnitthoehe: 1.00, visible: true },
])

const INITIAL_EBENEN = [
  { code: '00', name: 'RASTER',         color: '#484850', lw: 0.13, visible: true, locked: false },
  { code: '01', name: 'VERMESSUNG',     color: '#707078', lw: 0.18, visible: true, locked: false },
  { code: '10', name: 'SITUATION',      color: '#909090', lw: 0.18, visible: true, locked: false },
  { code: '11', name: 'STRASSE',        color: '#a89070', lw: 0.18, visible: true, locked: false },
  { code: '12', name: 'GEBÄUDE',        color: '#888888', lw: 0.25, visible: true, locked: false },
  { code: '13', name: 'BÄUME',          color: '#50a050', lw: 0.13, visible: true, locked: false },
  { code: '14', name: 'HÖHENLINIEN',    color: '#909050', lw: 0.18, visible: true, locked: false },
  { code: '20', name: 'WÄNDE',          color: '#0a0a0a', lw: 0.50, visible: true, locked: false },
  { code: '21', name: 'TÜREN_FENSTER',  color: '#5080c8', lw: 0.25, visible: true, locked: false },
  { code: '22', name: 'MÖBEL',          color: '#909090', lw: 0.13, visible: true, locked: false },
  { code: '25', name: 'STÜTZEN',        color: '#c87050', lw: 0.50, visible: true, locked: false },
  { code: '30', name: 'DECKEN',         color: '#605850', lw: 0.35, visible: true, locked: false },
  { code: '31', name: 'DÄCHER',         color: '#7a4a3a', lw: 0.35, visible: true, locked: false },
  { code: '35', name: 'TRÄGER',         color: '#a87858', lw: 0.50, visible: true, locked: false },
  { code: '50', name: 'TEXT',           color: '#d0d0d0', lw: 0.13, visible: true, locked: false },
  { code: '60', name: 'PLANGRAFIK',     color: '#c0a040', lw: 0.13, visible: true, locked: false },
  { code: '90', name: 'REFERENZEN',     color: '#585860', lw: 0.13, visible: true, locked: false },
  { code: '99', name: 'KONSTRUKTION',   color: '#404048', lw: 0.13, visible: true, locked: false },
]

export default function App() {
  const [zeichnungsebenen, setZeichnungsebenen] = useState(INITIAL_ZEICHNUNGSEBENEN)
  const [ebenen,           setEbenen]           = useState(INITIAL_EBENEN)
  const [activeId,         setActiveId]         = useState('eg')
  const [activeCode,       setActiveCode]       = useState('20')
  const [appliedZ,         setAppliedZ]         = useState(INITIAL_ZEICHNUNGSEBENEN)
  const [appliedE,         setAppliedE]         = useState(INITIAL_EBENEN)
  const [zMode,            setZMode]            = useState('active')
  const [eMode,            setEMode]            = useState('all')
  const [hatchPatterns,    setHatchPatterns]    = useState(['Solid', 'Hatch1', 'Hatch2', 'Hatch3', 'Plus', 'Squares', 'Grid', 'Grid60'])
  // Ebenenkombinationen (geteilter Store mit Ausschnitten)
  const [combinations,     setCombinations]     = useState([])         // Liste { name, layers }
  const [activeCombName,   setActiveCombName]   = useState(null)       // null = "Eigene"
  // Dialog fuer "alle bearbeiten" (Pencil-Button)
  const [combDialog,       setCombDialog]       = useState(null)       // { layers, presets } oder null
  const wantCombDialogRef = useRef(false)

  useEffect(() => {
    onMessage('STATE_SYNC', ({ zeichnungsebenen: z, ebenen: e, hatchPatterns: hp }) => {
      if (z) {
        const r = recalcOkff(z); setZeichnungsebenen(r); setAppliedZ(r)
        const active = r.find(zz => zz.id === activeId) || r[0]
        if (active) {
          setActiveZeichnungsebene(active)
          // Auch den Sublayer-Code aktiv setzen, damit Rhino's Current-Layer
          // beim Panel-Start sofort auf der Wahl im Panel landet (sonst bleibt
          // "Default" und neue Objekte landen dort).
          if (activeCode) setActiveEbene(activeCode)
        }
      }
      if (e) { setEbenen(e); setAppliedE(e) }
      if (Array.isArray(hp) && hp.length > 0) setHatchPatterns(hp)
    })
    onMessage('COMBINATION_DATA', ({ layers, presets }) => {
      setCombinations(presets || [])
      if (wantCombDialogRef.current) {
        wantCombDialogRef.current = false
        setCombDialog({ layers: layers || [], presets: presets || [] })
      } else if (combDialog) {
        // Dialog ist offen — Layer-Liste live aktualisieren (z.B. nach Preset-Save)
        setCombDialog(d => d ? { ...d, layers: layers || d.layers, presets: presets || [] } : d)
      }
    })
    onMessage('FIRST_RUN', ({ defaultEbenen } = {}) => {
      // Wenn der Dossier-Launcher ein eigenes Schema definiert hat, nutzen wir
      // das statt der hardcoded INITIAL_EBENEN. Felder ohne `visible`/`locked`
      // werden mit Defaults ergaenzt damit die UI-Komponenten keine undefineds
      // sehen.
      const useEbenen = (Array.isArray(defaultEbenen) && defaultEbenen.length)
        ? defaultEbenen.map(e => ({
            visible: true, locked: false,
            ...e,
          }))
        : INITIAL_EBENEN
      setEbenen(useEbenen)
      applyAll(INITIAL_ZEICHNUNGSEBENEN, useEbenen)
      setAppliedZ(INITIAL_ZEICHNUNGSEBENEN)
      setAppliedE(useEbenen)
      const active = INITIAL_ZEICHNUNGSEBENEN.find(zz => zz.id === activeId) || INITIAL_ZEICHNUNGSEBENEN[0]
      if (active) {
        setActiveZeichnungsebene(active)
        if (activeCode) setActiveEbene(activeCode)
      }
    })
    notifyReady()
    // Initial Liste der Kombinationen holen
    setTimeout(() => getCombination(), 200)

    // Native Browser-Context-Menu global unterdruecken
    const blockContext = (ev) => ev.preventDefault()
    document.addEventListener('contextmenu', blockContext)
    return () => document.removeEventListener('contextmenu', blockContext)
  }, [])

  // Sichtbarkeit live anwenden — bei relevanten Aenderungen
  const visibilityKey = useMemo(() => (
    activeId + '|' + activeCode + '|' + zMode + '|' + eMode + '|' +
    zeichnungsebenen.map(z => `${z.id}:${z.visible !== false ? 1 : 0}`).join(',') + '|' +
    ebenen.map(e => `${e.code}:${e.visible !== false ? 1 : 0}:${e.locked === true ? 1 : 0}`).join(',')
  ), [activeId, activeCode, zMode, eMode, zeichnungsebenen, ebenen])

  useEffect(() => {
    const activeZ = zeichnungsebenen.find(z => z.id === activeId)
    if (activeZ) applyVisibility(activeZ, zeichnungsebenen, activeCode, ebenen, zMode, eMode)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [visibilityKey])

  // Auto-Apply bei strukturellen Aenderungen (add/remove/rename) UND
  // wenn die Ebenen-Settings (z.B. fill) sich aendern — Python braucht den
  // neuen Stand in doc.Strings damit Auto-Fill und 'Nach Ebene' korrekt lesen.
  // Kanonische Signatur: leere/None-Fills sind alle aequivalent — sonst loest
  // schon das blosse Oeffnen+Schliessen des Settings-Dialogs ein applyAll aus
  // (Dialog initialisiert fill mit Default-Werten).
  const fillSig = (e) => {
    const f = e.fill
    if (!f || !f.pattern || f.pattern === 'None') return ''
    return [f.pattern, f.source || 'layer', f.color || '', f.scale ?? 1, f.rotation ?? 0].join('|')
  }
  // WICHTIG: alle Felder die das Backend braucht hier mit drin haben — sonst
  // triggert Aenderung an z.B. hasClipping/schnitthoehe kein Apply, und das
  // Backend sieht den neuen Stand nie. Frueher waren nur id/name/isGeschoss
  // drin -> Clipping-Toggle blieb wirkungslos.
  const zSig = (z) => [
    z.id, z.name, z.isGeschoss ? 1 : 0,
    z.hoehe ?? '', z.schnitthoehe ?? '',
    z.hasClipping ? 1 : 0,
  ].join(':')
  const structureKey = useMemo(() => (
    zeichnungsebenen.map(zSig).join(',') + '|' +
    ebenen.map(e => `${e.code}:${e.name}:${fillSig(e)}`).join(',')
  ), [zeichnungsebenen, ebenen])

  const appliedStructureKey = useMemo(() => (
    appliedZ.map(zSig).join(',') + '|' +
    appliedE.map(e => `${e.code}:${e.name}:${fillSig(e)}`).join(',')
  ), [appliedZ, appliedE])

  useEffect(() => {
    if (structureKey === appliedStructureKey) return
    const t = setTimeout(() => {
      applyAll(zeichnungsebenen, ebenen)
      setAppliedZ(zeichnungsebenen)
      setAppliedE(ebenen)
    }, 200)
    return () => clearTimeout(t)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [structureKey, appliedStructureKey])

  // --- Ebenen-Kombinationen ----------------------------------------------
  const handlePickCombination = (name) => {
    if (!name) { setActiveCombName(null); return }
    const preset = combinations.find(p => p.name === name)
    if (!preset) return
    // Eye-State bevorzugen wenn im Preset vorhanden (= verlustfreie Wiederherstellung,
    // beruecksichtigt z_mode/e_mode); fallback auf doc.Layer-Liste fuer alte Presets.
    applyCombination({
      layers:                preset.layers || [],
      dossierEbenen:           preset.dossierEbenen,
      dossierZeichnungsebenen: preset.dossierZeichnungsebenen,
    })
    setActiveCombName(name)
  }
  const handleSaveCurrentCombination = () => {
    const suggested = activeCombName || `Kombi ${combinations.length + 1}`
    const name = (window.prompt('Name für Ebenenkombination:', suggested) || '').trim()
    if (!name) return
    if (combinations.some(p => p.name === name) &&
        !window.confirm(`"${name}" überschreiben?`)) return
    saveCurrentAsCombination(name)
    setActiveCombName(name)
  }
  const handleDeleteCombination = (name) => {
    if (!name) return
    if (!window.confirm(`Ebenenkombination "${name}" löschen?`)) return
    deleteCombinationPreset(name)
    if (activeCombName === name) setActiveCombName(null)
  }
  const handleOpenCombDialog = () => {
    wantCombDialogRef.current = true
    getCombination()
  }
  // Wenn der User Sichtbarkeit/Lock manuell aendert -> "Eigene".
  // Wird direkt von EbenenManager aufgerufen, kein Effect-Race.
  const handleUserVisibilityChange = () => {
    if (activeCombName !== null) setActiveCombName(null)
  }

  const handleActiveChange = (id) => {
    setActiveId(id)
    const z = zeichnungsebenen.find(x => x.id === id)
    if (z) {
      setActiveZeichnungsebene(z)
      if (activeCode) setActiveEbene(activeCode)
    }
  }

  return (
    <div style={{
      display: 'flex', flexDirection: 'column',
      height: '100vh', overflow: 'hidden',
      background: 'var(--bg-base)',
      position: 'relative',
    }}>
      <div style={{ flex: 1, overflowY: 'auto' }}>
        <GeschossManager
          zeichnungsebenen={zeichnungsebenen}
          activeId={activeId}
          onActiveChange={handleActiveChange}
          onChange={(updated) => setZeichnungsebenen(recalcOkff(updated))}
          recalcOkff={recalcOkff}
          mode={zMode}
          onModeChange={setZMode}
        />
        <EbenenManager
          ebenen={ebenen}
          activeCode={activeCode}
          onActiveChange={(code) => { setActiveCode(code); setActiveEbene(code) }}
          onChange={setEbenen}
          mode={eMode}
          onModeChange={setEMode}
          hatchPatterns={hatchPatterns}
          combinations={combinations}
          activeCombName={activeCombName}
          onPickCombination={handlePickCombination}
          onSaveCurrentCombination={handleSaveCurrentCombination}
          onDeleteCombination={handleDeleteCombination}
          onEditCombinations={handleOpenCombDialog}
          onUserVisibilityChange={handleUserVisibilityChange}
        />
      </div>

      {combDialog && (
        <AusschnittLayerDialog
          snapName="Ebenenkombinationen bearbeiten"
          layers={combDialog.layers}
          presets={combDialog.presets}
          onClose={() => setCombDialog(null)}
          onSave={(layers) => {
            applyCombination(layers)
            setActiveCombName(null)
            setCombDialog(null)
          }}
          onSavePreset={(name, layers) => {
            saveCombinationPreset(name, layers)
            setCombDialog(d => d ? {
              ...d,
              presets: [...d.presets.filter(p => p.name !== name), { name, layers }],
            } : d)
          }}
          onDeletePreset={(name) => {
            deleteCombinationPreset(name)
            setCombDialog(d => d ? {
              ...d,
              presets: d.presets.filter(p => p.name !== name),
            } : d)
            if (activeCombName === name) setActiveCombName(null)
          }}
        />
      )}
    </div>
  )
}
