// SPDX-License-Identifier: AGPL-3.0-or-later
// Copyright (C) 2026 Karim Gabriele Varano
import { useState, useEffect, useRef } from 'react'
import Icon from './components/Icon'
import { BarToggle, BarButton, BarCombo, BAR_H } from './components/BarControls'
import {
  onMessage, notifyReady,
  requestMassstab, setMassstab,
  zoomOneToOne, zoomExtents, zoomSelection,
  setMassstabDpi, detectMassstabDpi, setShowLineweights,
} from './lib/rhinoBridge'

const PRESETS = [
  { value: 1,    label: '1:1'   },
  { value: 5,    label: '1:5'   },
  { value: 10,   label: '1:10'  },
  { value: 20,   label: '1:20'  },
  { value: 25,   label: '1:25'  },
  { value: 50,   label: '1:50'  },
  { value: 100,  label: '1:100' },
  { value: 200,  label: '1:200' },
  { value: 500,  label: '1:500' },
  { value: 1000, label: '1:1000'},
]

function fmtScale(s) {
  if (s == null) return '—'
  if (s >= 1)   return '1:' + (s >= 10 ? s.toFixed(0) : s.toFixed(1))
  return (1 / s).toFixed(2) + ':1'
}

function snapToPreset(s, tol = 0.03) {
  if (s == null) return null
  for (const p of PRESETS) {
    if (Math.abs(s - p.value) / p.value <= tol) return p.value
  }
  return null
}

function parseScale(input) {
  if (!input) return null
  const s = String(input).trim()
  for (const sep of [':', '=', '/']) {
    if (s.includes(sep)) {
      const [a, b] = s.split(sep, 2)
      const pa = parseFloat(a)
      const pb = parseFloat(b)
      if (pa > 0 && pb > 0) return pb / pa
      return null
    }
  }
  const n = parseFloat(s)
  return n > 0 ? n : null
}

const pillInput = {
  height: BAR_H,
  background: 'var(--bg-input)',
  border: '1px solid var(--border)',
  borderRadius: 999,
  color: 'var(--text-primary)',
  fontSize: 11, fontFamily: 'var(--font)',
  padding: '0 10px',
  outline: 'none',
  boxSizing: 'border-box',
}

// ---------------------------------------------------------------------------

export default function MassstabApp() {
  const [state, setState] = useState({
    viewName: null, parallel: false, scale: null,
    pixelWidth: null, pixelHeight: null, unitSystem: '?',
    dpi: 96, dpiSource: 'default',
    showLineweights: false,
  })
  const [appliedScale, setAppliedScale] = useState(null)
  const appliedScaleRef = useRef(null)
  const [draft, setDraft] = useState('')
  const [dpiOpen, setDpiOpen] = useState(false)
  const [dpiDraft, setDpiDraft] = useState('96')

  useEffect(() => {
    onMessage('STATE', (s) => {
      setState((prev) => ({ ...prev, ...s }))
      // Backend appliedScale (gilt nur fuer aktuellen Viewport) > Live-Snap > roher Live-Wert
      let next = null
      if (typeof s?.appliedScale === 'number' && s.appliedScale > 0) {
        next = s.appliedScale
      } else if (s?.parallel && typeof s?.scale === 'number' && s.scale > 0) {
        const snap = snapToPreset(s.scale)
        next = snap != null ? snap : Math.round(s.scale * 10) / 10
      }
      if (next != null && next > 0 && next !== appliedScaleRef.current) {
        setAppliedScale(next)
        appliedScaleRef.current = next
      }
    })
    notifyReady()
    setTimeout(() => requestMassstab(), 50)
  }, [])

  const isPerspective = state.parallel === false
  const scaleVal = state.scale
  const dropdownValue = appliedScale != null ? String(appliedScale) : '__none__'

  const applyDropdown = (val) => {
    if (val === '__none__') return
    const r = parseFloat(val)
    if (r > 0) {
      setAppliedScale(r)
      appliedScaleRef.current = r
      setMassstab(r)
    }
  }

  const applyDraft = () => {
    const r = parseScale(draft)
    if (r != null) {
      setAppliedScale(r)
      appliedScaleRef.current = r
      setMassstab(r)
      setDraft('')
    }
  }

  const commitDpi = () => {
    const v = parseFloat(dpiDraft)
    if (v >= 30 && v <= 600) setMassstabDpi(v)
    setDpiOpen(false)
  }

  // "100%" = Viewport-Zoom auf den aktuell eingestellten Massstab snappen
  // (nicht: Massstab auf 1:1 setzen). Praktisch nach Pan/Zoom, um wieder
  // zur Soll-Skala zu kommen.
  const apply100 = () => {
    if (appliedScale && appliedScale > 0) {
      setMassstab(appliedScale)
    }
  }

  return (
    <div style={{
      width: '100%', height: '100%',
      display: 'flex', alignItems: 'center', gap: 6,
      padding: '4px 8px',
      fontFamily: 'var(--font)', color: 'var(--text-primary)',
      background: 'var(--bg-base)',
      borderTop: '1px solid var(--border)',
      boxSizing: 'border-box',
      overflow: 'hidden',
    }}>
      {/* Live-Zoom Anzeige */}
      <div style={{
        fontSize: 13, fontWeight: 600,
        fontFamily: 'DM Mono, monospace',
        minWidth: 56, textAlign: 'right',
        color: isPerspective ? 'var(--text-muted)' : 'var(--text-primary)',
      }} title={isPerspective ? 'Perspective — kein Massstab'
                              : `Aktueller Zoom (live)${appliedScale!=null ? ` · gesetzt 1:${appliedScale}` : ''}`}>
        {isPerspective ? '—' : fmtScale(scaleVal)}
      </div>

      <div style={{ width: 1, height: 18, background: 'var(--border)' }} />

      {/* Skala-Dropdown */}
      <BarCombo
        disabled={isPerspective}
        value={dropdownValue}
        onChange={applyDropdown}
        width={84}
        title="Massstab wählen">
        <option value="__none__">1:?</option>
        {PRESETS.map(p => (
          <option key={p.value} value={String(p.value)}>{p.label}</option>
        ))}
        {appliedScale != null && !PRESETS.some(p => p.value === appliedScale) && (
          <option value={String(appliedScale)}>1:{appliedScale}</option>
        )}
      </BarCombo>

      {/* Freitext */}
      <input
        disabled={isPerspective}
        type="text"
        placeholder="1:N"
        value={draft}
        onChange={(e) => setDraft(e.target.value)}
        onKeyDown={(e) => { if (e.key === 'Enter') applyDraft() }}
        onBlur={() => { if (draft) applyDraft() }}
        style={{ ...pillInput, width: 64 }}
        title="Eigenen Massstab eingeben (Enter)"
      />

      <div style={{ width: 1, height: 18, background: 'var(--border)' }} />

      {/* Aktions-Buttons */}
      <BarToggle label="100%"
        disabled={isPerspective || !appliedScale}
        onClick={apply100}
        title={appliedScale
          ? `Zoom auf eingestellten Massstab snappen (1:${appliedScale >= 10 ? Math.round(appliedScale) : appliedScale})`
          : 'Erst einen Massstab wählen'} />
      <BarButton icon="fit_screen"
        onClick={zoomExtents}
        title="Auf gesamten Inhalt zoomen" />
      <BarButton icon="center_focus_strong"
        onClick={zoomSelection}
        title="Auf Selektion zoomen" />

      <div style={{ width: 1, height: 18, background: 'var(--border)' }} />

      {/* Print-View / Strichstaerken-Toggle */}
      <BarToggle
        icon={state.showLineweights ? 'print' : 'edit'}
        label={state.showLineweights ? 'Print' : 'Edit'}
        active={state.showLineweights}
        onClick={() => setShowLineweights(!state.showLineweights)}
        title={state.showLineweights
          ? 'Strichstärken werden angezeigt (Print-View) — klicken zum Ausschalten'
          : 'Strichstärken als Hairlines (Edit-View) — klicken um Print-View zu zeigen'} />

      {/* Spacer */}
      <div style={{ flex: 1 }} />

      {/* View-Name */}
      <div style={{
        fontSize: 10, color: 'var(--text-muted)',
        whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        maxWidth: 140,
      }} title={state.viewName || ''}>
        {state.viewName || '?'}{isPerspective ? ' · Persp.' : ''}
      </div>

      {/* DPI-Popover */}
      <div style={{ position: 'relative' }}>
        <BarToggle
          label={`${Math.round(state.dpi || 96)}dpi${state.dpiSource === 'auto' ? ' · auto' : ''}`}
          active={state.dpiSource === 'auto'}
          onClick={() => { setDpiDraft(String(state.dpi || 96)); setDpiOpen(o => !o) }}
          title={`DPI Kalibrierung — aktuell ${Math.round(state.dpi || 96)} dpi (${state.dpiSource || 'default'})`} />
        {dpiOpen && (
          <div style={{
            position: 'absolute', right: 0, bottom: '100%', marginBottom: 4,
            background: 'var(--bg-panel)', border: '1px solid var(--border)',
            borderRadius: 'var(--r-lg)', padding: 8, display: 'flex',
            flexDirection: 'column', gap: 6,
            boxShadow: '0 4px 12px rgba(0,0,0,0.15)', zIndex: 10, minWidth: 220,
          }}>
            <div style={{ fontSize: 10, color: 'var(--text-muted)' }}>
              Bildschirm-Auflösung für Massstab-Berechnung
            </div>
            <div style={{ display: 'flex', gap: 4, alignItems: 'center' }}>
              <input
                type="number" min={30} max={600}
                value={dpiDraft}
                onChange={(e) => setDpiDraft(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter') commitDpi() }}
                autoFocus
                style={{ ...pillInput, flex: 1 }}
              />
              <BarToggle label="OK" active onClick={commitDpi} />
            </div>
            <BarToggle icon="auto_fix_high" label="Auto-Detect (EDID)"
              onClick={() => { detectMassstabDpi(); setDpiOpen(false) }}
              title="DPI automatisch über EDID des Bildschirms ermitteln" />
          </div>
        )}
      </div>
    </div>
  )
}
