import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';
import type { Widget, Interface } from './lib/types';
import { containingTabPane, tabLabels } from './lib/containers';

interface Props {
  selected: Widget | null;
  multiCount: number;
  iface: Interface;
  onChange: (updated: Widget) => void;
  onRenameId: (oldId: string, newId: string) => void;
  onIfaceChange: (updated: Interface) => void;
  // When provided, a "Share…" button is shown in the canvas-level section that
  // opens the panel's sharing settings (folder, public visibility, grants).
  onShare?: () => void;
  width?: number;
}

function Field({ label, children }: { label: string; children: any }) {
  return (
    <div class="prop-field">
      <label class="prop-label">{label}</label>
      {children}
    </div>
  );
}

function TextInput({ value, onCommit }: { value: string; onCommit: (v: string) => void }) {
  const [local, setLocal] = useState(value || '');

  // Sync when external value changes (e.g. different widget selected)
  useEffect(() => {
    setLocal(value || '');
  }, [value]);

  return (
    <input
      class="prop-input"
      value={local}
      onInput={(e) => setLocal((e.target as HTMLInputElement).value)}
      onChange={(e) => onCommit((e.target as HTMLInputElement).value)}
      onKeyDown={(e) => {
        if (e.key === 'Enter') {
          onCommit((e.target as HTMLInputElement).value);
          (e.target as HTMLInputElement).blur();
        }
      }}
    />
  );
}

// Like TextInput, but for the widget id: rejects invalid (empty/duplicate)
// commits and reverts the field to the current id when `onRename` returns false.
function IdInput({ value, onRename }: { value: string; onRename: (v: string) => boolean }) {
  const [local, setLocal] = useState(value || '');

  useEffect(() => {
    setLocal(value || '');
  }, [value]);

  function commit() {
    const v = local.trim();
    if (v === value) { setLocal(value); return; }
    if (!onRename(v)) setLocal(value); // rejected — revert to the current id
  }

  return (
    <input
      class="prop-input"
      value={local}
      onInput={(e) => setLocal((e.target as HTMLInputElement).value)}
      onChange={commit}
      onKeyDown={(e) => {
        if (e.key === 'Enter') {
          commit();
          (e.target as HTMLInputElement).blur();
        }
      }}
    />
  );
}

// Widgets that show units from signal metadata (can be overridden)
const UNIT_WIDGETS = new Set(['textview', 'gauge', 'barh', 'barv', 'setvalue']);
// Widgets where the user can set a numeric format string
const FORMAT_WIDGETS = new Set(['textview', 'gauge', 'barh', 'barv']);
// Widgets with a signal-based label (can be overridden)
const LABEL_WIDGETS = new Set(['textview', 'gauge', 'barh', 'barv', 'setvalue', 'led', 'multiled', 'toggle']);
// Widgets with multiple signals
const MULTI_SIGNAL_WIDGETS = new Set(['plot', 'multiled', 'table']);

export default function PropertiesPane({ selected, multiCount, iface, onChange, onRenameId, onIfaceChange, onShare, width }: Props) {
  const [collapsed, setCollapsed] = useState(false);
  const [configSets, setConfigSets] = useState<{ id: string; name: string }[]>([]);

  // Config sets are only needed by the Config Selector widget; fetch lazily the
  // first time one is selected.
  useEffect(() => {
    if (selected?.type !== 'configselect' || configSets.length > 0) return;
    fetch('/api/v1/config/sets')
      .then(r => r.ok ? r.json() : [])
      .then((list: { id: string; name: string }[]) => setConfigSets(list ?? []))
      .catch(() => {});
  }, [selected?.type]);

  function setOpt(key: string, value: string) {
    if (!selected) return;
    onChange({ ...selected, options: { ...selected.options, [key]: value } });
  }

  function setProp(key: keyof Widget, value: any) {
    if (!selected) return;
    onChange({ ...selected, [key]: value } as Widget);
  }

  // Apply a custom widget id. Rejects empty or duplicate ids (returns false so
  // the field reverts); on success the rename propagates to logic/plot refs.
  function renameId(newId: string): boolean {
    if (!selected) return false;
    if (!newId) return false;
    if (newId === selected.id) return false;
    if ((iface.widgets || []).some(x => x.id === newId)) return false;
    onRenameId(selected.id, newId);
    return true;
  }

  function removeSignal(idx: number) {
    if (!selected) return;
    const signals = selected.signals.filter((_, i) => i !== idx);
    onChange({ ...selected, signals });
  }

  const w = selected;

  return (
    <aside class={`panel props-panel${collapsed ? ' collapsed' : ''}`} style={`border-left:1px solid #2d3748;border-right:none;${!collapsed && width ? `width:${width}px;min-width:${width}px;` : ''}`}>
      <div class="panel-header">
        {!collapsed && <span class="panel-title">Properties</span>}
        <button class="icon-btn" onClick={() => setCollapsed(c => !c)} title={collapsed ? 'Expand' : 'Collapse'}>
          {collapsed ? '◀' : '▶'}
        </button>
      </div>

      {!collapsed && (
        <div class="props-body">
          {/* Canvas-level properties */}
          <div class="props-section">
            <div class="props-section-title">{iface.kind === 'plot' ? 'Plot panel' : 'Canvas'}</div>
            <Field label="Name">
              <TextInput
                value={iface.name}
                onCommit={(v) => onIfaceChange({ ...iface, name: v })}
              />
            </Field>
            {/* Plot panels fill the viewport via the split layout — fixed px size
                is not meaningful, so width/height are hidden. */}
            {iface.kind !== 'plot' && (
              <div>
                <Field label="Width">
                  <input
                    class="prop-input prop-input-num"
                    type="number"
                    value={iface.w}
                    min={100}
                    onChange={(e) => onIfaceChange({ ...iface, w: parseInt((e.target as HTMLInputElement).value, 10) || iface.w })}
                  />
                </Field>
                <Field label="Height">
                  <input
                    class="prop-input prop-input-num"
                    type="number"
                    value={iface.h}
                    min={100}
                    onChange={(e) => onIfaceChange({ ...iface, h: parseInt((e.target as HTMLInputElement).value, 10) || iface.h })}
                  />
                </Field>
              </div>
            )}
            {onShare && (
              <button class="panel-btn props-share-btn" onClick={onShare} title="Manage folder, public visibility and per-user/group sharing">
                Share…
              </button>
            )}
          </div>

          {w && (
            <div class="props-section" key={w.id}>
              <div class="props-section-title">{w.type}</div>

              {/* Widget id — editable so logic/plot refs can use a friendly name */}
              <Field label="Widget ID">
                <IdInput value={w.id} onRename={renameId} />
              </Field>

              {/* Position / size */}
              <Field label="X">
                <input class="prop-input prop-input-num" type="number" value={w.x}
                  onChange={(e) => setProp('x', parseFloat((e.target as HTMLInputElement).value) || 0)} />
              </Field>
              <Field label="Y">
                <input class="prop-input prop-input-num" type="number" value={w.y}
                  onChange={(e) => setProp('y', parseFloat((e.target as HTMLInputElement).value) || 0)} />
              </Field>
              <Field label="Width">
                <input class="prop-input prop-input-num" type="number" value={w.w} min={10}
                  onChange={(e) => setProp('w', parseFloat((e.target as HTMLInputElement).value) || 10)} />
              </Field>
              <Field label="Height">
                <input class="prop-input prop-input-num" type="number" value={w.h} min={10}
                  onChange={(e) => setProp('h', parseFloat((e.target as HTMLInputElement).value) || 10)} />
              </Field>

              {/* Signals */}
              {MULTI_SIGNAL_WIDGETS.has(w.type) ? (
                <Field label="Signals">
                  <div class="prop-signals-list">
                    {w.signals.length === 0 && <span class="hint">No signals</span>}
                    {w.signals.map((s, i) => (
                      <div key={i} class="prop-signal-row">
                        <span class="prop-sig">{s.ds}:{s.name}</span>
                        <button
                          class="icon-btn prop-sig-remove"
                          title="Remove signal"
                          onClick={() => removeSignal(i)}
                        >✕</button>
                      </div>
                    ))}
                  </div>
                </Field>
              ) : w.signals.length > 0 ? (
                <Field label="Signal">
                  <span class="prop-sig">{w.signals[0].ds}:{w.signals[0].name}</span>
                </Field>
              ) : null}

              {/* ── Common label/unit overrides (issue #8) ──────────────────── */}

              {/* Label: button and textlabel use it as display text;
                  signal widgets use it as header label */}
              {w.type !== 'image' && w.type !== 'link' && w.type !== 'container' && w.type !== 'table' && (
                <Field label={LABEL_WIDGETS.has(w.type) ? 'Label override' : 'Label'}>
                  {LABEL_WIDGETS.has(w.type) ? (
                    <div class="prop-field-col">
                      <TextInput value={w.options['label'] ?? ''} onCommit={(v) => setOpt('label', v)} />
                      <span class="prop-hint">Empty = signal name</span>
                    </div>
                  ) : (
                    <TextInput value={w.options['label'] ?? ''} onCommit={(v) => setOpt('label', v)} />
                  )}
                </Field>
              )}

              {/* Unit override for signal widgets */}
              {UNIT_WIDGETS.has(w.type) && (
                <Field label="Unit">
                  <div class="prop-field-col">
                    <TextInput value={w.options['unit'] ?? ''} onCommit={(v) => setOpt('unit', v)} />
                    <span class="prop-hint">Empty = from meta · "none" = hide</span>
                  </div>
                </Field>
              )}

              {/* Format string for numeric display */}
              {FORMAT_WIDGETS.has(w.type) && (
                <Field label="Format">
                  <div class="prop-field-col">
                    <TextInput value={w.options['format'] ?? ''} onCommit={(v) => setOpt('format', v)} />
                    <span class="prop-hint">3f · 2e · 4g · empty=auto</span>
                  </div>
                </Field>
              )}

              {/* ── Type-specific options ──────────────────────────────────── */}

              {(w.type === 'gauge' || w.type === 'barh' || w.type === 'barv') && (
                <div>
                  <Field label="Min">
                    <TextInput value={w.options['min'] ?? '0'} onCommit={(v) => setOpt('min', v)} />
                  </Field>
                  <Field label="Max">
                    <TextInput value={w.options['max'] ?? '100'} onCommit={(v) => setOpt('max', v)} />
                  </Field>
                </div>
              )}

              {w.type === 'led' && (
                <Field label="Condition">
                  <TextInput value={w.options['condition'] ?? '>0'} onCommit={(v) => setOpt('condition', v)} />
                </Field>
              )}

              {w.type === 'setvalue' && (
                <div>
                  <Field label="Min">
                    <TextInput value={w.options['min'] ?? ''} onCommit={(v) => setOpt('min', v)} />
                  </Field>
                  <Field label="Confirm dialog">
                    <select class="prop-select" value={w.options['confirm'] ?? 'false'}
                      onChange={(e) => setOpt('confirm', (e.target as HTMLSelectElement).value)}>
                      <option value="false">No</option>
                      <option value="true">Yes</option>
                    </select>
                  </Field>
                </div>
              )}

              {w.type === 'toggle' && (
                <div>
                  <Field label="On value">
                    <TextInput value={w.options['onValue'] ?? '1'} onCommit={(v) => setOpt('onValue', v)} />
                  </Field>
                  <Field label="Off value">
                    <TextInput value={w.options['offValue'] ?? '0'} onCommit={(v) => setOpt('offValue', v)} />
                  </Field>
                  <Field label="On label">
                    <TextInput value={w.options['onLabel'] ?? ''} onCommit={(v) => setOpt('onLabel', v)} />
                  </Field>
                  <Field label="Off label">
                    <TextInput value={w.options['offLabel'] ?? ''} onCommit={(v) => setOpt('offLabel', v)} />
                  </Field>
                  <Field label="Confirm dialog">
                    <select class="prop-select" value={w.options['confirm'] ?? 'false'}
                      onChange={(e) => setOpt('confirm', (e.target as HTMLSelectElement).value)}>
                      <option value="false">No</option>
                      <option value="true">Yes</option>
                    </select>
                  </Field>
                </div>
              )}

              {/* Button options (issue #2) */}
              {w.type === 'button' && (
                <div>
                  <Field label="Send value">
                    <TextInput value={w.options['value'] ?? '1'} onCommit={(v) => setOpt('value', v)} />
                  </Field>
                  <Field label="Mode">
                    <select class="prop-select" value={w.options['mode'] ?? 'oneshot'}
                      onChange={(e) => setOpt('mode', (e.target as HTMLSelectElement).value)}>
                      <option value="oneshot">One-shot (set once)</option>
                      <option value="setReset">Set-reset (auto-revert)</option>
                      <option value="persistent">Persistent (toggle / monitor)</option>
                    </select>
                  </Field>
                  {(w.options['mode'] === 'setReset' || w.options['mode'] === 'persistent') && (
                    <Field label="Reset value">
                      <TextInput value={w.options['resetValue'] ?? '0'} onCommit={(v) => setOpt('resetValue', v)} />
                    </Field>
                  )}
                  {w.options['mode'] === 'setReset' && (
                    <Field label="Reset after (s)">
                      <TextInput value={w.options['resetTime'] ?? '1'} onCommit={(v) => setOpt('resetTime', v)} />
                    </Field>
                  )}
                  <Field label="Confirm dialog">
                    <select class="prop-select" value={w.options['confirm'] ?? 'false'}
                      onChange={(e) => setOpt('confirm', (e.target as HTMLSelectElement).value)}>
                      <option value="false">No</option>
                      <option value="true">Yes</option>
                    </select>
                  </Field>
                  <Field label="Action">
                    <div class="prop-field-col">
                      <select
                        class="prop-input"
                        value={w.options['action'] ?? ''}
                        onChange={(e) => setOpt('action', (e.target as HTMLSelectElement).value)}
                      >
                        <option value="">(none)</option>
                        {(iface.logic?.nodes ?? [])
                          .filter(n => n.kind === 'trigger.button')
                          .map(n => (
                            <option key={n.id} value={n.params.name ?? ''}>{n.params.name || '(unnamed)'}</option>
                          ))}
                      </select>
                      <span class="prop-hint">Fires a Button trigger node (define flows in the Logic tab)</span>
                    </div>
                  </Field>
                </div>
              )}

              {/* Plot options */}
              {w.type === 'plot' && (
                <div>
                  <Field label="Plot type">
                    <select
                      class="prop-select"
                      value={w.options['plotType'] ?? 'timeseries'}
                      onChange={(e) => setOpt('plotType', (e.target as HTMLSelectElement).value)}
                    >
                      <option value="timeseries">Time-series</option>
                      <option value="histogram">Histogram</option>
                      <option value="bar">Bar chart</option>
                      <option value="fft">FFT</option>
                      <option value="waterfall">Waterfall</option>
                      <option value="logic">Logic analyser</option>
                      <option value="waveform">Waveform (array)</option>
                    </select>
                  </Field>
                  {(w.options['plotType'] ?? 'timeseries') === 'timeseries' && (
                    <Field label="Time window (s)">
                      <TextInput value={w.options['timeWindow'] ?? '60'} onCommit={(v) => setOpt('timeWindow', v)} />
                    </Field>
                  )}
                  <Field label="Y min">
                    <TextInput value={w.options['yMin'] ?? 'auto'} onCommit={(v) => setOpt('yMin', v)} />
                  </Field>
                  <Field label="Y max">
                    <TextInput value={w.options['yMax'] ?? 'auto'} onCommit={(v) => setOpt('yMax', v)} />
                  </Field>
                  <Field label="Y format">
                    <div class="prop-field-col">
                      <TextInput value={w.options['format'] ?? ''} onCommit={(v) => setOpt('format', v)} />
                      <span class="prop-hint">3f · 2e · 4g · empty=auto</span>
                    </div>
                  </Field>
                  <Field label="Legend">
                    <select class="prop-select" value={w.options['legend'] ?? 'bottom'}
                      onChange={(e) => setOpt('legend', (e.target as HTMLSelectElement).value)}>
                      <option value="bottom">Bottom</option>
                      <option value="top">Top</option>
                      <option value="none">None</option>
                    </select>
                  </Field>
                </div>
              )}

              {w.type === 'textlabel' && (
                <div>
                  <Field label="Font size">
                    <TextInput value={w.options['fontSize'] ?? '14'} onCommit={(v) => setOpt('fontSize', v)} />
                  </Field>
                  <Field label="Color">
                    <TextInput value={w.options['color'] ?? '#e2e8f0'} onCommit={(v) => setOpt('color', v)} />
                  </Field>
                </div>
              )}

              {w.type === 'image' && (
                <Field label="URL">
                  <TextInput value={w.options['url'] ?? ''} onCommit={(v) => setOpt('url', v)} />
                </Field>
              )}

              {w.type === 'link' && (
                <div>
                  <Field label="Target">
                    <TextInput value={w.options['target'] ?? ''} onCommit={(v) => setOpt('target', v)} />
                  </Field>
                  <Field label="Label">
                    <TextInput value={w.options['label'] ?? ''} onCommit={(v) => setOpt('label', v)} />
                  </Field>
                </div>
              )}

              {w.type === 'configselect' && (
                <div>
                  <Field label="Label">
                    <TextInput value={w.options['label'] ?? 'Config'} onCommit={(v) => setOpt('label', v)} />
                  </Field>
                  <Field label="Config set">
                    <select class="prop-select" value={w.options['set'] ?? ''}
                      onChange={(e) => setOpt('set', (e.target as HTMLSelectElement).value)}>
                      <option value="">choose…</option>
                      {configSets.map(cs => <option key={cs.id} value={cs.id}>{cs.name}</option>)}
                    </select>
                  </Field>
                  <Field label="Output variable">
                    <div class="prop-field-col">
                      <select class="prop-input" value={w.options['output'] ?? ''}
                        onChange={(e) => setOpt('output', (e.target as HTMLSelectElement).value)}>
                        <option value="">(none)</option>
                        {(iface.statevars ?? []).map(v => (
                          <option key={v.name} value={v.name}>{v.name}</option>
                        ))}
                      </select>
                      <span class="prop-hint">A panel variable (define one of type "string" in the Variables tab); receives the selected instance id.</span>
                    </div>
                  </Field>
                  <Field label="Instance subset">
                    <div class="prop-field-col">
                      <TextInput value={w.options['instances'] ?? ''} onCommit={(v) => setOpt('instances', v)} />
                      <span class="prop-hint">Comma-separated instance ids to show. Empty = all instances of the set.</span>
                    </div>
                  </Field>
                </div>
              )}

              {w.type === 'container' && (
                <div>
                  <Field label="Variant">
                    <select class="prop-select" value={w.options['variant'] ?? 'pane'}
                      onChange={(e) => setOpt('variant', (e.target as HTMLSelectElement).value)}>
                      <option value="pane">Pane</option>
                      <option value="tabs">Tabs</option>
                    </select>
                  </Field>
                  <Field label="Accent">
                    <TextInput value={w.options['accent'] ?? '#3d4f6e'} onCommit={(v) => setOpt('accent', v)} />
                  </Field>
                  <Field label="Background">
                    <select class="prop-select" value={w.options['bg'] ?? 'true'}
                      onChange={(e) => setOpt('bg', (e.target as HTMLSelectElement).value)}>
                      <option value="true">Filled</option>
                      <option value="false">None</option>
                    </select>
                  </Field>
                  {(w.options['variant'] ?? 'pane') === 'tabs' ? (
                    <Field label="Tabs">
                      <div class="prop-field-col">
                        <TextInput value={w.options['tabs'] ?? 'Tab 1,Tab 2'} onCommit={(v) => setOpt('tabs', v)} />
                        <span class="prop-hint">Comma-separated tab names. Assign each widget to a tab via its "Tab" field.</span>
                      </div>
                    </Field>
                  ) : (
                    <div>
                      <Field label="Title">
                        <TextInput value={w.options['title'] ?? ''} onCommit={(v) => setOpt('title', v)} />
                      </Field>
                      <Field label="Collapsible">
                        <select class="prop-select" value={w.options['collapsible'] ?? 'false'}
                          onChange={(e) => setOpt('collapsible', (e.target as HTMLSelectElement).value)}>
                          <option value="false">No</option>
                          <option value="true">Yes</option>
                        </select>
                      </Field>
                    </div>
                  )}
                </div>
              )}

              {w.type === 'table' && (() => {
                const allCols = ['name', 'value', 'unit', 'quality', 'time'];
                const colLabels: Record<string, string> = {
                  name: 'Name', value: 'Value', unit: 'Unit', quality: 'Status', time: 'Time',
                };
                const cur = (w.options['columns'] ?? 'name,value,unit')
                  .split(',').map(s => s.trim()).filter(Boolean);
                const toggleCol = (c: string) => {
                  const next = cur.includes(c)
                    ? cur.filter(x => x !== c)
                    : allCols.filter(x => cur.includes(x) || x === c);
                  setOpt('columns', next.join(','));
                };
                return (
                  <div>
                    <Field label="Title">
                      <TextInput value={w.options['title'] ?? ''} onCommit={(v) => setOpt('title', v)} />
                    </Field>
                    <Field label="Columns">
                      <div class="prop-field-col">
                        {allCols.map(c => (
                          <label key={c} class="prop-check">
                            <input type="checkbox" checked={cur.includes(c)} onChange={() => toggleCol(c)} />
                            {colLabels[c]}
                          </label>
                        ))}
                      </div>
                    </Field>
                    <Field label="Header row">
                      <select class="prop-select" value={w.options['header'] ?? 'true'}
                        onChange={(e) => setOpt('header', (e.target as HTMLSelectElement).value)}>
                        <option value="true">Show</option>
                        <option value="false">Hide</option>
                      </select>
                    </Field>
                    <Field label="Value format">
                      <div class="prop-field-col">
                        <TextInput value={w.options['format'] ?? ''} onCommit={(v) => setOpt('format', v)} />
                        <span class="prop-hint">3f · 2e · 4g · empty=auto</span>
                      </div>
                    </Field>
                    <Field label="Row labels">
                      <div class="prop-field-col">
                        <TextInput value={w.options['labels'] ?? ''} onCommit={(v) => setOpt('labels', v)} />
                        <span class="prop-hint">Comma-separated, one per signal. Empty = signal name.</span>
                      </div>
                    </Field>
                  </div>
                );
              })()}

              {/* Tab assignment for a widget sitting inside a tabbed container. */}
              {w.type !== 'container' && (() => {
                const host = containingTabPane(w, iface.widgets);
                if (!host) return null;
                const tabs = tabLabels(host);
                return (
                  <Field label="Tab">
                    <select class="prop-select" value={w.options['tab'] ?? '0'}
                      onChange={(e) => setOpt('tab', (e.target as HTMLSelectElement).value)}>
                      {tabs.map((t, i) => <option key={i} value={String(i)}>{t}</option>)}
                    </select>
                  </Field>
                );
              })()}
            </div>
          )}

          {!w && multiCount > 1 && (
            <p class="hint" style="padding:0.75rem;">{multiCount} widgets selected.<br/>Use align/distribute tools in the toolbar.</p>
          )}

          {!w && multiCount === 0 && (
            <p class="hint" style="padding:0.75rem;">Select a widget to edit its properties.</p>
          )}
        </div>
      )}
    </aside>
  );
}
