import { useState, useEffect, useRef } from 'react'
import Icon from './components/Icon'
import {
  onMessage, notifyReady,
  requestSelection, setColorSource, setLwSource, setLinetypeSource, setLinetypeScale, setFill,
} from './lib/rhinoBridge'

const LW_PRESETS = [0.02, 0.10, 0.13, 0.18, 0.25, 0.35, 0.50, 0.70, 1.00]

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

function SectionHead({ title }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '10px 14px 6px' }}>
      <span style={{ fontSize: 11, fontWeight: 500, color: 'var(--text-primary)' }}>
        {title}
      </span>
      <div style={{ flex: 1, height: 1, background: 'var(--border)' }} />
    </div>
  )
}

/** Source-Dropdown im Vectorworks-Stil: Link-Icon + Label + Caret */
function SourceSelect({ source, onChange, overrideLabel = 'Eigene' }) {
  return (
    <div style={{ position: 'relative', display: 'flex' }}>
      <Icon
        name={source === 'layer' ? 'link' : 'link_off'}
        size={14}
        style={{
          position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)',
          color: source === 'layer' ? 'var(--text-primary)' : 'var(--text-muted)',
          pointerEvents: 'none',
        }}
      />
      <select
        value={source}
        onChange={(ev) => onChange(ev.target.value)}
        style={{
          paddingLeft: 30, flex: 1,
          fontFamily: 'var(--font)',
          fontWeight: 500,
          fontSize: 11,
          textTransform: 'none',
          /* borderRadius/background uebernehmen wir von der globalen <select>-
             Pille (index.css) damit's optisch konsistent mit dem Fill-Dropdown
             wirkt. Nur paddingLeft ueberschreiben wegen Link-Icon. */
        }}
      >
        <option value="layer">Nach Ebene</option>
        <option value="object">{overrideLabel}</option>
      </select>
    </div>
  )
}

function ColorBar({ color, onChange, height = 22 }) {
  // Readonly-Variante als reines Anzeige-Rechteck
  if (!onChange) {
    return (
      <div
        style={{
          width: '100%', height,
          background: color,
          borderRadius: 'var(--r)',
          border: '1px solid var(--border)',
          boxShadow: 'inset 0 1px 2px rgba(0,0,0,0.08)',
        }}
      />
    )
  }
  // Editierbar: <label> umschliesst einen sichtbaren Swatch-Div + einen
  // unsichtbaren color-input. Click aufs Label routet automatisch zum Input
  // und oeffnet den nativen Picker. Die Anzeige (div background) ist voll
  // unter unserer Kontrolle und wird durch local-State live aktualisiert.
  // Debounce 150ms verhindert Python-Spam waehrend Picker-Drag.
  const [local, setLocal] = useState(color || '#888888')
  const timer = useRef(null)
  useEffect(() => {
    if (color && color !== local) {
      // Selektion wechselt -> evtl. noch laufenden Debounce abbrechen,
      // sonst feuert die alte Pick-Farbe auf das neue Objekt.
      if (timer.current) { clearTimeout(timer.current); timer.current = null }
      setLocal(color)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [color])

  const handle = (ev) => {
    const v = ev.target.value
    if (!v || !/^#[0-9a-fA-F]{6}$/.test(v)) return
    setLocal(v)
    if (timer.current) clearTimeout(timer.current)
    timer.current = setTimeout(() => onChange(v), 150)
  }

  return (
    <label
      style={{
        position: 'relative', display: 'block',
        width: '100%', height,
        borderRadius: 'var(--r)',
        border: '1px solid var(--border)',
        overflow: 'hidden',
        cursor: 'pointer',
      }}
    >
      <div
        style={{
          position: 'absolute', inset: 0,
          background: local,
          pointerEvents: 'none',
          boxShadow: 'inset 0 1px 2px rgba(0,0,0,0.08)',
        }}
      />
      <input
        type="color"
        value={local}
        onInput={handle}
        onChange={handle}
        style={{
          position: 'absolute', inset: 0,
          width: '100%', height: '100%',
          opacity: 0,
          border: 'none', padding: 0, margin: 0,
          cursor: 'pointer',
          background: 'transparent',
        }}
      />
    </label>
  )
}

/** Number-Input mit Commit-on-Blur/Enter und externer Sync. */
function NumInput({ value, onCommit, step = 0.01, min, max, width = 56, mono = true }) {
  const [val, setVal] = useState(String(value))
  useEffect(() => { setVal(typeof value === 'number' ? value.toFixed(2) : String(value)) }, [value])
  const commit = () => {
    const v = parseFloat(val)
    if (!isNaN(v) && (min == null || v >= min) && (max == null || v <= max)) onCommit(v)
    else setVal(typeof value === 'number' ? value.toFixed(2) : String(value))
  }
  return (
    <input
      type="number" step={step} min={min} max={max}
      value={val}
      onChange={(ev) => setVal(ev.target.value)}
      onBlur={commit}
      onKeyDown={(ev) => {
        if (ev.key === 'Enter') ev.target.blur()
        if (ev.key === 'Escape') { setVal(typeof value === 'number' ? value.toFixed(2) : String(value)); ev.target.blur() }
      }}
      style={{ width, textAlign: 'right', fontSize: 11, fontFamily: mono ? 'var(--font-mono)' : 'var(--font)' }}
    />
  )
}

function LwPreview({ lw }) {
  return (
    <div style={{
      flex: 1, height: 22,
      background: 'var(--bg-item)',
      border: '1px solid var(--border)',
      borderRadius: 'var(--r)',
      display: 'flex', alignItems: 'center',
      padding: '0 8px', overflow: 'hidden',
    }}>
      <div style={{
        width: '100%',
        height: Math.max(1, Math.min(8, lw * 6)),
        background: 'var(--text-primary)',
        borderRadius: 1,
      }} />
    </div>
  )
}

// ---------------------------------------------------------------------------
// Pen color

function PenColor({ sel }) {
  const source     = sel.colorSource === 'mixed' ? 'object' : (sel.colorSource || 'layer')
  const effective  = source === 'object'
    ? (sel.color || '#888888')
    : (sel.layerColor || '#888888')

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      <SourceSelect
        source={source}
        onChange={(v) => setColorSource(v, v === 'object' ? effective : null)}
      />
      <ColorBar
        color={effective}
        onChange={(c) => setColorSource('object', c)}
      />
    </div>
  )
}

// LW row inline
function PenLw({ sel }) {
  const source     = sel.lwSource === 'mixed' ? 'object' : (sel.lwSource || 'layer')
  const effective  = source === 'object'
    ? (sel.lw ?? 0.18)
    : (sel.layerLw ?? 0.18)

  const step = (dir) => {
    let next
    if (dir > 0) next = LW_PRESETS.find(p => p > effective + 0.001)
    else         next = [...LW_PRESETS].reverse().find(p => p < effective - 0.001)
    if (next !== undefined) setLwSource('object', next)
  }

  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
      <button
        className="btn-icon-sm"
        onClick={() => setLwSource(source === 'object' ? 'layer' : 'object', source === 'object' ? null : effective)}
        title={source === 'object' ? 'Nach Ebene' : 'Uebersteuern'}
        style={{ color: source === 'layer' ? 'var(--text-primary)' : 'var(--text-muted)' }}
      >
        <Icon name={source === 'layer' ? 'link' : 'link_off'} size={14} />
      </button>
      <LwPreview lw={effective} />
      <NumInput
        value={effective}
        onCommit={(v) => setLwSource('object', v)}
        step={0.01} min={0.01} max={2.0}
        width={52}
      />
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <button className="btn-step" onClick={() => step(+1)}><Icon name="arrow_drop_up" size={12}/></button>
        <button className="btn-step" onClick={() => step(-1)}><Icon name="arrow_drop_down" size={12}/></button>
      </div>
    </div>
  )
}

function PenLinetype({ sel }) {
  const source = sel.linetypeSource === 'mixed' ? 'object' : (sel.linetypeSource || 'layer')
  const effective = source === 'object'
    ? (sel.linetype || sel.layerLinetype || (sel.linetypes?.[0] || ''))
    : (sel.layerLinetype || (sel.linetypes?.[0] || ''))
  const options = sel.linetypes || []
  const hasOption = options.includes(effective)
  const ltScale = sel.linetypeScale ?? 1.0
  const isSolid = (effective || '').toLowerCase() === 'continuous'

  // Spezialwert "__layer__" damit dieselbe Pill wie bei Color/Fill — Klick
  // auf den Wert "Nach Ebene" wechselt zurueck auf source=layer.
  const ltCurrent = source === 'layer' ? '__layer__' : effective
  const ltChange = (v) => {
    if (v === '__layer__') {
      setLinetypeSource('layer', null)
    } else {
      setLinetypeSource('object', v)
    }
  }
  return (
    <>
      <div style={{ position: 'relative', display: 'flex' }}>
        <Icon
          name={source === 'layer' ? 'link' : 'link_off'}
          size={14}
          style={{
            position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)',
            color: source === 'layer' ? 'var(--text-primary)' : 'var(--text-muted)',
            pointerEvents: 'none',
          }}
        />
        <select
          value={ltCurrent}
          onChange={(ev) => ltChange(ev.target.value)}
          style={{
            paddingLeft: 30, flex: 1,
            fontFamily: 'var(--font)',
            fontWeight: 500,
            fontSize: 11,
            textTransform: 'none',
          }}
        >
          <option value="__layer__">Nach Ebene</option>
          {!hasOption && effective && <option value={effective}>{effective}</option>}
          {options.map(n => <option key={n} value={n}>{n}</option>)}
        </select>
      </div>
      {!isSolid && (
        <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <Icon name="open_in_full" size={14} style={{ color: 'var(--text-muted)', flexShrink: 0 }} title="Linientyp-Skalierung" />
          <NumInput
            value={ltScale} step={0.1} min={0.001} max={1000}
            onCommit={(v) => setLinetypeScale(v)}
            width={70}
          />
          <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>× Pattern</span>
        </div>
      )}
    </>
  )
}

function PenBlock({ sel }) {
  return (
    <div style={{ padding: '0 14px 12px', display: 'flex', flexDirection: 'column', gap: 8 }}>
      <PenColor    sel={sel} />
      <PenLw       sel={sel} />
      <PenLinetype sel={sel} />
      {sel.layerName && (
        <div style={{ fontSize: 9, color: 'var(--text-muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
          Ebene: {sel.layerName}
        </div>
      )}
    </div>
  )
}

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

function FillBlock({ sel }) {
  if (sel.canFill !== true) {
    return (
      <div style={{ padding: '0 14px 12px', fontSize: 10, color: 'var(--text-muted)', fontStyle: 'italic' }}>
        Keine geschlossenen 2D-Kurven in der Auswahl.
      </div>
    )
  }
  const enabled    = sel.fillEnabled === true
  const source     = sel.fillSource   || 'layer'
  const color      = sel.fillColor    || sel.layerColor || '#cccccc'
  const objectPat  = enabled ? (sel.fillPattern || 'Solid') : 'None'
  const scale      = sel.fillScale    ?? 1.0
  const rotation   = sel.fillRotation ?? 0.0
  const patternList = sel.hatchPatterns || ['Solid']

  // Special-Value im kombinierten Dropdown:
  //   "__layer__" = Nach Ebene  -> Pattern/Scale/Rot/Color aus Ebenen-Einstellungen
  //   "None"      = keine Fuellung
  //   "Solid"     = volle Flaeche (Eigene Quelle)
  //   sonst       = Hatch-Pattern (Eigene Quelle)
  // source=='layer' -> immer "Nach Ebene" zeigen, auch wenn (noch) keine Hatch
  // existiert (Ebene hat halt aktuell kein Pattern -> wird gefuellt, sobald
  // eines definiert wird).
  const currentValue = source === 'layer'
    ? '__layer__'
    : (!enabled ? 'None' : objectPat)

  const dropdownOptions = [
    { value: '__layer__', label: 'Nach Ebene' },
    { value: 'None',      label: 'None' },
    { value: 'Solid',     label: 'Solid' },
    ...patternList
      .filter(p => p !== 'Solid' && p !== 'None')
      .map(p => ({ value: p, label: p })),
  ]
  // Falls aktueller Pattern-Name nicht in der Liste, anhaengen damit's nicht verloren geht
  if (currentValue !== '__layer__' && currentValue !== 'None'
      && !dropdownOptions.some(o => o.value === currentValue)) {
    dropdownOptions.push({ value: currentValue, label: currentValue })
  }

  const applyPattern = (newValue) => {
    if (newValue === '__layer__') {
      // Source -> layer, Python liest Pattern/Scale/Rot/Color aus Layer.SectionStyle
      setFill(true, 'layer', null, null, null, null)
    } else if (newValue === 'None') {
      setFill(false, source, null, null, scale, rotation)
    } else {
      // Eigene Quelle mit gewaehltem Pattern
      setFill(true, 'object', color, newValue, scale, rotation)
    }
  }
  // Anpassungen wenn schon im "Eigene"-Modus (Scale/Rotation/Color/Source-Toggle)
  const apply = (over) => setFill(
    true,
    over.source    ?? (source === 'layer' ? 'object' : source),
    (over.source ?? source) === 'layer' ? null : (over.color ?? color),
    over.pattern   ?? (objectPat === 'None' ? 'Solid' : objectPat),
    over.scale     ?? scale,
    over.rotation  ?? rotation,
  )
  const isLayerSource = source === 'layer'

  return (
    <div style={{ padding: '0 14px 12px', display: 'flex', flexDirection: 'column', gap: 8 }}>
      {/* Pill mit Link-Icon im Dropdown (analog zur Pen-Source-Pill).
          Link-Icon wenn "Nach Ebene", sonst link_off. */}
      <div style={{ position: 'relative', display: 'flex' }}>
        <Icon
          name={isLayerSource ? 'link' : 'link_off'}
          size={14}
          style={{
            position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)',
            color: isLayerSource ? 'var(--text-primary)' : 'var(--text-muted)',
            pointerEvents: 'none',
          }}
        />
        <select
          value={currentValue}
          onChange={(ev) => applyPattern(ev.target.value)}
          style={{
            paddingLeft: 30, flex: 1,
            fontFamily: 'var(--font)',
            fontWeight: 500,
            fontSize: 11,
            textTransform: 'none',
          }}
        >
          {dropdownOptions.map(o => (
            <option key={o.value} value={o.value}>{o.label}</option>
          ))}
        </select>
      </div>
      {currentValue !== 'None' && (
        <>
          <ColorBar
            color={color}
            onChange={isLayerSource ? undefined : ((c) => apply({ source: 'object', color: c }))}
          />
          {!isLayerSource && objectPat !== 'Solid' && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <Icon name="aspect_ratio" size={14} style={{ color: 'var(--text-muted)', flexShrink: 0 }} title="Skalierung" />
              <NumInput
                value={scale} step={0.05} min={0.001}
                onCommit={(v) => apply({ scale: v })}
                width={70}
              />
              <Icon name="rotate_right" size={14} style={{ color: 'var(--text-muted)', flexShrink: 0 }} title="Winkel (Grad)" />
              <NumInput
                value={rotation} step={5}
                onCommit={(v) => apply({ rotation: v })}
                width={56}
              />
              <span style={{ fontSize: 10, color: 'var(--text-muted)' }}>°</span>
            </div>
          )}
          {isLayerSource && (
            <div style={{ fontSize: 10, color: 'var(--text-muted)', fontStyle: 'italic' }}>
              Pattern, Skalierung &amp; Farbe folgen Layer-Section-Style
            </div>
          )}
        </>
      )}
    </div>
  )
}

function EmptyState() {
  return (
    <div style={{
      padding: '60px 24px', textAlign: 'center',
      color: 'var(--text-muted)', fontSize: 11,
      display: 'flex', flexDirection: 'column', gap: 14, alignItems: 'center',
    }}>
      <Icon name="touch_app" size={36} style={{ color: 'var(--text-muted)' }} />
      <div>Waehle ein oder mehrere Objekte in Rhino aus.</div>
    </div>
  )
}

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

export default function GestaltungApp() {
  const [sel, setSel] = useState({ count: 0, linetypes: [] })

  useEffect(() => {
    onMessage('SELECTION', (data) => setSel(data || { count: 0, linetypes: [] }))
    notifyReady()
    const blockContext = (ev) => ev.preventDefault()
    document.addEventListener('contextmenu', blockContext)
    return () => document.removeEventListener('contextmenu', blockContext)
  }, [])

  const empty = sel.count === 0

  return (
    <div style={{
      display: 'flex', flexDirection: 'column',
      height: '100vh', overflow: 'hidden',
      background: 'var(--bg-base)',
      position: 'relative',
    }}>
      <div style={{
        display: 'flex', alignItems: 'center', gap: 8,
        padding: '10px 14px',
        borderBottom: '1px solid var(--border-light)',
      }}>
        <Icon name="tune" size={16} style={{ color: 'var(--text-muted)' }} />
        <span style={{ flex: 1, fontWeight: 500, fontSize: 12, color: 'var(--text-primary)' }}>
          Attribute
        </span>
        {sel.count > 0 && <span className="chip">{sel.count}</span>}
        <button className="btn-icon-sm" onClick={() => requestSelection()} title="Aktualisieren">
          <Icon name="refresh" size={14} />
        </button>
      </div>

      <div style={{ flex: 1, overflowY: 'auto' }}>
        {empty ? <EmptyState /> : (
          <>
            <SectionHead title="Fill" />
            <FillBlock sel={sel} />

            <SectionHead title="Pen" />
            <PenBlock sel={sel} />

            <SectionHead title="Effects" />
            <div style={{ padding: '0 14px 14px', fontSize: 10, color: 'var(--text-muted)', fontStyle: 'italic' }}>
              Schatten / Transparenz folgen spaeter.
            </div>
          </>
        )}
      </div>
    </div>
  )
}
